mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-09-02 07:09:29 +00:00
Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
71e5d2bf43 | |||
7188bc9d8d | |||
49180e826e | |||
c6fbbb499f | |||
7b565f0f0e | |||
a7cc637aa2 | |||
824bb93b0a | |||
6266e16644 | |||
edd4fcfcc0 | |||
30dc229477 | |||
d288885a89 | |||
bd19d9d5b4 | |||
a284af2000 | |||
88e8d028b4 | |||
8cab2b9d87 | |||
14f8858d3d | |||
62f7642de9 | |||
98048a178e | |||
26eb9bf300 | |||
d4ff4a3e0d | |||
488e143e1f | |||
4d5c5ef404 | |||
628742c2f3 | |||
2492a42151 | |||
4a0b6e99d4 | |||
65946fe254 | |||
3286b5c05e | |||
ac8e02604d | |||
ddf71ffdac | |||
5dd57204dd | |||
9a622a5e44 | |||
42e79ce921 | |||
cdde6ad36e | |||
c03e615483 |
@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.7)
|
cmake_minimum_required(VERSION 3.7)
|
||||||
project(libdatachannel
|
project(libdatachannel
|
||||||
DESCRIPTION "WebRTC Data Channels Library"
|
DESCRIPTION "WebRTC Data Channels Library"
|
||||||
VERSION 0.9.0
|
VERSION 0.9.1
|
||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
|
2
deps/libjuice
vendored
2
deps/libjuice
vendored
Submodule deps/libjuice updated: ddc3064890...da29d725ab
2
deps/usrsctp
vendored
2
deps/usrsctp
vendored
Submodule deps/usrsctp updated: ffed0925f2...0db9691000
@ -3,7 +3,12 @@ if(POLICY CMP0079)
|
|||||||
cmake_policy(SET CMP0079 NEW)
|
cmake_policy(SET CMP0079 NEW)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(datachannel-client main.cpp)
|
if(WIN32)
|
||||||
|
add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h getopt.cpp getopt.h)
|
||||||
|
target_compile_definitions(datachannel-client PUBLIC STATIC_GETOPT)
|
||||||
|
else()
|
||||||
|
add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h)
|
||||||
|
endif()
|
||||||
set_target_properties(datachannel-client PROPERTIES
|
set_target_properties(datachannel-client PROPERTIES
|
||||||
CXX_STANDARD 17
|
CXX_STANDARD 17
|
||||||
OUTPUT_NAME client)
|
OUTPUT_NAME client)
|
||||||
|
4
examples/client/README-getopt-for-windows.md
Normal file
4
examples/client/README-getopt-for-windows.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# getopt-for-windows
|
||||||
|
getopt.h and getopt.c is very often used in linux, to make it easy for windows user, two files were extracted from glibc. In order to make it works properly in windows, some modification was done and you may compare the change using original source files. Enjoy it!
|
||||||
|
|
||||||
|
Source: https://github.com/Chunde/getopt-for-windows. IMPORTANT: getopt.[ch] are likely not safe for Linux due to conflict with existing getopt.[ch]. They are thus NOT in CMakeFiles.txt and instead both files are #include only on Windows.
|
973
examples/client/getopt.cpp
Normal file
973
examples/client/getopt.cpp
Normal file
@ -0,0 +1,973 @@
|
|||||||
|
/* Getopt for Microsoft C
|
||||||
|
This code is a modification of the Free Software Foundation, Inc.
|
||||||
|
Getopt library for parsing command line argument the purpose was
|
||||||
|
to provide a Microsoft Visual C friendly derivative. This code
|
||||||
|
provides functionality for both Unicode and Multibyte builds.
|
||||||
|
|
||||||
|
Date: 02/03/2011 - Ludvik Jerabek - Initial Release
|
||||||
|
Version: 1.0
|
||||||
|
Comment: Supports getopt, getopt_long, and getopt_long_only
|
||||||
|
and POSIXLY_CORRECT environment flag
|
||||||
|
License: LGPL
|
||||||
|
|
||||||
|
Revisions:
|
||||||
|
|
||||||
|
02/03/2011 - Ludvik Jerabek - Initial Release
|
||||||
|
02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4
|
||||||
|
07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs
|
||||||
|
08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception
|
||||||
|
08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB
|
||||||
|
02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file
|
||||||
|
08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi
|
||||||
|
10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features
|
||||||
|
06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable
|
||||||
|
|
||||||
|
**DISCLAIMER**
|
||||||
|
THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE
|
||||||
|
EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT
|
||||||
|
APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY
|
||||||
|
DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY
|
||||||
|
USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST
|
||||||
|
PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON
|
||||||
|
YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
|
||||||
|
EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
*/
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include "getopt.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define _GETOPT_THROW throw()
|
||||||
|
#else
|
||||||
|
#define _GETOPT_THROW
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int optind = 1;
|
||||||
|
int opterr = 1;
|
||||||
|
int optopt = '?';
|
||||||
|
enum ENUM_ORDERING { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER };
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Ansi structures and functions follow
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
static struct _getopt_data_a
|
||||||
|
{
|
||||||
|
int optind;
|
||||||
|
int opterr;
|
||||||
|
int optopt;
|
||||||
|
char *optarg;
|
||||||
|
int __initialized;
|
||||||
|
char *__nextchar;
|
||||||
|
enum ENUM_ORDERING __ordering;
|
||||||
|
int __posixly_correct;
|
||||||
|
int __first_nonopt;
|
||||||
|
int __last_nonopt;
|
||||||
|
} getopt_data_a;
|
||||||
|
char *optarg_a;
|
||||||
|
|
||||||
|
static void exchange_a(char **argv, struct _getopt_data_a *d)
|
||||||
|
{
|
||||||
|
int bottom = d->__first_nonopt;
|
||||||
|
int middle = d->__last_nonopt;
|
||||||
|
int top = d->optind;
|
||||||
|
char *tem;
|
||||||
|
while (top > middle && middle > bottom)
|
||||||
|
{
|
||||||
|
if (top - middle > middle - bottom)
|
||||||
|
{
|
||||||
|
int len = middle - bottom;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
tem = argv[bottom + i];
|
||||||
|
argv[bottom + i] = argv[top - (middle - bottom) + i];
|
||||||
|
argv[top - (middle - bottom) + i] = tem;
|
||||||
|
}
|
||||||
|
top -= len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int len = top - middle;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
tem = argv[bottom + i];
|
||||||
|
argv[bottom + i] = argv[middle + i];
|
||||||
|
argv[middle + i] = tem;
|
||||||
|
}
|
||||||
|
bottom += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->__first_nonopt += (d->optind - d->__last_nonopt);
|
||||||
|
d->__last_nonopt = d->optind;
|
||||||
|
}
|
||||||
|
static const char *_getopt_initialize_a (const char *optstring, struct _getopt_data_a *d, int posixly_correct)
|
||||||
|
{
|
||||||
|
d->__first_nonopt = d->__last_nonopt = d->optind;
|
||||||
|
d->__nextchar = NULL;
|
||||||
|
d->__posixly_correct = posixly_correct | !!getenv("POSIXLY_CORRECT");
|
||||||
|
if (optstring[0] == '-')
|
||||||
|
{
|
||||||
|
d->__ordering = RETURN_IN_ORDER;
|
||||||
|
++optstring;
|
||||||
|
}
|
||||||
|
else if (optstring[0] == '+')
|
||||||
|
{
|
||||||
|
d->__ordering = REQUIRE_ORDER;
|
||||||
|
++optstring;
|
||||||
|
}
|
||||||
|
else if (d->__posixly_correct)
|
||||||
|
d->__ordering = REQUIRE_ORDER;
|
||||||
|
else
|
||||||
|
d->__ordering = PERMUTE;
|
||||||
|
return optstring;
|
||||||
|
}
|
||||||
|
int _getopt_internal_r_a (int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, struct _getopt_data_a *d, int posixly_correct)
|
||||||
|
{
|
||||||
|
int print_errors = d->opterr;
|
||||||
|
if (argc < 1)
|
||||||
|
return -1;
|
||||||
|
d->optarg = NULL;
|
||||||
|
if (d->optind == 0 || !d->__initialized)
|
||||||
|
{
|
||||||
|
if (d->optind == 0)
|
||||||
|
d->optind = 1;
|
||||||
|
optstring = _getopt_initialize_a (optstring, d, posixly_correct);
|
||||||
|
d->__initialized = 1;
|
||||||
|
}
|
||||||
|
else if (optstring[0] == '-' || optstring[0] == '+')
|
||||||
|
optstring++;
|
||||||
|
if (optstring[0] == ':')
|
||||||
|
print_errors = 0;
|
||||||
|
if (d->__nextchar == NULL || *d->__nextchar == '\0')
|
||||||
|
{
|
||||||
|
if (d->__last_nonopt > d->optind)
|
||||||
|
d->__last_nonopt = d->optind;
|
||||||
|
if (d->__first_nonopt > d->optind)
|
||||||
|
d->__first_nonopt = d->optind;
|
||||||
|
if (d->__ordering == PERMUTE)
|
||||||
|
{
|
||||||
|
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
|
||||||
|
exchange_a ((char **) argv, d);
|
||||||
|
else if (d->__last_nonopt != d->optind)
|
||||||
|
d->__first_nonopt = d->optind;
|
||||||
|
while (d->optind < argc && (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0'))
|
||||||
|
d->optind++;
|
||||||
|
d->__last_nonopt = d->optind;
|
||||||
|
}
|
||||||
|
if (d->optind != argc && !strcmp(argv[d->optind], "--"))
|
||||||
|
{
|
||||||
|
d->optind++;
|
||||||
|
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
|
||||||
|
exchange_a((char **) argv, d);
|
||||||
|
else if (d->__first_nonopt == d->__last_nonopt)
|
||||||
|
d->__first_nonopt = d->optind;
|
||||||
|
d->__last_nonopt = argc;
|
||||||
|
d->optind = argc;
|
||||||
|
}
|
||||||
|
if (d->optind == argc)
|
||||||
|
{
|
||||||
|
if (d->__first_nonopt != d->__last_nonopt)
|
||||||
|
d->optind = d->__first_nonopt;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((argv[d->optind][0] != '-' || argv[d->optind][1] == '\0'))
|
||||||
|
{
|
||||||
|
if (d->__ordering == REQUIRE_ORDER)
|
||||||
|
return -1;
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == '-'));
|
||||||
|
}
|
||||||
|
if (longopts != NULL && (argv[d->optind][1] == '-' || (long_only && (argv[d->optind][2] || !strchr(optstring, argv[d->optind][1])))))
|
||||||
|
{
|
||||||
|
char *nameend;
|
||||||
|
unsigned int namelen;
|
||||||
|
const struct option_a *p;
|
||||||
|
const struct option_a *pfound = NULL;
|
||||||
|
struct option_list
|
||||||
|
{
|
||||||
|
const struct option_a *p;
|
||||||
|
struct option_list *next;
|
||||||
|
} *ambig_list = NULL;
|
||||||
|
int exact = 0;
|
||||||
|
int indfound = -1;
|
||||||
|
int option_index;
|
||||||
|
for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++);
|
||||||
|
namelen = (unsigned int)(nameend - d->__nextchar);
|
||||||
|
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
||||||
|
if (!strncmp(p->name, d->__nextchar, namelen))
|
||||||
|
{
|
||||||
|
if (namelen == (unsigned int)strlen(p->name))
|
||||||
|
{
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
exact = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (pfound == NULL)
|
||||||
|
{
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
}
|
||||||
|
else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
|
||||||
|
{
|
||||||
|
struct option_list *newp = (struct option_list*)alloca(sizeof(*newp));
|
||||||
|
newp->p = p;
|
||||||
|
newp->next = ambig_list;
|
||||||
|
ambig_list = newp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ambig_list != NULL && !exact)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
struct option_list first;
|
||||||
|
first.p = pfound;
|
||||||
|
first.next = ambig_list;
|
||||||
|
ambig_list = &first;
|
||||||
|
fprintf (stderr, "%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
fprintf (stderr, " '--%s'", ambig_list->p->name);
|
||||||
|
ambig_list = ambig_list->next;
|
||||||
|
}
|
||||||
|
while (ambig_list != NULL);
|
||||||
|
fputc ('\n', stderr);
|
||||||
|
}
|
||||||
|
d->__nextchar += strlen(d->__nextchar);
|
||||||
|
d->optind++;
|
||||||
|
d->optopt = 0;
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
if (pfound != NULL)
|
||||||
|
{
|
||||||
|
option_index = indfound;
|
||||||
|
d->optind++;
|
||||||
|
if (*nameend)
|
||||||
|
{
|
||||||
|
if (pfound->has_arg)
|
||||||
|
d->optarg = nameend + 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
if (argv[d->optind - 1][1] == '-')
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: option '--%s' doesn't allow an argument\n",argv[0], pfound->name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: option '%c%s' doesn't allow an argument\n",argv[0], argv[d->optind - 1][0],pfound->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->__nextchar += strlen(d->__nextchar);
|
||||||
|
d->optopt = pfound->val;
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pfound->has_arg == 1)
|
||||||
|
{
|
||||||
|
if (d->optind < argc)
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"%s: option '--%s' requires an argument\n",argv[0], pfound->name);
|
||||||
|
}
|
||||||
|
d->__nextchar += strlen(d->__nextchar);
|
||||||
|
d->optopt = pfound->val;
|
||||||
|
return optstring[0] == ':' ? ':' : '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->__nextchar += strlen(d->__nextchar);
|
||||||
|
if (longind != NULL)
|
||||||
|
*longind = option_index;
|
||||||
|
if (pfound->flag)
|
||||||
|
{
|
||||||
|
*(pfound->flag) = pfound->val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return pfound->val;
|
||||||
|
}
|
||||||
|
if (!long_only || argv[d->optind][1] == '-' || strchr(optstring, *d->__nextchar) == NULL)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
if (argv[d->optind][1] == '-')
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: unrecognized option '--%s'\n",argv[0], d->__nextchar);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: unrecognized option '%c%s'\n",argv[0], argv[d->optind][0], d->__nextchar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->__nextchar = (char *)"";
|
||||||
|
d->optind++;
|
||||||
|
d->optopt = 0;
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char c = *d->__nextchar++;
|
||||||
|
char *temp = (char*)strchr(optstring, c);
|
||||||
|
if (*d->__nextchar == '\0')
|
||||||
|
++d->optind;
|
||||||
|
if (temp == NULL || c == ':' || c == ';')
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], c);
|
||||||
|
}
|
||||||
|
d->optopt = c;
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
if (temp[0] == 'W' && temp[1] == ';')
|
||||||
|
{
|
||||||
|
char *nameend;
|
||||||
|
const struct option_a *p;
|
||||||
|
const struct option_a *pfound = NULL;
|
||||||
|
int exact = 0;
|
||||||
|
int ambig = 0;
|
||||||
|
int indfound = 0;
|
||||||
|
int option_index;
|
||||||
|
if (longopts == NULL)
|
||||||
|
goto no_longs;
|
||||||
|
if (*d->__nextchar != '\0')
|
||||||
|
{
|
||||||
|
d->optarg = d->__nextchar;
|
||||||
|
d->optind++;
|
||||||
|
}
|
||||||
|
else if (d->optind == argc)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"%s: option requires an argument -- '%c'\n",argv[0], c);
|
||||||
|
}
|
||||||
|
d->optopt = c;
|
||||||
|
if (optstring[0] == ':')
|
||||||
|
c = ':';
|
||||||
|
else
|
||||||
|
c = '?';
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; nameend++);
|
||||||
|
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
||||||
|
if (!strncmp(p->name, d->__nextchar, nameend - d->__nextchar))
|
||||||
|
{
|
||||||
|
if ((unsigned int) (nameend - d->__nextchar) == strlen(p->name))
|
||||||
|
{
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
exact = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (pfound == NULL)
|
||||||
|
{
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
}
|
||||||
|
else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
|
||||||
|
ambig = 1;
|
||||||
|
}
|
||||||
|
if (ambig && !exact)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: option '-W %s' is ambiguous\n",argv[0], d->optarg);
|
||||||
|
}
|
||||||
|
d->__nextchar += strlen(d->__nextchar);
|
||||||
|
d->optind++;
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
if (pfound != NULL)
|
||||||
|
{
|
||||||
|
option_index = indfound;
|
||||||
|
if (*nameend)
|
||||||
|
{
|
||||||
|
if (pfound->has_arg)
|
||||||
|
d->optarg = nameend + 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: option '-W %s' doesn't allow an argument\n",argv[0], pfound->name);
|
||||||
|
}
|
||||||
|
d->__nextchar += strlen(d->__nextchar);
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pfound->has_arg == 1)
|
||||||
|
{
|
||||||
|
if (d->optind < argc)
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: option '-W %s' requires an argument\n",argv[0], pfound->name);
|
||||||
|
}
|
||||||
|
d->__nextchar += strlen(d->__nextchar);
|
||||||
|
return optstring[0] == ':' ? ':' : '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d->optarg = NULL;
|
||||||
|
d->__nextchar += strlen(d->__nextchar);
|
||||||
|
if (longind != NULL)
|
||||||
|
*longind = option_index;
|
||||||
|
if (pfound->flag)
|
||||||
|
{
|
||||||
|
*(pfound->flag) = pfound->val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return pfound->val;
|
||||||
|
}
|
||||||
|
no_longs:
|
||||||
|
d->__nextchar = NULL;
|
||||||
|
return 'W';
|
||||||
|
}
|
||||||
|
if (temp[1] == ':')
|
||||||
|
{
|
||||||
|
if (temp[2] == ':')
|
||||||
|
{
|
||||||
|
if (*d->__nextchar != '\0')
|
||||||
|
{
|
||||||
|
d->optarg = d->__nextchar;
|
||||||
|
d->optind++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d->optarg = NULL;
|
||||||
|
d->__nextchar = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (*d->__nextchar != '\0')
|
||||||
|
{
|
||||||
|
d->optarg = d->__nextchar;
|
||||||
|
d->optind++;
|
||||||
|
}
|
||||||
|
else if (d->optind == argc)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"%s: option requires an argument -- '%c'\n",argv[0], c);
|
||||||
|
}
|
||||||
|
d->optopt = c;
|
||||||
|
if (optstring[0] == ':')
|
||||||
|
c = ':';
|
||||||
|
else
|
||||||
|
c = '?';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
d->__nextchar = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int _getopt_internal_a (int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, int posixly_correct)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
getopt_data_a.optind = optind;
|
||||||
|
getopt_data_a.opterr = opterr;
|
||||||
|
result = _getopt_internal_r_a (argc, argv, optstring, longopts,longind, long_only, &getopt_data_a,posixly_correct);
|
||||||
|
optind = getopt_data_a.optind;
|
||||||
|
optarg_a = getopt_data_a.optarg;
|
||||||
|
optopt = getopt_data_a.optopt;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int getopt_a (int argc, char *const *argv, const char *optstring) _GETOPT_THROW
|
||||||
|
{
|
||||||
|
return _getopt_internal_a (argc, argv, optstring, (const struct option_a *) 0, (int *) 0, 0, 0);
|
||||||
|
}
|
||||||
|
int getopt_long_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW
|
||||||
|
{
|
||||||
|
return _getopt_internal_a (argc, argv, options, long_options, opt_index, 0, 0);
|
||||||
|
}
|
||||||
|
int getopt_long_only_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW
|
||||||
|
{
|
||||||
|
return _getopt_internal_a (argc, argv, options, long_options, opt_index, 1, 0);
|
||||||
|
}
|
||||||
|
int _getopt_long_r_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d)
|
||||||
|
{
|
||||||
|
return _getopt_internal_r_a (argc, argv, options, long_options, opt_index,0, d, 0);
|
||||||
|
}
|
||||||
|
int _getopt_long_only_r_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d)
|
||||||
|
{
|
||||||
|
return _getopt_internal_r_a (argc, argv, options, long_options, opt_index, 1, d, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Unicode Structures and Functions
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
static struct _getopt_data_w
|
||||||
|
{
|
||||||
|
int optind;
|
||||||
|
int opterr;
|
||||||
|
int optopt;
|
||||||
|
wchar_t *optarg;
|
||||||
|
int __initialized;
|
||||||
|
wchar_t *__nextchar;
|
||||||
|
enum ENUM_ORDERING __ordering;
|
||||||
|
int __posixly_correct;
|
||||||
|
int __first_nonopt;
|
||||||
|
int __last_nonopt;
|
||||||
|
} getopt_data_w;
|
||||||
|
wchar_t *optarg_w;
|
||||||
|
|
||||||
|
static void exchange_w(wchar_t **argv, struct _getopt_data_w *d)
|
||||||
|
{
|
||||||
|
int bottom = d->__first_nonopt;
|
||||||
|
int middle = d->__last_nonopt;
|
||||||
|
int top = d->optind;
|
||||||
|
wchar_t *tem;
|
||||||
|
while (top > middle && middle > bottom)
|
||||||
|
{
|
||||||
|
if (top - middle > middle - bottom)
|
||||||
|
{
|
||||||
|
int len = middle - bottom;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
tem = argv[bottom + i];
|
||||||
|
argv[bottom + i] = argv[top - (middle - bottom) + i];
|
||||||
|
argv[top - (middle - bottom) + i] = tem;
|
||||||
|
}
|
||||||
|
top -= len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int len = top - middle;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
tem = argv[bottom + i];
|
||||||
|
argv[bottom + i] = argv[middle + i];
|
||||||
|
argv[middle + i] = tem;
|
||||||
|
}
|
||||||
|
bottom += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->__first_nonopt += (d->optind - d->__last_nonopt);
|
||||||
|
d->__last_nonopt = d->optind;
|
||||||
|
}
|
||||||
|
static const wchar_t *_getopt_initialize_w (const wchar_t *optstring, struct _getopt_data_w *d, int posixly_correct)
|
||||||
|
{
|
||||||
|
d->__first_nonopt = d->__last_nonopt = d->optind;
|
||||||
|
d->__nextchar = NULL;
|
||||||
|
d->__posixly_correct = posixly_correct | !!_wgetenv(L"POSIXLY_CORRECT");
|
||||||
|
if (optstring[0] == L'-')
|
||||||
|
{
|
||||||
|
d->__ordering = RETURN_IN_ORDER;
|
||||||
|
++optstring;
|
||||||
|
}
|
||||||
|
else if (optstring[0] == L'+')
|
||||||
|
{
|
||||||
|
d->__ordering = REQUIRE_ORDER;
|
||||||
|
++optstring;
|
||||||
|
}
|
||||||
|
else if (d->__posixly_correct)
|
||||||
|
d->__ordering = REQUIRE_ORDER;
|
||||||
|
else
|
||||||
|
d->__ordering = PERMUTE;
|
||||||
|
return optstring;
|
||||||
|
}
|
||||||
|
int _getopt_internal_r_w (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, struct _getopt_data_w *d, int posixly_correct)
|
||||||
|
{
|
||||||
|
int print_errors = d->opterr;
|
||||||
|
if (argc < 1)
|
||||||
|
return -1;
|
||||||
|
d->optarg = NULL;
|
||||||
|
if (d->optind == 0 || !d->__initialized)
|
||||||
|
{
|
||||||
|
if (d->optind == 0)
|
||||||
|
d->optind = 1;
|
||||||
|
optstring = _getopt_initialize_w (optstring, d, posixly_correct);
|
||||||
|
d->__initialized = 1;
|
||||||
|
}
|
||||||
|
else if (optstring[0] == L'-' || optstring[0] == L'+')
|
||||||
|
optstring++;
|
||||||
|
if (optstring[0] == L':')
|
||||||
|
print_errors = 0;
|
||||||
|
if (d->__nextchar == NULL || *d->__nextchar == L'\0')
|
||||||
|
{
|
||||||
|
if (d->__last_nonopt > d->optind)
|
||||||
|
d->__last_nonopt = d->optind;
|
||||||
|
if (d->__first_nonopt > d->optind)
|
||||||
|
d->__first_nonopt = d->optind;
|
||||||
|
if (d->__ordering == PERMUTE)
|
||||||
|
{
|
||||||
|
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
|
||||||
|
exchange_w((wchar_t **) argv, d);
|
||||||
|
else if (d->__last_nonopt != d->optind)
|
||||||
|
d->__first_nonopt = d->optind;
|
||||||
|
while (d->optind < argc && (argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0'))
|
||||||
|
d->optind++;
|
||||||
|
d->__last_nonopt = d->optind;
|
||||||
|
}
|
||||||
|
if (d->optind != argc && !wcscmp(argv[d->optind], L"--"))
|
||||||
|
{
|
||||||
|
d->optind++;
|
||||||
|
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
|
||||||
|
exchange_w((wchar_t **) argv, d);
|
||||||
|
else if (d->__first_nonopt == d->__last_nonopt)
|
||||||
|
d->__first_nonopt = d->optind;
|
||||||
|
d->__last_nonopt = argc;
|
||||||
|
d->optind = argc;
|
||||||
|
}
|
||||||
|
if (d->optind == argc)
|
||||||
|
{
|
||||||
|
if (d->__first_nonopt != d->__last_nonopt)
|
||||||
|
d->optind = d->__first_nonopt;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0'))
|
||||||
|
{
|
||||||
|
if (d->__ordering == REQUIRE_ORDER)
|
||||||
|
return -1;
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == L'-'));
|
||||||
|
}
|
||||||
|
if (longopts != NULL && (argv[d->optind][1] == L'-' || (long_only && (argv[d->optind][2] || !wcschr(optstring, argv[d->optind][1])))))
|
||||||
|
{
|
||||||
|
wchar_t *nameend;
|
||||||
|
unsigned int namelen;
|
||||||
|
const struct option_w *p;
|
||||||
|
const struct option_w *pfound = NULL;
|
||||||
|
struct option_list
|
||||||
|
{
|
||||||
|
const struct option_w *p;
|
||||||
|
struct option_list *next;
|
||||||
|
} *ambig_list = NULL;
|
||||||
|
int exact = 0;
|
||||||
|
int indfound = -1;
|
||||||
|
int option_index;
|
||||||
|
for (nameend = d->__nextchar; *nameend && *nameend != L'='; nameend++);
|
||||||
|
namelen = (unsigned int)(nameend - d->__nextchar);
|
||||||
|
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
||||||
|
if (!wcsncmp(p->name, d->__nextchar, namelen))
|
||||||
|
{
|
||||||
|
if (namelen == (unsigned int)wcslen(p->name))
|
||||||
|
{
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
exact = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (pfound == NULL)
|
||||||
|
{
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
}
|
||||||
|
else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
|
||||||
|
{
|
||||||
|
struct option_list *newp = (struct option_list*)alloca(sizeof(*newp));
|
||||||
|
newp->p = p;
|
||||||
|
newp->next = ambig_list;
|
||||||
|
ambig_list = newp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ambig_list != NULL && !exact)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
struct option_list first;
|
||||||
|
first.p = pfound;
|
||||||
|
first.next = ambig_list;
|
||||||
|
ambig_list = &first;
|
||||||
|
fwprintf(stderr, L"%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
fwprintf (stderr, L" '--%s'", ambig_list->p->name);
|
||||||
|
ambig_list = ambig_list->next;
|
||||||
|
}
|
||||||
|
while (ambig_list != NULL);
|
||||||
|
fputwc (L'\n', stderr);
|
||||||
|
}
|
||||||
|
d->__nextchar += wcslen(d->__nextchar);
|
||||||
|
d->optind++;
|
||||||
|
d->optopt = 0;
|
||||||
|
return L'?';
|
||||||
|
}
|
||||||
|
if (pfound != NULL)
|
||||||
|
{
|
||||||
|
option_index = indfound;
|
||||||
|
d->optind++;
|
||||||
|
if (*nameend)
|
||||||
|
{
|
||||||
|
if (pfound->has_arg)
|
||||||
|
d->optarg = nameend + 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
if (argv[d->optind - 1][1] == L'-')
|
||||||
|
{
|
||||||
|
fwprintf(stderr, L"%s: option '--%s' doesn't allow an argument\n",argv[0], pfound->name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fwprintf(stderr, L"%s: option '%c%s' doesn't allow an argument\n",argv[0], argv[d->optind - 1][0],pfound->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->__nextchar += wcslen(d->__nextchar);
|
||||||
|
d->optopt = pfound->val;
|
||||||
|
return L'?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pfound->has_arg == 1)
|
||||||
|
{
|
||||||
|
if (d->optind < argc)
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fwprintf(stderr,L"%s: option '--%s' requires an argument\n",argv[0], pfound->name);
|
||||||
|
}
|
||||||
|
d->__nextchar += wcslen(d->__nextchar);
|
||||||
|
d->optopt = pfound->val;
|
||||||
|
return optstring[0] == L':' ? L':' : L'?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->__nextchar += wcslen(d->__nextchar);
|
||||||
|
if (longind != NULL)
|
||||||
|
*longind = option_index;
|
||||||
|
if (pfound->flag)
|
||||||
|
{
|
||||||
|
*(pfound->flag) = pfound->val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return pfound->val;
|
||||||
|
}
|
||||||
|
if (!long_only || argv[d->optind][1] == L'-' || wcschr(optstring, *d->__nextchar) == NULL)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
if (argv[d->optind][1] == L'-')
|
||||||
|
{
|
||||||
|
fwprintf(stderr, L"%s: unrecognized option '--%s'\n",argv[0], d->__nextchar);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fwprintf(stderr, L"%s: unrecognized option '%c%s'\n",argv[0], argv[d->optind][0], d->__nextchar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->__nextchar = (wchar_t *)L"";
|
||||||
|
d->optind++;
|
||||||
|
d->optopt = 0;
|
||||||
|
return L'?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
wchar_t c = *d->__nextchar++;
|
||||||
|
wchar_t *temp = (wchar_t*)wcschr(optstring, c);
|
||||||
|
if (*d->__nextchar == L'\0')
|
||||||
|
++d->optind;
|
||||||
|
if (temp == NULL || c == L':' || c == L';')
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fwprintf(stderr, L"%s: invalid option -- '%c'\n", argv[0], c);
|
||||||
|
}
|
||||||
|
d->optopt = c;
|
||||||
|
return L'?';
|
||||||
|
}
|
||||||
|
if (temp[0] == L'W' && temp[1] == L';')
|
||||||
|
{
|
||||||
|
wchar_t *nameend;
|
||||||
|
const struct option_w *p;
|
||||||
|
const struct option_w *pfound = NULL;
|
||||||
|
int exact = 0;
|
||||||
|
int ambig = 0;
|
||||||
|
int indfound = 0;
|
||||||
|
int option_index;
|
||||||
|
if (longopts == NULL)
|
||||||
|
goto no_longs;
|
||||||
|
if (*d->__nextchar != L'\0')
|
||||||
|
{
|
||||||
|
d->optarg = d->__nextchar;
|
||||||
|
d->optind++;
|
||||||
|
}
|
||||||
|
else if (d->optind == argc)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fwprintf(stderr,L"%s: option requires an argument -- '%c'\n",argv[0], c);
|
||||||
|
}
|
||||||
|
d->optopt = c;
|
||||||
|
if (optstring[0] == L':')
|
||||||
|
c = L':';
|
||||||
|
else
|
||||||
|
c = L'?';
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != L'='; nameend++);
|
||||||
|
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
||||||
|
if (!wcsncmp(p->name, d->__nextchar, nameend - d->__nextchar))
|
||||||
|
{
|
||||||
|
if ((unsigned int) (nameend - d->__nextchar) == wcslen(p->name))
|
||||||
|
{
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
exact = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (pfound == NULL)
|
||||||
|
{
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
}
|
||||||
|
else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
|
||||||
|
ambig = 1;
|
||||||
|
}
|
||||||
|
if (ambig && !exact)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fwprintf(stderr, L"%s: option '-W %s' is ambiguous\n",argv[0], d->optarg);
|
||||||
|
}
|
||||||
|
d->__nextchar += wcslen(d->__nextchar);
|
||||||
|
d->optind++;
|
||||||
|
return L'?';
|
||||||
|
}
|
||||||
|
if (pfound != NULL)
|
||||||
|
{
|
||||||
|
option_index = indfound;
|
||||||
|
if (*nameend)
|
||||||
|
{
|
||||||
|
if (pfound->has_arg)
|
||||||
|
d->optarg = nameend + 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fwprintf(stderr, L"%s: option '-W %s' doesn't allow an argument\n",argv[0], pfound->name);
|
||||||
|
}
|
||||||
|
d->__nextchar += wcslen(d->__nextchar);
|
||||||
|
return L'?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pfound->has_arg == 1)
|
||||||
|
{
|
||||||
|
if (d->optind < argc)
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fwprintf(stderr, L"%s: option '-W %s' requires an argument\n",argv[0], pfound->name);
|
||||||
|
}
|
||||||
|
d->__nextchar += wcslen(d->__nextchar);
|
||||||
|
return optstring[0] == L':' ? L':' : L'?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d->optarg = NULL;
|
||||||
|
d->__nextchar += wcslen(d->__nextchar);
|
||||||
|
if (longind != NULL)
|
||||||
|
*longind = option_index;
|
||||||
|
if (pfound->flag)
|
||||||
|
{
|
||||||
|
*(pfound->flag) = pfound->val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return pfound->val;
|
||||||
|
}
|
||||||
|
no_longs:
|
||||||
|
d->__nextchar = NULL;
|
||||||
|
return L'W';
|
||||||
|
}
|
||||||
|
if (temp[1] == L':')
|
||||||
|
{
|
||||||
|
if (temp[2] == L':')
|
||||||
|
{
|
||||||
|
if (*d->__nextchar != L'\0')
|
||||||
|
{
|
||||||
|
d->optarg = d->__nextchar;
|
||||||
|
d->optind++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d->optarg = NULL;
|
||||||
|
d->__nextchar = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (*d->__nextchar != L'\0')
|
||||||
|
{
|
||||||
|
d->optarg = d->__nextchar;
|
||||||
|
d->optind++;
|
||||||
|
}
|
||||||
|
else if (d->optind == argc)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
{
|
||||||
|
fwprintf(stderr,L"%s: option requires an argument -- '%c'\n",argv[0], c);
|
||||||
|
}
|
||||||
|
d->optopt = c;
|
||||||
|
if (optstring[0] == L':')
|
||||||
|
c = L':';
|
||||||
|
else
|
||||||
|
c = L'?';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
d->__nextchar = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int _getopt_internal_w (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, int posixly_correct)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
getopt_data_w.optind = optind;
|
||||||
|
getopt_data_w.opterr = opterr;
|
||||||
|
result = _getopt_internal_r_w (argc, argv, optstring, longopts,longind, long_only, &getopt_data_w,posixly_correct);
|
||||||
|
optind = getopt_data_w.optind;
|
||||||
|
optarg_w = getopt_data_w.optarg;
|
||||||
|
optopt = getopt_data_w.optopt;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int getopt_w (int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW
|
||||||
|
{
|
||||||
|
return _getopt_internal_w (argc, argv, optstring, (const struct option_w *) 0, (int *) 0, 0, 0);
|
||||||
|
}
|
||||||
|
int getopt_long_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW
|
||||||
|
{
|
||||||
|
return _getopt_internal_w (argc, argv, options, long_options, opt_index, 0, 0);
|
||||||
|
}
|
||||||
|
int getopt_long_only_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW
|
||||||
|
{
|
||||||
|
return _getopt_internal_w (argc, argv, options, long_options, opt_index, 1, 0);
|
||||||
|
}
|
||||||
|
int _getopt_long_r_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d)
|
||||||
|
{
|
||||||
|
return _getopt_internal_r_w (argc, argv, options, long_options, opt_index,0, d, 0);
|
||||||
|
}
|
||||||
|
int _getopt_long_only_r_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d)
|
||||||
|
{
|
||||||
|
return _getopt_internal_r_w (argc, argv, options, long_options, opt_index, 1, d, 0);
|
||||||
|
}
|
136
examples/client/getopt.h
Normal file
136
examples/client/getopt.h
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/* Getopt for Microsoft C
|
||||||
|
This code is a modification of the Free Software Foundation, Inc.
|
||||||
|
Getopt library for parsing command line argument the purpose was
|
||||||
|
to provide a Microsoft Visual C friendly derivative. This code
|
||||||
|
provides functionality for both Unicode and Multibyte builds.
|
||||||
|
|
||||||
|
Date: 02/03/2011 - Ludvik Jerabek - Initial Release
|
||||||
|
Version: 1.0
|
||||||
|
Comment: Supports getopt, getopt_long, and getopt_long_only
|
||||||
|
and POSIXLY_CORRECT environment flag
|
||||||
|
License: LGPL
|
||||||
|
|
||||||
|
Revisions:
|
||||||
|
|
||||||
|
02/03/2011 - Ludvik Jerabek - Initial Release
|
||||||
|
02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4
|
||||||
|
07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs
|
||||||
|
08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception
|
||||||
|
08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB
|
||||||
|
02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file
|
||||||
|
08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi
|
||||||
|
10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features
|
||||||
|
06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable
|
||||||
|
|
||||||
|
**DISCLAIMER**
|
||||||
|
THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE
|
||||||
|
EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT
|
||||||
|
APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY
|
||||||
|
DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY
|
||||||
|
USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST
|
||||||
|
PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON
|
||||||
|
YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
|
||||||
|
EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
*/
|
||||||
|
#ifndef __GETOPT_H_
|
||||||
|
#define __GETOPT_H_
|
||||||
|
|
||||||
|
#ifdef _GETOPT_API
|
||||||
|
#undef _GETOPT_API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT)
|
||||||
|
#error "The preprocessor definitions of EXPORTS_GETOPT and STATIC_GETOPT can only be used individually"
|
||||||
|
#elif defined(STATIC_GETOPT)
|
||||||
|
#pragma message("Warning static builds of getopt violate the Lesser GNU Public License")
|
||||||
|
#define _GETOPT_API
|
||||||
|
#elif defined(EXPORTS_GETOPT)
|
||||||
|
#pragma message("Exporting getopt library")
|
||||||
|
#define _GETOPT_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#pragma message("Importing getopt library")
|
||||||
|
#define _GETOPT_API __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Change behavior for C\C++
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define _BEGIN_EXTERN_C extern "C" {
|
||||||
|
#define _END_EXTERN_C }
|
||||||
|
#define _GETOPT_THROW throw()
|
||||||
|
#else
|
||||||
|
#define _BEGIN_EXTERN_C
|
||||||
|
#define _END_EXTERN_C
|
||||||
|
#define _GETOPT_THROW
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Standard GNU options
|
||||||
|
#define null_argument 0 /*Argument Null*/
|
||||||
|
#define no_argument 0 /*Argument Switch Only*/
|
||||||
|
#define required_argument 1 /*Argument Required*/
|
||||||
|
#define optional_argument 2 /*Argument Optional*/
|
||||||
|
|
||||||
|
// Shorter Options
|
||||||
|
#define ARG_NULL 0 /*Argument Null*/
|
||||||
|
#define ARG_NONE 0 /*Argument Switch Only*/
|
||||||
|
#define ARG_REQ 1 /*Argument Required*/
|
||||||
|
#define ARG_OPT 2 /*Argument Optional*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
_BEGIN_EXTERN_C
|
||||||
|
|
||||||
|
extern _GETOPT_API int optind;
|
||||||
|
extern _GETOPT_API int opterr;
|
||||||
|
extern _GETOPT_API int optopt;
|
||||||
|
|
||||||
|
// Ansi
|
||||||
|
struct option_a
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int has_arg;
|
||||||
|
int *flag;
|
||||||
|
int val;
|
||||||
|
};
|
||||||
|
extern _GETOPT_API char *optarg_a;
|
||||||
|
extern _GETOPT_API int getopt_a(int argc, char *const *argv, const char *optstring) _GETOPT_THROW;
|
||||||
|
extern _GETOPT_API int getopt_long_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW;
|
||||||
|
extern _GETOPT_API int getopt_long_only_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW;
|
||||||
|
|
||||||
|
// Unicode
|
||||||
|
struct option_w
|
||||||
|
{
|
||||||
|
const wchar_t* name;
|
||||||
|
int has_arg;
|
||||||
|
int *flag;
|
||||||
|
int val;
|
||||||
|
};
|
||||||
|
extern _GETOPT_API wchar_t *optarg_w;
|
||||||
|
extern _GETOPT_API int getopt_w(int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW;
|
||||||
|
extern _GETOPT_API int getopt_long_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW;
|
||||||
|
extern _GETOPT_API int getopt_long_only_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW;
|
||||||
|
|
||||||
|
_END_EXTERN_C
|
||||||
|
|
||||||
|
#undef _BEGIN_EXTERN_C
|
||||||
|
#undef _END_EXTERN_C
|
||||||
|
#undef _GETOPT_THROW
|
||||||
|
#undef _GETOPT_API
|
||||||
|
|
||||||
|
#ifdef _UNICODE
|
||||||
|
#define getopt getopt_w
|
||||||
|
#define getopt_long getopt_long_w
|
||||||
|
#define getopt_long_only getopt_long_only_w
|
||||||
|
#define option option_w
|
||||||
|
#define optarg optarg_w
|
||||||
|
#else
|
||||||
|
#define getopt getopt_a
|
||||||
|
#define getopt_long getopt_long_a
|
||||||
|
#define getopt_long_only getopt_long_only_a
|
||||||
|
#define option option_a
|
||||||
|
#define optarg optarg_a
|
||||||
|
#endif
|
||||||
|
#endif // __GETOPT_H_
|
@ -5,6 +5,7 @@
|
|||||||
* Copyright (c) 2020 Will Munn
|
* Copyright (c) 2020 Will Munn
|
||||||
* Copyright (c) 2020 Nico Chatzi
|
* Copyright (c) 2020 Nico Chatzi
|
||||||
* Copyright (c) 2020 Lara Mackey
|
* Copyright (c) 2020 Lara Mackey
|
||||||
|
* Copyright (c) 2020 Erik Cota-Robles
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@ -30,6 +31,7 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include "parse_cl.h"
|
||||||
|
|
||||||
using namespace rtc;
|
using namespace rtc;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -43,20 +45,41 @@ unordered_map<string, shared_ptr<PeerConnection>> peerConnectionMap;
|
|||||||
unordered_map<string, shared_ptr<DataChannel>> dataChannelMap;
|
unordered_map<string, shared_ptr<DataChannel>> dataChannelMap;
|
||||||
|
|
||||||
string localId;
|
string localId;
|
||||||
|
bool echoDataChannelMessages = false;
|
||||||
|
|
||||||
shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
|
shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
|
||||||
weak_ptr<WebSocket> wws, string id);
|
weak_ptr<WebSocket> wws, string id);
|
||||||
|
void confirmOnStdout(bool echoed, string id, string type, size_t length);
|
||||||
string randomId(size_t length);
|
string randomId(size_t length);
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
Cmdline *params = nullptr;
|
||||||
|
try {
|
||||||
|
params = new Cmdline(argc, argv);
|
||||||
|
} catch (const std::range_error&e) {
|
||||||
|
std::cout<< e.what() << '\n';
|
||||||
|
delete params;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
rtc::InitLogger(LogLevel::Debug);
|
rtc::InitLogger(LogLevel::Debug);
|
||||||
|
|
||||||
Configuration config;
|
Configuration config;
|
||||||
config.iceServers.emplace_back("stun:stun.l.google.com:19302"); // change to your STUN server
|
string stunServer = "";
|
||||||
|
if (params->stunServer().substr(0,5).compare("stun:") != 0) {
|
||||||
|
stunServer = "stun:";
|
||||||
|
}
|
||||||
|
stunServer += params->stunServer() + ":" + to_string(params->stunPort());
|
||||||
|
cout << "Stun server is " << stunServer << endl;
|
||||||
|
config.iceServers.emplace_back(stunServer);
|
||||||
|
|
||||||
localId = randomId(4);
|
localId = randomId(4);
|
||||||
cout << "The local ID is: " << localId << endl;
|
cout << "The local ID is: " << localId << endl;
|
||||||
|
|
||||||
|
echoDataChannelMessages = params->echoDataChannelMessages();
|
||||||
|
cout << "Received data channel messages will be "
|
||||||
|
<< (echoDataChannelMessages ? "echoed back to sender" : "printed to stdout") << endl;
|
||||||
|
|
||||||
auto ws = make_shared<WebSocket>();
|
auto ws = make_shared<WebSocket>();
|
||||||
|
|
||||||
ws->onOpen([]() { cout << "WebSocket connected, signaling ready" << endl; });
|
ws->onOpen([]() { cout << "WebSocket connected, signaling ready" << endl; });
|
||||||
@ -101,7 +124,13 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const string url = "ws://localhost:8000/" + localId;
|
string wsPrefix = "";
|
||||||
|
if (params->webSocketServer().substr(0,5).compare("ws://") != 0) {
|
||||||
|
wsPrefix = "ws://";
|
||||||
|
}
|
||||||
|
const string url = wsPrefix + params->webSocketServer() + ":" +
|
||||||
|
to_string(params->webSocketPort()) + "/" + localId;
|
||||||
|
cout << "Url is " << url << endl;
|
||||||
ws->open(url);
|
ws->open(url);
|
||||||
|
|
||||||
cout << "Waiting for signaling to be connected..." << endl;
|
cout << "Waiting for signaling to be connected..." << endl;
|
||||||
@ -137,11 +166,20 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
|
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
|
||||||
|
|
||||||
dc->onMessage([id](const variant<binary, string> &message) {
|
dc->onMessage([id, wdc = make_weak_ptr(dc)](const variant<binary, string> &message) {
|
||||||
if (!holds_alternative<string>(message))
|
static bool firstMessage = true;
|
||||||
return;
|
if (holds_alternative<string>(message) && (!echoDataChannelMessages || firstMessage)) {
|
||||||
|
cout << "Message from " << id << " received: " << get<string>(message) << endl;
|
||||||
cout << "Message from " << id << " received: " << get<string>(message) << endl;
|
firstMessage = false;
|
||||||
|
} else if (echoDataChannelMessages) {
|
||||||
|
bool echoed = false;
|
||||||
|
if (auto dc = wdc.lock()) {
|
||||||
|
dc->send(message);
|
||||||
|
echoed = true;
|
||||||
|
}
|
||||||
|
confirmOnStdout(echoed, id, (holds_alternative<string>(message) ? "text" : "binary"),
|
||||||
|
get<string>(message).length());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
dataChannelMap.emplace(id, dc);
|
dataChannelMap.emplace(id, dc);
|
||||||
@ -153,6 +191,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
dataChannelMap.clear();
|
dataChannelMap.clear();
|
||||||
peerConnectionMap.clear();
|
peerConnectionMap.clear();
|
||||||
|
delete params;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,11 +229,20 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
|
|||||||
|
|
||||||
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
|
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
|
||||||
|
|
||||||
dc->onMessage([id](const variant<binary, string> &message) {
|
dc->onMessage([id, wdc = make_weak_ptr(dc)](const variant<binary, string> &message) {
|
||||||
if (!holds_alternative<string>(message))
|
static bool firstMessage = true;
|
||||||
return;
|
if (holds_alternative<string>(message) && (!echoDataChannelMessages || firstMessage)) {
|
||||||
|
cout << "Message from " << id << " received: " << get<string>(message) << endl;
|
||||||
cout << "Message from " << id << " received: " << get<string>(message) << endl;
|
firstMessage = false;
|
||||||
|
} else if (echoDataChannelMessages) {
|
||||||
|
bool echoed = false;
|
||||||
|
if (auto dc = wdc.lock()) {
|
||||||
|
dc->send(message);
|
||||||
|
echoed = true;
|
||||||
|
}
|
||||||
|
confirmOnStdout(echoed, id, (holds_alternative<string>(message) ? "text" : "binary"),
|
||||||
|
get<string>(message).length());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
dc->send("Hello from " + localId);
|
dc->send("Hello from " + localId);
|
||||||
@ -206,6 +254,19 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
|
|||||||
return pc;
|
return pc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void confirmOnStdout(bool echoed, string id, string type, size_t length) {
|
||||||
|
static long count = 0;
|
||||||
|
static long freq = 100;
|
||||||
|
if (!(++count%freq)) {
|
||||||
|
cout << "Received " << count << " pings in total from host " << id << ", most recent of type "
|
||||||
|
<< type << " and " << (echoed ? "" : "un") << "successfully echoed most recent ping of size "
|
||||||
|
<< length << " back to " << id << endl;
|
||||||
|
if (count >= (freq * 10) && freq < 1000000) {
|
||||||
|
freq *= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to generate a random ID
|
// Helper function to generate a random ID
|
||||||
string randomId(size_t length) {
|
string randomId(size_t length) {
|
||||||
static const string characters(
|
static const string characters(
|
||||||
|
173
examples/client/parse_cl.cpp
Normal file
173
examples/client/parse_cl.cpp
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
**
|
||||||
|
** parse_cl.cpp
|
||||||
|
**
|
||||||
|
** Thu Aug 6 19:42:25 2020
|
||||||
|
** Linux 5.4.0-42-generic (#46-Ubuntu SMP Fri Jul 10 00:24:02 UTC 2020) x86_64
|
||||||
|
** cerik@Erik-VBox-Ubuntu (Erik Cota-Robles)
|
||||||
|
**
|
||||||
|
** Copyright (c) 2020 Erik Cota-Robles
|
||||||
|
**
|
||||||
|
** Definition of command line parser class
|
||||||
|
**
|
||||||
|
** Automatically created by genparse v0.9.3
|
||||||
|
**
|
||||||
|
** See http://genparse.sourceforge.net for details and updates
|
||||||
|
**
|
||||||
|
**
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(WIN32)
|
||||||
|
#include "getopt.h"
|
||||||
|
#else
|
||||||
|
#include <getopt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "parse_cl.h"
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
** Cmdline::Cmdline ()
|
||||||
|
**
|
||||||
|
** Constructor method.
|
||||||
|
**
|
||||||
|
**--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
Cmdline::Cmdline (int argc, char *argv[]) // ISO C++17 not allowed: throw (std::string )
|
||||||
|
{
|
||||||
|
extern char *optarg;
|
||||||
|
extern int optind;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
static struct option long_options[] =
|
||||||
|
{
|
||||||
|
{"stunServer", required_argument, NULL, 's'},
|
||||||
|
{"stunPort", required_argument, NULL, 't'},
|
||||||
|
{"webSocketServer", required_argument, NULL, 'w'},
|
||||||
|
{"webSocketPort", required_argument, NULL, 'x'},
|
||||||
|
{"help", no_argument, NULL, 'h'},
|
||||||
|
{"version", no_argument, NULL, 'v'},
|
||||||
|
{NULL, 0, NULL, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
_program_name += argv[0];
|
||||||
|
|
||||||
|
/* default values */
|
||||||
|
_s = "stun.l.google.com";
|
||||||
|
_t = 19302;
|
||||||
|
_w = "localhost";
|
||||||
|
_x = 8000;
|
||||||
|
_e = false;
|
||||||
|
_h = false;
|
||||||
|
_v = false;
|
||||||
|
|
||||||
|
optind = 0;
|
||||||
|
while ((c = getopt_long (argc, argv, "s:t:w:x:ehv", long_options, &optind)) != - 1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
_s = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
_t = atoi (optarg);
|
||||||
|
if (_t < 0)
|
||||||
|
{
|
||||||
|
std::string err;
|
||||||
|
err += "parameter range error: t must be >= 0";
|
||||||
|
throw (std::range_error(err));
|
||||||
|
}
|
||||||
|
if (_t > 65535)
|
||||||
|
{
|
||||||
|
std::string err;
|
||||||
|
err += "parameter range error: t must be <= 65535";
|
||||||
|
throw (std::range_error(err));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'w':
|
||||||
|
_w = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'x':
|
||||||
|
_x = atoi (optarg);
|
||||||
|
if (_x < 0)
|
||||||
|
{
|
||||||
|
std::string err;
|
||||||
|
err += "parameter range error: x must be >= 0";
|
||||||
|
throw (std::range_error(err));
|
||||||
|
}
|
||||||
|
if (_x > 65535)
|
||||||
|
{
|
||||||
|
std::string err;
|
||||||
|
err += "parameter range error: x must be <= 65535";
|
||||||
|
throw (std::range_error(err));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'e':
|
||||||
|
_e = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
_h = true;
|
||||||
|
this->usage (EXIT_SUCCESS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'v':
|
||||||
|
_v = true;
|
||||||
|
this->version (EXIT_SUCCESS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this->usage (EXIT_FAILURE);
|
||||||
|
|
||||||
|
}
|
||||||
|
} /* while */
|
||||||
|
|
||||||
|
_optind = optind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
** Cmdline::usage () and version()
|
||||||
|
**
|
||||||
|
** Print out usage (or version) information, then exit.
|
||||||
|
**
|
||||||
|
**--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void Cmdline::usage (int status)
|
||||||
|
{
|
||||||
|
if (status != EXIT_SUCCESS)
|
||||||
|
std::cerr << "Try `" << _program_name << " --help' for more information.\n";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "\
|
||||||
|
usage: " << _program_name << " [ -estwxhv ] \n\
|
||||||
|
libdatachannel client implementing WebRTC Data Channels with WebSocket signaling\n\
|
||||||
|
[ -e ] [ --echo ] (type=FLAG)\n\
|
||||||
|
Echo data channel messages back to sender rather than putting to stdout.\n\
|
||||||
|
[ -s ] [ --stunServer ] (type=STRING, default=stun.l.google.com)\n\
|
||||||
|
Stun server URL or IP address.\n\
|
||||||
|
[ -t ] [ --stunPort ] (type=INTEGER, range=0...65535, default=19302)\n\
|
||||||
|
Stun server port.\n\
|
||||||
|
[ -w ] [ --webSocketServer ] (type=STRING, default=localhost)\n\
|
||||||
|
Web socket server URL or IP address.\n\
|
||||||
|
[ -x ] [ --webSocketPort ] (type=INTEGER, range=0...65535, default=8000)\n\
|
||||||
|
Web socket server port.\n\
|
||||||
|
[ -h ] [ --help ] (type=FLAG)\n\
|
||||||
|
Display this help and exit.\n\
|
||||||
|
[ -v ] [ --version ] (type=FLAG)\n\
|
||||||
|
Output version information and exit.\n";
|
||||||
|
}
|
||||||
|
exit (status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cmdline::version (int status)
|
||||||
|
{
|
||||||
|
std::cout << _program_name << " v0.5\n";
|
||||||
|
exit (status);
|
||||||
|
}
|
72
examples/client/parse_cl.h
Normal file
72
examples/client/parse_cl.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
**
|
||||||
|
** parse_cl.h
|
||||||
|
**
|
||||||
|
** Thu Aug 6 19:42:25 2020
|
||||||
|
** Linux 5.4.0-42-generic (#46-Ubuntu SMP Fri Jul 10 00:24:02 UTC 2020) x86_64
|
||||||
|
** cerik@Erik-VBox-Ubuntu (Erik Cota-Robles)
|
||||||
|
**
|
||||||
|
** Copyright (c) 2020 Erik Cota-Robles
|
||||||
|
**
|
||||||
|
** Header file for command line parser class
|
||||||
|
**
|
||||||
|
** Automatically created by genparse v0.9.3
|
||||||
|
**
|
||||||
|
** See http://genparse.sourceforge.net for details and updates
|
||||||
|
**
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CMDLINE_H
|
||||||
|
#define CMDLINE_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
** class Cmdline
|
||||||
|
**
|
||||||
|
** command line parser class
|
||||||
|
**
|
||||||
|
**--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
class Cmdline
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/* parameters */
|
||||||
|
std::string _s;
|
||||||
|
int _t;
|
||||||
|
std::string _w;
|
||||||
|
int _x;
|
||||||
|
bool _e;
|
||||||
|
bool _h;
|
||||||
|
bool _v;
|
||||||
|
|
||||||
|
/* other stuff to keep track of */
|
||||||
|
std::string _program_name;
|
||||||
|
int _optind;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* constructor and destructor */
|
||||||
|
Cmdline (int, char **); // ISO C++17 not allowed: throw (std::string);
|
||||||
|
~Cmdline (){}
|
||||||
|
|
||||||
|
/* usage function */
|
||||||
|
void usage (int status);
|
||||||
|
|
||||||
|
/* version function */
|
||||||
|
void version (int status);
|
||||||
|
|
||||||
|
/* return next (non-option) parameter */
|
||||||
|
int next_param () { return _optind; }
|
||||||
|
|
||||||
|
std::string stunServer () const { return _s; }
|
||||||
|
int stunPort () const { return _t; }
|
||||||
|
std::string webSocketServer () const { return _w; }
|
||||||
|
int webSocketPort () const { return _x; }
|
||||||
|
bool echoDataChannelMessages () const { return _e; }
|
||||||
|
bool h () const { return _h; }
|
||||||
|
bool v () const { return _v; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -73,7 +73,7 @@ int main() {
|
|||||||
track->onMessage(
|
track->onMessage(
|
||||||
[session, sock, addr](rtc::binary message) {
|
[session, sock, addr](rtc::binary message) {
|
||||||
// This is an RTP packet
|
// This is an RTP packet
|
||||||
sendto(sock, reinterpret_cast<const char *>(message.data()), message.size(), 0,
|
sendto(sock, reinterpret_cast<const char *>(message.data()), int(message.size()), 0,
|
||||||
reinterpret_cast<const struct sockaddr *>(&addr), sizeof(addr));
|
reinterpret_cast<const struct sockaddr *>(&addr), sizeof(addr));
|
||||||
},
|
},
|
||||||
nullptr);
|
nullptr);
|
||||||
|
@ -63,16 +63,20 @@ async def handle_websocket(websocket, path):
|
|||||||
print('Client {} disconnected'.format(client_id))
|
print('Client {} disconnected'.format(client_id))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
|
# Usage: ./server.py [[host:]port] [SSL certificate file]
|
||||||
|
endpoint_or_port = sys.argv[1] if len(sys.argv) > 1 else "8000"
|
||||||
ssl_cert = sys.argv[2] if len(sys.argv) > 2 else None
|
ssl_cert = sys.argv[2] if len(sys.argv) > 2 else None
|
||||||
|
|
||||||
|
endpoint = endpoint_or_port if ':' in endpoint_or_port else "127.0.0.1:" + endpoint_or_port
|
||||||
|
|
||||||
if ssl_cert:
|
if ssl_cert:
|
||||||
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||||
ssl_context.load_cert_chain(ssl_cert)
|
ssl_context.load_cert_chain(ssl_cert)
|
||||||
else:
|
else:
|
||||||
ssl_context = None
|
ssl_context = None
|
||||||
|
|
||||||
print('Listening on port {}'.format(port))
|
print('Listening on {}'.format(endpoint))
|
||||||
start_server = websockets.serve(handle_websocket, '127.0.0.1', port, ssl=ssl_context)
|
host, port = endpoint.rsplit(':', 1)
|
||||||
|
start_server = websockets.serve(handle_websocket, host, int(port), ssl=ssl_context)
|
||||||
asyncio.get_event_loop().run_until_complete(start_server)
|
asyncio.get_event_loop().run_until_complete(start_server)
|
||||||
asyncio.get_event_loop().run_forever()
|
asyncio.get_event_loop().run_forever()
|
||||||
|
2
examples/signaling-server-rust/Cargo.lock
generated
2
examples/signaling-server-rust/Cargo.lock
generated
@ -348,7 +348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701"
|
checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libdatachannel_signaling_example"
|
name = "libdatachannel_signaling_server_example"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
@ -92,7 +92,9 @@ async fn handle(clients: ClientsMap, stream: TcpStream) {
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), std::io::Error> {
|
async fn main() -> Result<(), std::io::Error> {
|
||||||
let service = env::args().nth(1).unwrap_or("8000".to_string());
|
let service = env::args().nth(1).unwrap_or("8000".to_string());
|
||||||
let endpoint = format!("127.0.0.1:{}", service);
|
let endpoint = if service.contains(':') { service } else { format!("127.0.0.1:{}", service) };
|
||||||
|
|
||||||
|
println!("Listening on {}", endpoint);
|
||||||
|
|
||||||
let mut listener = TcpListener::bind(endpoint)
|
let mut listener = TcpListener::bind(endpoint)
|
||||||
.await.expect("Listener binding failed");
|
.await.expect("Listener binding failed");
|
||||||
|
@ -98,8 +98,10 @@ wsServer.on('request', (req) => {
|
|||||||
clients[id] = conn;
|
clients[id] = conn;
|
||||||
});
|
});
|
||||||
|
|
||||||
const hostname = '127.0.0.1';
|
const endpoint = process.env.PORT || '8000';
|
||||||
const port = 8000;
|
const splitted = endpoint.split(':');
|
||||||
|
const port = splitted.pop();
|
||||||
|
const hostname = splitted.join(':') || '127.0.0.1';
|
||||||
|
|
||||||
httpServer.listen(port, hostname, () => {
|
httpServer.listen(port, hostname, () => {
|
||||||
console.log(`Server listening on ${hostname}:${port}`);
|
console.log(`Server listening on ${hostname}:${port}`);
|
||||||
|
@ -47,6 +47,8 @@ public:
|
|||||||
Role role() const;
|
Role role() const;
|
||||||
string roleString() const;
|
string roleString() const;
|
||||||
string bundleMid() const;
|
string bundleMid() const;
|
||||||
|
string iceUfrag() const;
|
||||||
|
string icePwd() const;
|
||||||
std::optional<string> fingerprint() const;
|
std::optional<string> fingerprint() const;
|
||||||
bool ended() const;
|
bool ended() const;
|
||||||
|
|
||||||
|
@ -76,11 +76,13 @@ public:
|
|||||||
const Configuration *config() const;
|
const Configuration *config() const;
|
||||||
State state() const;
|
State state() const;
|
||||||
GatheringState gatheringState() const;
|
GatheringState gatheringState() const;
|
||||||
|
bool hasLocalDescription() const;
|
||||||
|
bool hasRemoteDescription() const;
|
||||||
|
bool hasMedia() const;
|
||||||
std::optional<Description> localDescription() const;
|
std::optional<Description> localDescription() const;
|
||||||
std::optional<Description> remoteDescription() const;
|
std::optional<Description> remoteDescription() const;
|
||||||
std::optional<string> localAddress() const;
|
std::optional<string> localAddress() const;
|
||||||
std::optional<string> remoteAddress() const;
|
std::optional<string> remoteAddress() const;
|
||||||
bool hasMedia() const;
|
|
||||||
|
|
||||||
void setLocalDescription();
|
void setLocalDescription();
|
||||||
void setRemoteDescription(Description description);
|
void setRemoteDescription(Description description);
|
||||||
|
@ -44,11 +44,15 @@ public:
|
|||||||
size_t amount() const; // amount
|
size_t amount() const; // amount
|
||||||
void push(T element);
|
void push(T element);
|
||||||
std::optional<T> pop();
|
std::optional<T> pop();
|
||||||
|
std::optional<T> tryPop();
|
||||||
std::optional<T> peek();
|
std::optional<T> peek();
|
||||||
std::optional<T> exchange(T element);
|
std::optional<T> exchange(T element);
|
||||||
bool wait(const std::optional<std::chrono::milliseconds> &duration = nullopt);
|
bool wait(const std::optional<std::chrono::milliseconds> &duration = nullopt);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void pushImpl(T element);
|
||||||
|
std::optional<T> popImpl();
|
||||||
|
|
||||||
const size_t mLimit;
|
const size_t mLimit;
|
||||||
size_t mAmount;
|
size_t mAmount;
|
||||||
std::queue<T> mQueue;
|
std::queue<T> mQueue;
|
||||||
@ -99,43 +103,32 @@ template <typename T> size_t Queue<T>::amount() const {
|
|||||||
template <typename T> void Queue<T>::push(T element) {
|
template <typename T> void Queue<T>::push(T element) {
|
||||||
std::unique_lock lock(mMutex);
|
std::unique_lock lock(mMutex);
|
||||||
mPushCondition.wait(lock, [this]() { return !mLimit || mQueue.size() < mLimit || mStopping; });
|
mPushCondition.wait(lock, [this]() { return !mLimit || mQueue.size() < mLimit || mStopping; });
|
||||||
if (!mStopping) {
|
pushImpl(std::move(element));
|
||||||
mAmount += mAmountFunction(element);
|
|
||||||
mQueue.emplace(std::move(element));
|
|
||||||
mPopCondition.notify_one();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> std::optional<T> Queue<T>::pop() {
|
template <typename T> std::optional<T> Queue<T>::pop() {
|
||||||
std::unique_lock lock(mMutex);
|
std::unique_lock lock(mMutex);
|
||||||
mPopCondition.wait(lock, [this]() { return !mQueue.empty() || mStopping; });
|
mPopCondition.wait(lock, [this]() { return !mQueue.empty() || mStopping; });
|
||||||
if (!mQueue.empty()) {
|
return popImpl();
|
||||||
mAmount -= mAmountFunction(mQueue.front());
|
}
|
||||||
std::optional<T> element{std::move(mQueue.front())};
|
|
||||||
mQueue.pop();
|
template <typename T> std::optional<T> Queue<T>::tryPop() {
|
||||||
return element;
|
std::unique_lock lock(mMutex);
|
||||||
} else {
|
return popImpl();
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> std::optional<T> Queue<T>::peek() {
|
template <typename T> std::optional<T> Queue<T>::peek() {
|
||||||
std::unique_lock lock(mMutex);
|
std::unique_lock lock(mMutex);
|
||||||
if (!mQueue.empty()) {
|
return !mQueue.empty() ? std::make_optional(mQueue.front()) : nullopt;
|
||||||
return std::optional<T>{mQueue.front()};
|
|
||||||
} else {
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> std::optional<T> Queue<T>::exchange(T element) {
|
template <typename T> std::optional<T> Queue<T>::exchange(T element) {
|
||||||
std::unique_lock lock(mMutex);
|
std::unique_lock lock(mMutex);
|
||||||
if (!mQueue.empty()) {
|
if (mQueue.empty())
|
||||||
std::swap(mQueue.front(), element);
|
|
||||||
return std::optional<T>{element};
|
|
||||||
} else {
|
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
|
||||||
|
std::swap(mQueue.front(), element);
|
||||||
|
return std::make_optional(std::move(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -145,7 +138,27 @@ bool Queue<T>::wait(const std::optional<std::chrono::milliseconds> &duration) {
|
|||||||
mPopCondition.wait_for(lock, *duration, [this]() { return !mQueue.empty() || mStopping; });
|
mPopCondition.wait_for(lock, *duration, [this]() { return !mQueue.empty() || mStopping; });
|
||||||
else
|
else
|
||||||
mPopCondition.wait(lock, [this]() { return !mQueue.empty() || mStopping; });
|
mPopCondition.wait(lock, [this]() { return !mQueue.empty() || mStopping; });
|
||||||
return !mStopping;
|
|
||||||
|
return !mQueue.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void Queue<T>::pushImpl(T element) {
|
||||||
|
if (mStopping)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mAmount += mAmountFunction(element);
|
||||||
|
mQueue.emplace(std::move(element));
|
||||||
|
mPopCondition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> std::optional<T> Queue<T>::popImpl() {
|
||||||
|
if (mQueue.empty())
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
mAmount -= mAmountFunction(mQueue.front());
|
||||||
|
std::optional<T> element{std::move(mQueue.front())};
|
||||||
|
mQueue.pop();
|
||||||
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
@ -49,7 +49,7 @@ namespace rtc {
|
|||||||
|
|
||||||
Candidate::Candidate(string candidate, string mid) : mIsResolved(false) {
|
Candidate::Candidate(string candidate, string mid) : mIsResolved(false) {
|
||||||
const std::array prefixes{"a=", "candidate:"};
|
const std::array prefixes{"a=", "candidate:"};
|
||||||
for (string prefix : prefixes)
|
for (const string &prefix : prefixes)
|
||||||
if (hasprefix(candidate, prefix))
|
if (hasprefix(candidate, prefix))
|
||||||
candidate.erase(0, prefix.size());
|
candidate.erase(0, prefix.size());
|
||||||
|
|
||||||
|
18
src/capi.cpp
18
src/capi.cpp
@ -325,15 +325,17 @@ int rtcDeleteDataChannel(int dc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int rtcAddTrack(int pc, const char *mediaDescriptionSdp) {
|
int rtcAddTrack(int pc, const char *mediaDescriptionSdp) {
|
||||||
if (!mediaDescriptionSdp)
|
return WRAP({
|
||||||
throw std::invalid_argument("Unexpected null pointer for track media description");
|
if (!mediaDescriptionSdp)
|
||||||
|
throw std::invalid_argument("Unexpected null pointer for track media description");
|
||||||
|
|
||||||
auto peerConnection = getPeerConnection(pc);
|
auto peerConnection = getPeerConnection(pc);
|
||||||
Description::Media media{string(mediaDescriptionSdp)};
|
Description::Media media{string(mediaDescriptionSdp)};
|
||||||
int tr = emplaceTrack(peerConnection->addTrack(std::move(media)));
|
int tr = emplaceTrack(peerConnection->addTrack(std::move(media)));
|
||||||
if (auto ptr = getUserPointer(pc))
|
if (auto ptr = getUserPointer(pc))
|
||||||
rtcSetUserPointer(tr, *ptr);
|
rtcSetUserPointer(tr, *ptr);
|
||||||
return tr;
|
return tr;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcDeleteTrack(int tr) {
|
int rtcDeleteTrack(int tr) {
|
||||||
|
@ -122,8 +122,8 @@ bool DataChannel::send(const byte *data, size_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<message_variant> DataChannel::receive() {
|
std::optional<message_variant> DataChannel::receive() {
|
||||||
while (!mRecvQueue.empty()) {
|
while (auto next = mRecvQueue.tryPop()) {
|
||||||
auto message = *mRecvQueue.pop();
|
message_ptr message = std::move(*next);
|
||||||
if (message->type == Message::Control) {
|
if (message->type == Message::Control) {
|
||||||
auto raw = reinterpret_cast<const uint8_t *>(message->data());
|
auto raw = reinterpret_cast<const uint8_t *>(message->data());
|
||||||
if (!message->empty() && raw[0] == MESSAGE_CLOSE)
|
if (!message->empty() && raw[0] == MESSAGE_CLOSE)
|
||||||
|
@ -79,13 +79,15 @@ Description::Description(const string &sdp, Type type, Role role)
|
|||||||
std::uniform_int_distribution<uint32_t> uniform;
|
std::uniform_int_distribution<uint32_t> uniform;
|
||||||
mSessionId = std::to_string(uniform(generator));
|
mSessionId = std::to_string(uniform(generator));
|
||||||
|
|
||||||
std::istringstream ss(sdp);
|
|
||||||
std::shared_ptr<Entry> current;
|
|
||||||
|
|
||||||
int index = -1;
|
int index = -1;
|
||||||
string line;
|
std::shared_ptr<Entry> current;
|
||||||
while (std::getline(ss, line) || !line.empty()) {
|
std::istringstream ss(sdp);
|
||||||
|
while (ss) {
|
||||||
|
string line;
|
||||||
|
std::getline(ss, line);
|
||||||
trim_end(line);
|
trim_end(line);
|
||||||
|
if (line.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
// Media description line (aka m-line)
|
// Media description line (aka m-line)
|
||||||
if (match_prefix(line, "m=")) {
|
if (match_prefix(line, "m=")) {
|
||||||
@ -130,7 +132,13 @@ Description::Description(const string &sdp, Type type, Role role)
|
|||||||
} else if (current) {
|
} else if (current) {
|
||||||
current->parseSdpLine(std::move(line));
|
current->parseSdpLine(std::move(line));
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
if (mIceUfrag.empty())
|
||||||
|
throw std::invalid_argument("Missing ice-ufrag parameter in SDP description");
|
||||||
|
|
||||||
|
if (mIcePwd.empty())
|
||||||
|
throw std::invalid_argument("Missing ice-pwd parameter in SDP description");
|
||||||
}
|
}
|
||||||
|
|
||||||
Description::Type Description::type() const { return mType; }
|
Description::Type Description::type() const { return mType; }
|
||||||
@ -146,6 +154,10 @@ string Description::bundleMid() const {
|
|||||||
return !mEntries.empty() ? mEntries[0]->mid() : "0";
|
return !mEntries.empty() ? mEntries[0]->mid() : "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string Description::iceUfrag() const { return mIceUfrag; }
|
||||||
|
|
||||||
|
string Description::icePwd() const { return mIcePwd; }
|
||||||
|
|
||||||
std::optional<string> Description::fingerprint() const { return mFingerprint; }
|
std::optional<string> Description::fingerprint() const { return mFingerprint; }
|
||||||
|
|
||||||
bool Description::ended() const { return mEnded; }
|
bool Description::ended() const { return mEnded; }
|
||||||
@ -488,9 +500,13 @@ void Description::Application::parseSdpLine(string_view line) {
|
|||||||
|
|
||||||
Description::Media::Media(const string &sdp) : Entry(sdp, "", Direction::Unknown) {
|
Description::Media::Media(const string &sdp) : Entry(sdp, "", Direction::Unknown) {
|
||||||
std::istringstream ss(sdp);
|
std::istringstream ss(sdp);
|
||||||
string line;
|
while (ss) {
|
||||||
while (std::getline(ss, line) || !line.empty()) {
|
string line;
|
||||||
|
std::getline(ss, line);
|
||||||
trim_end(line);
|
trim_end(line);
|
||||||
|
if (line.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
parseSdpLine(line);
|
parseSdpLine(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ ssize_t DtlsTransport::WriteCallback(gnutls_transport_ptr_t ptr, const void *dat
|
|||||||
ssize_t DtlsTransport::ReadCallback(gnutls_transport_ptr_t ptr, void *data, size_t maxlen) {
|
ssize_t DtlsTransport::ReadCallback(gnutls_transport_ptr_t ptr, void *data, size_t maxlen) {
|
||||||
DtlsTransport *t = static_cast<DtlsTransport *>(ptr);
|
DtlsTransport *t = static_cast<DtlsTransport *>(ptr);
|
||||||
if (auto next = t->mIncomingQueue.pop()) {
|
if (auto next = t->mIncomingQueue.pop()) {
|
||||||
auto message = *next;
|
message_ptr message = std::move(*next);
|
||||||
ssize_t len = std::min(maxlen, message->size());
|
ssize_t len = std::min(maxlen, message->size());
|
||||||
std::memcpy(data, message->data(), len);
|
std::memcpy(data, message->data(), len);
|
||||||
gnutls_transport_set_errno(t->mSession, 0);
|
gnutls_transport_set_errno(t->mSession, 0);
|
||||||
@ -271,9 +271,9 @@ ssize_t DtlsTransport::ReadCallback(gnutls_transport_ptr_t ptr, void *data, size
|
|||||||
|
|
||||||
int DtlsTransport::TimeoutCallback(gnutls_transport_ptr_t ptr, unsigned int ms) {
|
int DtlsTransport::TimeoutCallback(gnutls_transport_ptr_t ptr, unsigned int ms) {
|
||||||
DtlsTransport *t = static_cast<DtlsTransport *>(ptr);
|
DtlsTransport *t = static_cast<DtlsTransport *>(ptr);
|
||||||
t->mIncomingQueue.wait(ms != GNUTLS_INDEFINITE_TIMEOUT ? std::make_optional(milliseconds(ms))
|
bool notEmpty = t->mIncomingQueue.wait(
|
||||||
: nullopt);
|
ms != GNUTLS_INDEFINITE_TIMEOUT ? std::make_optional(milliseconds(ms)) : nullopt);
|
||||||
return !t->mIncomingQueue.empty() ? 1 : 0;
|
return notEmpty ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // USE_GNUTLS==0
|
#else // USE_GNUTLS==0
|
||||||
@ -437,8 +437,8 @@ void DtlsTransport::runRecvLoop() {
|
|||||||
byte buffer[bufferSize];
|
byte buffer[bufferSize];
|
||||||
while (true) {
|
while (true) {
|
||||||
// Process pending messages
|
// Process pending messages
|
||||||
while (!mIncomingQueue.empty()) {
|
while (auto next = mIncomingQueue.tryPop()) {
|
||||||
auto message = *mIncomingQueue.pop();
|
message_ptr message = std::move(*next);
|
||||||
BIO_write(mInBio, message->data(), int(message->size()));
|
BIO_write(mInBio, message->data(), int(message->size()));
|
||||||
|
|
||||||
if (state() == State::Connecting) {
|
if (state() == State::Connecting) {
|
||||||
|
@ -58,8 +58,31 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
|||||||
if (config.enableIceTcp) {
|
if (config.enableIceTcp) {
|
||||||
PLOG_WARNING << "ICE-TCP is not supported with libjuice";
|
PLOG_WARNING << "ICE-TCP is not supported with libjuice";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
juice_log_level_t level;
|
||||||
|
switch (plog::get()->getMaxSeverity()) {
|
||||||
|
case plog::none:
|
||||||
|
level = JUICE_LOG_LEVEL_NONE;
|
||||||
|
break;
|
||||||
|
case plog::fatal:
|
||||||
|
level = JUICE_LOG_LEVEL_VERBOSE;
|
||||||
|
break;
|
||||||
|
case plog::error:
|
||||||
|
level = JUICE_LOG_LEVEL_ERROR;
|
||||||
|
break;
|
||||||
|
case plog::warning:
|
||||||
|
level = JUICE_LOG_LEVEL_WARN;
|
||||||
|
break;
|
||||||
|
case plog::info:
|
||||||
|
case plog::debug: // juice debug is output as verbose
|
||||||
|
level = JUICE_LOG_LEVEL_INFO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
level = JUICE_LOG_LEVEL_VERBOSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
juice_set_log_handler(IceTransport::LogCallback);
|
juice_set_log_handler(IceTransport::LogCallback);
|
||||||
juice_set_log_level(JUICE_LOG_LEVEL_VERBOSE);
|
juice_set_log_level(level);
|
||||||
|
|
||||||
juice_config_t jconfig = {};
|
juice_config_t jconfig = {};
|
||||||
jconfig.cb_state_changed = IceTransport::StateChangeCallback;
|
jconfig.cb_state_changed = IceTransport::StateChangeCallback;
|
||||||
|
@ -82,11 +82,26 @@ std::optional<Description> PeerConnection::remoteDescription() const {
|
|||||||
return mRemoteDescription;
|
return mRemoteDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PeerConnection::hasLocalDescription() const {
|
||||||
|
std::lock_guard lock(mLocalDescriptionMutex);
|
||||||
|
return bool(mLocalDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerConnection::hasRemoteDescription() const {
|
||||||
|
std::lock_guard lock(mRemoteDescriptionMutex);
|
||||||
|
return bool(mRemoteDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerConnection::hasMedia() const {
|
||||||
|
auto local = localDescription();
|
||||||
|
return local && local->hasAudioOrVideo();
|
||||||
|
}
|
||||||
|
|
||||||
void PeerConnection::setLocalDescription() {
|
void PeerConnection::setLocalDescription() {
|
||||||
PLOG_VERBOSE << "Setting local description";
|
PLOG_VERBOSE << "Setting local description";
|
||||||
|
|
||||||
if (std::atomic_load(&mIceTransport)) {
|
if (std::atomic_load(&mIceTransport)) {
|
||||||
PLOG_DEBUG << "Local description is already set";
|
PLOG_DEBUG << "Local description is already set, ignoring";
|
||||||
}
|
}
|
||||||
|
|
||||||
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
||||||
@ -101,6 +116,9 @@ void PeerConnection::setLocalDescription() {
|
|||||||
void PeerConnection::setRemoteDescription(Description description) {
|
void PeerConnection::setRemoteDescription(Description description) {
|
||||||
PLOG_VERBOSE << "Setting remote description: " << string(description);
|
PLOG_VERBOSE << "Setting remote description: " << string(description);
|
||||||
|
|
||||||
|
if (hasRemoteDescription())
|
||||||
|
throw std::logic_error("Remote description is already set");
|
||||||
|
|
||||||
if (description.mediaCount() == 0)
|
if (description.mediaCount() == 0)
|
||||||
throw std::invalid_argument("Remote description has no media line");
|
throw std::invalid_argument("Remote description has no media line");
|
||||||
|
|
||||||
@ -120,9 +138,27 @@ void PeerConnection::setRemoteDescription(Description description) {
|
|||||||
if (!description.fingerprint())
|
if (!description.fingerprint())
|
||||||
throw std::invalid_argument("Remote description has no fingerprint");
|
throw std::invalid_argument("Remote description has no fingerprint");
|
||||||
|
|
||||||
description.hintType(localDescription() ? Description::Type::Answer : Description::Type::Offer);
|
description.hintType(hasLocalDescription() ? Description::Type::Answer
|
||||||
auto type = description.type();
|
: Description::Type::Offer);
|
||||||
auto remoteCandidates = description.extractCandidates(); // Candidates will be added at the end
|
|
||||||
|
if (description.type() == Description::Type::Offer) {
|
||||||
|
if (hasLocalDescription()) {
|
||||||
|
PLOG_ERROR << "Got a remote offer description while an answer was expected";
|
||||||
|
throw std::logic_error("Got an unexpected remote offer description");
|
||||||
|
}
|
||||||
|
} else { // Answer
|
||||||
|
if (auto local = localDescription()) {
|
||||||
|
if (description.iceUfrag() == local->iceUfrag() &&
|
||||||
|
description.icePwd() == local->icePwd())
|
||||||
|
throw std::logic_error("Got the local description as remote description");
|
||||||
|
} else {
|
||||||
|
PLOG_ERROR << "Got a remote answer description while an offer was expected";
|
||||||
|
throw std::logic_error("Got an unexpected remote answer description");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Candidates will be added at the end, extract them for now
|
||||||
|
auto remoteCandidates = description.extractCandidates();
|
||||||
|
|
||||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||||
if (!iceTransport)
|
if (!iceTransport)
|
||||||
@ -130,11 +166,12 @@ void PeerConnection::setRemoteDescription(Description description) {
|
|||||||
iceTransport->setRemoteDescription(description);
|
iceTransport->setRemoteDescription(description);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// Set as remote description
|
||||||
std::lock_guard lock(mRemoteDescriptionMutex);
|
std::lock_guard lock(mRemoteDescriptionMutex);
|
||||||
mRemoteDescription.emplace(std::move(description));
|
mRemoteDescription.emplace(std::move(description));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == Description::Type::Offer) {
|
if (description.type() == Description::Type::Offer) {
|
||||||
// This is an offer and we are the answerer.
|
// This is an offer and we are the answerer.
|
||||||
Description localDescription = iceTransport->getLocalDescription(Description::Type::Answer);
|
Description localDescription = iceTransport->getLocalDescription(Description::Type::Answer);
|
||||||
processLocalDescription(localDescription);
|
processLocalDescription(localDescription);
|
||||||
@ -161,7 +198,7 @@ void PeerConnection::setRemoteDescription(Description description) {
|
|||||||
|
|
||||||
for (const auto &candidate : remoteCandidates)
|
for (const auto &candidate : remoteCandidates)
|
||||||
addRemoteCandidate(candidate);
|
addRemoteCandidate(candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::addRemoteCandidate(Candidate candidate) {
|
void PeerConnection::addRemoteCandidate(Candidate candidate) {
|
||||||
PLOG_VERBOSE << "Adding remote candidate: " << string(candidate);
|
PLOG_VERBOSE << "Adding remote candidate: " << string(candidate);
|
||||||
@ -250,13 +287,8 @@ void PeerConnection::onGatheringStateChange(std::function<void(GatheringState st
|
|||||||
mGatheringStateChangeCallback = callback;
|
mGatheringStateChangeCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PeerConnection::hasMedia() const {
|
|
||||||
auto local = localDescription();
|
|
||||||
return local && local->hasAudioOrVideo();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description) {
|
std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description) {
|
||||||
if (localDescription())
|
if (hasLocalDescription())
|
||||||
throw std::logic_error("Tracks must be created before local description");
|
throw std::logic_error("Tracks must be created before local description");
|
||||||
|
|
||||||
if (auto it = mTracks.find(description.mid()); it != mTracks.end())
|
if (auto it = mTracks.find(description.mid()); it != mTracks.end())
|
||||||
@ -699,6 +731,9 @@ void PeerConnection::openTracks() {
|
|||||||
void PeerConnection::processLocalDescription(Description description) {
|
void PeerConnection::processLocalDescription(Description description) {
|
||||||
int activeMediaCount = 0;
|
int activeMediaCount = 0;
|
||||||
|
|
||||||
|
if (hasLocalDescription())
|
||||||
|
throw std::logic_error("Local description is already set");
|
||||||
|
|
||||||
if (auto remote = remoteDescription()) {
|
if (auto remote = remoteDescription()) {
|
||||||
// Reciprocate remote description
|
// Reciprocate remote description
|
||||||
for (int i = 0; i < remote->mediaCount(); ++i)
|
for (int i = 0; i < remote->mediaCount(); ++i)
|
||||||
@ -782,8 +817,11 @@ void PeerConnection::processLocalDescription(Description description) {
|
|||||||
// Set local fingerprint (wait for certificate if necessary)
|
// Set local fingerprint (wait for certificate if necessary)
|
||||||
description.setFingerprint(mCertificate.get()->fingerprint());
|
description.setFingerprint(mCertificate.get()->fingerprint());
|
||||||
|
|
||||||
std::lock_guard lock(mLocalDescriptionMutex);
|
{
|
||||||
mLocalDescription.emplace(std::move(description));
|
// Set as local description
|
||||||
|
std::lock_guard lock(mLocalDescriptionMutex);
|
||||||
|
mLocalDescription.emplace(std::move(description));
|
||||||
|
}
|
||||||
|
|
||||||
mProcessor->enqueue([this, description = *mLocalDescription]() {
|
mProcessor->enqueue([this, description = *mLocalDescription]() {
|
||||||
PLOG_VERBOSE << "Issuing local description: " << description;
|
PLOG_VERBOSE << "Issuing local description: " << description;
|
||||||
|
@ -322,7 +322,7 @@ void SctpTransport::incoming(message_ptr message) {
|
|||||||
bool SctpTransport::trySendQueue() {
|
bool SctpTransport::trySendQueue() {
|
||||||
// Requires mSendMutex to be locked
|
// Requires mSendMutex to be locked
|
||||||
while (auto next = mSendQueue.peek()) {
|
while (auto next = mSendQueue.peek()) {
|
||||||
auto message = *next;
|
message_ptr message = std::move(*next);
|
||||||
if (!trySendMessage(message))
|
if (!trySendMessage(message))
|
||||||
return false;
|
return false;
|
||||||
mSendQueue.pop();
|
mSendQueue.pop();
|
||||||
@ -702,8 +702,9 @@ std::optional<milliseconds> SctpTransport::rtt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::RecvCallback(struct socket *sock, union sctp_sockstore addr, void *data,
|
int SctpTransport::RecvCallback(struct socket *sock, union sctp_sockstore addr, void *data,
|
||||||
size_t len, struct sctp_rcvinfo recv_info, int flags, void *ptr) {
|
size_t len, struct sctp_rcvinfo recv_info, int flags,
|
||||||
auto *transport = static_cast<SctpTransport *>(ptr);
|
void *ulp_info) {
|
||||||
|
auto *transport = static_cast<SctpTransport *>(ulp_info);
|
||||||
|
|
||||||
std::shared_lock lock(InstancesMutex);
|
std::shared_lock lock(InstancesMutex);
|
||||||
if (Instances.find(transport) == Instances.end()) {
|
if (Instances.find(transport) == Instances.end()) {
|
||||||
@ -717,15 +718,8 @@ int SctpTransport::RecvCallback(struct socket *sock, union sctp_sockstore addr,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::SendCallback(struct socket *sock, uint32_t sb_free) {
|
int SctpTransport::SendCallback(struct socket *, uint32_t sb_free, void *ulp_info) {
|
||||||
struct sctp_paddrinfo paddrinfo = {};
|
auto *transport = static_cast<SctpTransport *>(ulp_info);
|
||||||
socklen_t len = sizeof(paddrinfo);
|
|
||||||
if (usrsctp_getsockopt(sock, IPPROTO_SCTP, SCTP_GET_PEER_ADDR_INFO, &paddrinfo, &len))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
auto sconn = reinterpret_cast<struct sockaddr_conn *>(&paddrinfo.spinfo_address);
|
|
||||||
void *ptr = sconn->sconn_addr;
|
|
||||||
auto *transport = static_cast<SctpTransport *>(ptr);
|
|
||||||
|
|
||||||
std::shared_lock lock(InstancesMutex);
|
std::shared_lock lock(InstancesMutex);
|
||||||
if (Instances.find(transport) == Instances.end())
|
if (Instances.find(transport) == Instances.end())
|
||||||
|
@ -110,8 +110,8 @@ private:
|
|||||||
std::atomic<size_t> mBytesSent = 0, mBytesReceived = 0;
|
std::atomic<size_t> mBytesSent = 0, mBytesReceived = 0;
|
||||||
|
|
||||||
static int RecvCallback(struct socket *sock, union sctp_sockstore addr, void *data, size_t len,
|
static int RecvCallback(struct socket *sock, union sctp_sockstore addr, void *data, size_t len,
|
||||||
struct sctp_rcvinfo recv_info, int flags, void *user_data);
|
struct sctp_rcvinfo recv_info, int flags, void *ulp_info);
|
||||||
static int SendCallback(struct socket *sock, uint32_t sb_free);
|
static int SendCallback(struct socket *sock, uint32_t sb_free, void *ulp_info);
|
||||||
static int WriteCallback(void *sctp_ptr, void *data, size_t len, uint8_t tos, uint8_t set_df);
|
static int WriteCallback(void *sctp_ptr, void *data, size_t len, uint8_t tos, uint8_t set_df);
|
||||||
|
|
||||||
static std::unordered_set<SctpTransport *> Instances;
|
static std::unordered_set<SctpTransport *> Instances;
|
||||||
|
@ -271,7 +271,7 @@ void TcpTransport::close() {
|
|||||||
bool TcpTransport::trySendQueue() {
|
bool TcpTransport::trySendQueue() {
|
||||||
// mSockMutex must be locked
|
// mSockMutex must be locked
|
||||||
while (auto next = mSendQueue.peek()) {
|
while (auto next = mSendQueue.peek()) {
|
||||||
auto message = *next;
|
message_ptr message = std::move(*next);
|
||||||
if (!trySendMessage(message)) {
|
if (!trySendMessage(message)) {
|
||||||
mSendQueue.exchange(message);
|
mSendQueue.exchange(message);
|
||||||
return false;
|
return false;
|
||||||
|
@ -238,11 +238,9 @@ ssize_t TlsTransport::ReadCallback(gnutls_transport_ptr_t ptr, void *data, size_
|
|||||||
|
|
||||||
int TlsTransport::TimeoutCallback(gnutls_transport_ptr_t ptr, unsigned int ms) {
|
int TlsTransport::TimeoutCallback(gnutls_transport_ptr_t ptr, unsigned int ms) {
|
||||||
TlsTransport *t = static_cast<TlsTransport *>(ptr);
|
TlsTransport *t = static_cast<TlsTransport *>(ptr);
|
||||||
if (ms != GNUTLS_INDEFINITE_TIMEOUT)
|
bool notEmpty = t->mIncomingQueue.wait(
|
||||||
t->mIncomingQueue.wait(milliseconds(ms));
|
ms != GNUTLS_INDEFINITE_TIMEOUT ? std::make_optional(milliseconds(ms)) : nullopt);
|
||||||
else
|
return notEmpty ? 1 : 0;
|
||||||
t->mIncomingQueue.wait();
|
|
||||||
return !t->mIncomingQueue.empty() ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // USE_GNUTLS==0
|
#else // USE_GNUTLS==0
|
||||||
@ -413,7 +411,7 @@ void TlsTransport::runRecvLoop() {
|
|||||||
if (!next)
|
if (!next)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
message_ptr message = *next;
|
message_ptr message = std::move(*next);
|
||||||
if (message->size() > 0)
|
if (message->size() > 0)
|
||||||
BIO_write(mInBio, message->data(), int(message->size())); // Input
|
BIO_write(mInBio, message->data(), int(message->size())); // Input
|
||||||
else
|
else
|
||||||
|
@ -45,8 +45,8 @@ bool Track::send(const byte *data, size_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<message_variant> Track::receive() {
|
std::optional<message_variant> Track::receive() {
|
||||||
if (!mRecvQueue.empty())
|
if (auto next = mRecvQueue.tryPop())
|
||||||
return to_variant(std::move(**mRecvQueue.pop()));
|
return to_variant(std::move(**next));
|
||||||
|
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
@ -110,8 +110,8 @@ bool WebSocket::isClosed() const { return mState == State::Closed; }
|
|||||||
size_t WebSocket::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
|
size_t WebSocket::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
|
||||||
|
|
||||||
std::optional<message_variant> WebSocket::receive() {
|
std::optional<message_variant> WebSocket::receive() {
|
||||||
while (!mRecvQueue.empty()) {
|
while (auto next = mRecvQueue.tryPop()) {
|
||||||
auto message = *mRecvQueue.pop();
|
message_ptr message = std::move(*next);
|
||||||
if (message->type != Message::Control)
|
if (message->type != Message::Control)
|
||||||
return to_variant(std::move(*message));
|
return to_variant(std::move(*message));
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,8 @@ size_t benchmark(milliseconds duration) {
|
|||||||
|
|
||||||
endTime = steady_clock::now();
|
endTime = steady_clock::now();
|
||||||
|
|
||||||
auto connectDuration = duration_cast<milliseconds>(openTime - startTime);
|
auto connectDuration = duration_cast<milliseconds>(dc1->isOpen() ? openTime - startTime
|
||||||
|
: steady_clock::duration(0));
|
||||||
auto transferDuration = duration_cast<milliseconds>(endTime - receivedTime);
|
auto transferDuration = duration_cast<milliseconds>(endTime - receivedTime);
|
||||||
|
|
||||||
cout << "Test duration: " << duration.count() << " ms" << endl;
|
cout << "Test duration: " << duration.count() << " ms" << endl;
|
||||||
|
Reference in New Issue
Block a user