mirror of
https://github.com/game-stop/veejay.git
synced 2025-12-16 04:40:11 +01:00
Initial checkin of veejay 1.4
git-svn-id: svn://code.dyne.org/veejay/trunk@1172 eb8d1916-c9e9-0310-b8de-cf0c9472ead5
This commit is contained in:
599
veejay-current/veejay-server/libOSC/OSC-address-space.c
Normal file
599
veejay-current/veejay-server/libOSC/OSC-address-space.c
Normal file
@@ -0,0 +1,599 @@
|
||||
/*
|
||||
Copyright <20> 1998. The Regents of the University of California (Regents).
|
||||
All Rights Reserved.
|
||||
|
||||
Written by Matt Wright, The Center for New Music and Audio Technologies,
|
||||
University of California, Berkeley.
|
||||
|
||||
Permission to use, copy, modify, distribute, and distribute modified versions
|
||||
of this software and its documentation without fee and without a signed
|
||||
licensing agreement, is hereby granted, provided that the above copyright
|
||||
notice, this paragraph and the following two paragraphs appear in all copies,
|
||||
modifications, and distributions.
|
||||
|
||||
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
|
||||
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
|
||||
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
|
||||
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
|
||||
HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
|
||||
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
|
||||
The OpenSound Control WWW page is
|
||||
http://www.cnmat.berkeley.edu/OpenSoundControl
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
OSC-address-space.c
|
||||
Matt Wright, 3/16/98
|
||||
*/
|
||||
#include <config.h>
|
||||
#include <libOSC/OSC-common.h>
|
||||
#include <libOSC/OSC-timetag.h>
|
||||
#include <libOSC/OSC-address-space.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAX_ALIASES_PER_CONTAINER 3
|
||||
#define MAX_CHILDREN_PER_CONTAINER 20
|
||||
#define MAX_METHODS_PER_CONTAINER 30
|
||||
#define BASE_NUM_TO_REALLOCATE 10
|
||||
|
||||
|
||||
struct OSCContainerStruct {
|
||||
struct OSCContainerStruct *parent;
|
||||
int numChildren;
|
||||
Name childrenNames[MAX_CHILDREN_PER_CONTAINER];
|
||||
struct OSCContainerStruct *children[MAX_CHILDREN_PER_CONTAINER];
|
||||
int numMethods;
|
||||
Name methodNames[MAX_METHODS_PER_CONTAINER];
|
||||
OSCMethod methods[MAX_METHODS_PER_CONTAINER];
|
||||
struct OSCContainerQueryResponseInfoStruct QueryResponseInfo;
|
||||
struct OSCContainerStruct *next;
|
||||
};
|
||||
|
||||
struct OSCMethodStruct {
|
||||
methodCallback callback;
|
||||
void *context;
|
||||
struct OSCMethodQueryResponseInfoStruct QueryResponseInfo;
|
||||
struct OSCMethodStruct *next;
|
||||
};
|
||||
|
||||
/* Globals */
|
||||
static Boolean Initialized = FALSE;
|
||||
static OSCcontainer OSCTopLevelContainer;
|
||||
static OSCcontainer freeContainers; /* Linked list via next field. */
|
||||
static OSCMethod freeMethods; /* Linked list via next field. */
|
||||
static void *(*RealTimeMemoryAllocator)(int numBytes);
|
||||
|
||||
|
||||
/* Note: The free list of containers should actually be a "free forest", so
|
||||
that all the subcontainers recursively under a freed container are
|
||||
automatically freed.
|
||||
|
||||
FREE: just stick the freed subtree on the front of the list.
|
||||
|
||||
ALLOC: Take all the children of the first container on the list and
|
||||
insert them in the free list, then return that first container.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/************************ Initialization and Memory ************************/
|
||||
|
||||
static void MakeFreeContainersList(int n) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i+1 < n; ++i) {
|
||||
freeContainers[i].next = &(freeContainers[i+1]);
|
||||
}
|
||||
freeContainers[n-1].next = 0;
|
||||
}
|
||||
|
||||
static void MakeFreeMethodsList(int n) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i+1 < n; ++i) {
|
||||
freeMethods[i].next = &(freeMethods[i+1]);
|
||||
}
|
||||
freeMethods[n-1].next = 0;
|
||||
}
|
||||
|
||||
OSCcontainer OSCInitAddressSpace(struct OSCAddressSpaceMemoryTuner *t) {
|
||||
int bytesNeeded;
|
||||
|
||||
if (Initialized)
|
||||
fatal_error("OSCInitAddressSpace: already initialized!");
|
||||
Initialized = TRUE;
|
||||
|
||||
RealTimeMemoryAllocator = t->RealTimeMemoryAllocator;
|
||||
|
||||
bytesNeeded = (1 + t->initNumContainers) * sizeof(*freeContainers);
|
||||
freeContainers = (OSCcontainer) (*(t->InitTimeMemoryAllocator))(bytesNeeded);
|
||||
if (freeContainers == 0) {
|
||||
fatal_error("OSCInitAddressSpace: couldn't allocate %d bytes for %d containers",
|
||||
bytesNeeded, t->initNumContainers);
|
||||
}
|
||||
|
||||
OSCTopLevelContainer = &freeContainers[t->initNumContainers];
|
||||
MakeFreeContainersList(t->initNumContainers);
|
||||
|
||||
bytesNeeded = t->initNumMethods * sizeof(*freeMethods);
|
||||
freeMethods = (OSCMethod) (*(t->InitTimeMemoryAllocator))(bytesNeeded);
|
||||
if (freeMethods == 0) {
|
||||
fatal_error("OSCInitAddressSpace: couldn't allocate %d bytes for %d methods",
|
||||
bytesNeeded, t->initNumMethods);
|
||||
}
|
||||
MakeFreeMethodsList(t->initNumMethods);
|
||||
|
||||
/* Initialize the top-level container */
|
||||
OSCTopLevelContainer->parent = 0;
|
||||
OSCTopLevelContainer->numChildren = 0;
|
||||
OSCTopLevelContainer->numMethods = 0;
|
||||
OSCTopLevelContainer->QueryResponseInfo.comment = "OSC top-level container";
|
||||
OSCTopLevelContainer->next = 0;
|
||||
|
||||
return OSCTopLevelContainer;
|
||||
}
|
||||
|
||||
|
||||
/* Container and method memory management: linked lists of free objects */
|
||||
|
||||
static OSCcontainer AllocContainer(void) {
|
||||
static int numExtraAllocs = 0;
|
||||
|
||||
OSCcontainer result;
|
||||
if (freeContainers != 0) {
|
||||
result = freeContainers;
|
||||
freeContainers = freeContainers->next;
|
||||
return result;
|
||||
}
|
||||
|
||||
OSCWarning("Out of memory for containers; trying to allocate more in real time");
|
||||
{
|
||||
int num = BASE_NUM_TO_REALLOCATE * ++numExtraAllocs;
|
||||
freeContainers = (*RealTimeMemoryAllocator)(num * sizeof(*freeContainers));
|
||||
if (freeContainers == 0) {
|
||||
OSCWarning("Real-time allocation failed");
|
||||
return 0;
|
||||
}
|
||||
MakeFreeContainersList(num);
|
||||
return AllocContainer();
|
||||
}
|
||||
}
|
||||
|
||||
//static void FreeContainer(OSCcontainer c) {
|
||||
// c->next = freeContainers;
|
||||
// freeContainers = c;
|
||||
//}
|
||||
|
||||
static OSCMethod AllocMethod(void) {
|
||||
static int numExtraAllocs = 0;
|
||||
OSCMethod result;
|
||||
|
||||
if (freeMethods != 0) {
|
||||
result = freeMethods;
|
||||
freeMethods = freeMethods->next;
|
||||
return result;
|
||||
}
|
||||
|
||||
OSCWarning("Out of memory for methods; trying to allocate more in real time");
|
||||
{
|
||||
int num = BASE_NUM_TO_REALLOCATE * ++numExtraAllocs;
|
||||
freeMethods = (*RealTimeMemoryAllocator)(num * sizeof(*freeMethods));
|
||||
if (freeMethods == 0) {
|
||||
OSCWarning("Real-time allocation failed");
|
||||
return 0;
|
||||
}
|
||||
MakeFreeMethodsList(num);
|
||||
return AllocMethod();
|
||||
}
|
||||
}
|
||||
|
||||
//static void FreeMethod(OSCMethod c) {
|
||||
// c->next = freeMethods;
|
||||
// freeMethods = c;
|
||||
//}
|
||||
|
||||
|
||||
/**************************** Containers ****************************/
|
||||
|
||||
/* Managing the tree of containers and subcontainers, with aliases */
|
||||
|
||||
void AddSubContainer(OSCcontainer parent, OSCcontainer child, Name name) {
|
||||
if (parent->numChildren >= MAX_CHILDREN_PER_CONTAINER) {
|
||||
fatal_error("AddSubContainer: exceeded MAX_CHILDREN_PER_CONTAINER (%d)\n"
|
||||
"Increase the value in OSC-address-space.c and recompile.",
|
||||
MAX_CHILDREN_PER_CONTAINER);
|
||||
}
|
||||
|
||||
parent->childrenNames[parent->numChildren] = name;
|
||||
parent->children[parent->numChildren] = child;
|
||||
++(parent->numChildren);
|
||||
}
|
||||
|
||||
|
||||
Boolean OSCAddContainerAlias(OSCcontainer container, Name otherName) {
|
||||
if (container->parent->numChildren >= MAX_CHILDREN_PER_CONTAINER) {
|
||||
return FALSE;
|
||||
}
|
||||
AddSubContainer(container->parent, container, otherName);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void RemoveSubContainer(OSCcontainer parent, OSCcontainer child) {
|
||||
int i, numRemoved;
|
||||
|
||||
/* Remove every pointer to the container, even if it has multiple aliases */
|
||||
|
||||
numRemoved = 0;
|
||||
for (i = 0; i < parent->numChildren; ++i) {
|
||||
if (parent->children[i] != child) {
|
||||
parent->children[i-numRemoved] = parent->children[i];
|
||||
parent->childrenNames[i-numRemoved] = parent->childrenNames[i];
|
||||
} else {
|
||||
++numRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
parent->numChildren -= numRemoved;
|
||||
|
||||
if (numRemoved == 0) {
|
||||
fatal_error("RemoveSubContainer: subcontainer not found!\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Boolean OSCRemoveContainerAlias(OSCcontainer container, Name otherName) {
|
||||
int i, j;
|
||||
OSCcontainer parent = container->parent;
|
||||
Boolean found = FALSE;
|
||||
|
||||
for (i = 0; i < parent->numChildren; ++i) {
|
||||
if (parent->childrenNames[i] == otherName) {
|
||||
if (parent->children[i] != container) {
|
||||
fatal_error("OSCRemoveContainerAlias: %s is actually a sibling's name!",
|
||||
otherName);
|
||||
}
|
||||
found = TRUE;
|
||||
for (j = i+1; j < parent->numChildren; ++j) {
|
||||
parent->children[j-1] = parent->children[j];
|
||||
parent->childrenNames[j-1] = parent->childrenNames[j];
|
||||
--(parent->numChildren);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fatal_error("OSCRemoveContainerAlias: %s not found!", otherName);
|
||||
}
|
||||
|
||||
/* Now make sure the child still exists under another name */
|
||||
for (i = 0; i < parent->numChildren; ++i) {
|
||||
if (parent->children[i] == container) return TRUE;
|
||||
}
|
||||
|
||||
OSCWarning("OSCRemoveContainerAlias: %s was the last name for that subcontainer");
|
||||
|
||||
/* xxx should recursively free the container and its children... */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
OSCcontainer OSCNewContainer(Name name, OSCcontainer parent,
|
||||
struct OSCContainerQueryResponseInfoStruct *QueryResponseInfo) {
|
||||
OSCcontainer me;
|
||||
|
||||
me = AllocContainer();
|
||||
if (me == 0) return 0;
|
||||
|
||||
if (strchr(name, '/') != NULL) {
|
||||
OSCProblem("Container name \"%s\" contains a slash --- not good.",
|
||||
name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
me->parent = parent;
|
||||
AddSubContainer(me->parent, me, name);
|
||||
me->numChildren = 0;
|
||||
me->numMethods = 0;
|
||||
me->QueryResponseInfo = (*QueryResponseInfo);
|
||||
return me;
|
||||
}
|
||||
|
||||
|
||||
static const char *ContainerName(OSCcontainer c) {
|
||||
/* Return the first name associated with me in my parent's child list.
|
||||
(Assume all later ones are aliases.) */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < c->parent->numChildren; ++i) {
|
||||
if (c->parent->children[i] == c) {
|
||||
return c->parent->childrenNames[i];
|
||||
}
|
||||
}
|
||||
fatal_error("ContainerName: Container %p isn't in its parent's child list.", c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gasHelp(char *target, int maxlength, OSCcontainer c );
|
||||
|
||||
Boolean OSCGetAddressString(char *target, int maxLength, OSCcontainer c) {
|
||||
int lenNeeded;
|
||||
|
||||
if (maxLength <= 1) return FALSE;
|
||||
|
||||
lenNeeded = gasHelp(target, maxLength-1, c) + 1; /* -1, +1 are for null char. */
|
||||
if (lenNeeded > maxLength) {
|
||||
OSCProblem("Address string too long (room for %d chars; need %d)",
|
||||
maxLength, lenNeeded);
|
||||
target[0] = '\0';
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int gasHelp(char *target, int maxLength, OSCcontainer c) {
|
||||
int sublength, length;
|
||||
const char *myName;
|
||||
|
||||
/* printf("*** gasHelp %s %d %p %s\n", target, maxLength, c, c->name); */
|
||||
|
||||
if (c == OSCTopLevelContainer) {
|
||||
target[0] = '/';
|
||||
target[1] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
myName = ContainerName(c);
|
||||
sublength = gasHelp(target, maxLength, c->parent);
|
||||
length = sublength + strlen(myName) + 1; /* +1 is for trailing slash */
|
||||
if (length > maxLength) {
|
||||
return length;
|
||||
}
|
||||
|
||||
strcpy(target+sublength, myName);
|
||||
target[length-1] = '/';
|
||||
target[length] = '\0';
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
/**************************** Methods ****************************/
|
||||
|
||||
#define LONG_ADDR_SIZE 1000 /* Just for error messages */
|
||||
|
||||
OSCMethod OSCNewMethod(Name name, OSCcontainer me, methodCallback callback,
|
||||
void *context, struct OSCMethodQueryResponseInfoStruct *QueryResponseInfo) {
|
||||
|
||||
char addr[LONG_ADDR_SIZE];
|
||||
OSCMethod m;
|
||||
|
||||
if (strchr(name, '/') != NULL) {
|
||||
OSCProblem("Method name \"%s\" contains a slash --- not good.",
|
||||
name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (me->numMethods >= MAX_METHODS_PER_CONTAINER) {
|
||||
addr[0] = '\0';
|
||||
OSCGetAddressString(addr, LONG_ADDR_SIZE, me);
|
||||
OSCProblem("OSCNewMethod: container %s already has %d methods; can't add another\n"
|
||||
"Change MAX_METHODS_PER_CONTAINER in OSC-address-space.c and recompile.",
|
||||
addr, me->numMethods);
|
||||
return 0;
|
||||
}
|
||||
|
||||
m = AllocMethod();
|
||||
if (!m) return 0;
|
||||
|
||||
m->callback = callback;
|
||||
m->context = context;
|
||||
m->QueryResponseInfo = *QueryResponseInfo;
|
||||
|
||||
me->methodNames[me->numMethods] = name;
|
||||
me->methods[me->numMethods] = m;
|
||||
++(me->numMethods);
|
||||
return m;
|
||||
}
|
||||
|
||||
/**************************** Queries ****************************/
|
||||
|
||||
void OSCInitContainerQueryResponseInfo(struct OSCContainerQueryResponseInfoStruct *i) {
|
||||
i->comment = 0;
|
||||
}
|
||||
|
||||
void OSCInitMethodQueryResponseInfo(struct OSCMethodQueryResponseInfoStruct *i) {
|
||||
i->description = 0;
|
||||
i->pvq = 0;
|
||||
}
|
||||
|
||||
/******************************* Debug ********************************/
|
||||
|
||||
|
||||
static int ContainerAliases(OSCcontainer c, char *target) {
|
||||
/* Write a space-delimited list of alias names in the given string,
|
||||
and return the number */
|
||||
int i, n;
|
||||
|
||||
if (c == OSCTopLevelContainer) return 0;
|
||||
target[0] = '\0';
|
||||
n = 0;
|
||||
|
||||
for (i = 0; i < c->parent->numChildren; ++i) {
|
||||
if (c->parent->children[i] == c) {
|
||||
if (n > 0) {
|
||||
strcat(target, " ");
|
||||
strcat(target, c->parent->childrenNames[i]);
|
||||
}
|
||||
++n;
|
||||
}
|
||||
}
|
||||
if (n == 0) fatal_error("ContainerAliases: internal inconsistency");
|
||||
|
||||
return n-1;
|
||||
}
|
||||
|
||||
#define BIG_ADDRESS 50
|
||||
|
||||
static void PrintHelp(OSCcontainer c) {
|
||||
char addr[BIG_ADDRESS];
|
||||
char aliasNames[1000];
|
||||
|
||||
int i, j, numAliases;
|
||||
|
||||
if (OSCGetAddressString(addr, BIG_ADDRESS, c) == FALSE) {
|
||||
printf(" /.../%s", ContainerName(c));
|
||||
} else {
|
||||
printf(" %s", addr);
|
||||
}
|
||||
|
||||
numAliases = ContainerAliases(c, aliasNames);
|
||||
if (numAliases > 0) {
|
||||
printf(" (%d aliases:%s)", numAliases, aliasNames);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
for (i = 0; i < c->numMethods; ++i) {
|
||||
printf(" %s%s: %s\n", addr, c->methodNames[i],
|
||||
c->methods[i]->QueryResponseInfo.description);
|
||||
}
|
||||
|
||||
/* Forgive this quadratic kludge: */
|
||||
for (i = 0; i < c->numChildren; ++i) {
|
||||
int matches = 0;
|
||||
for (j = 0; j < i; ++j) {
|
||||
if (c->children[j] == c->children[i]) {
|
||||
/* c->children[i] is just an alias to c->children[j],
|
||||
which we already printed, so ignore it. */
|
||||
matches ++;
|
||||
}
|
||||
}
|
||||
|
||||
if(matches == 0 ) PrintHelp(c->children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void OSCPrintWholeAddressSpace(void) {
|
||||
printf("\n----- The OSC address space -----\n");
|
||||
PrintHelp(OSCTopLevelContainer);
|
||||
printf("...end of OSC address space.\n\n\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/***************************** Dispatching *****************************/
|
||||
|
||||
#include <libOSC/OSC-dispatch.h>
|
||||
#include <libOSC/OSC-callbacklist.h>
|
||||
#include <libOSC/OSC-pattern-match.h>
|
||||
|
||||
/* To do quick concatenation of singly-linked lists, we pass around
|
||||
this data structure that points to the first and last elements: */
|
||||
|
||||
typedef struct callbackListEnds_struct {
|
||||
callbackList begin;
|
||||
callbackList end;
|
||||
} callbackListEnds;
|
||||
|
||||
/* Helper proc. declarations */
|
||||
static callbackListEnds DispatchSubMessage(char *pattern, OSCcontainer c);
|
||||
static char *NextSlashOrNull(char *p);
|
||||
|
||||
|
||||
callbackList OSCDispatchMessage(char *pattern) {
|
||||
callbackListEnds result;
|
||||
|
||||
if (pattern[0] != '/') {
|
||||
OSCProblem("Invalid address \"%s\" does not begin with /", pattern);
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = DispatchSubMessage(pattern+1, OSCTopLevelContainer);
|
||||
|
||||
return result.begin;
|
||||
}
|
||||
|
||||
#define LONG_ADDR_LEN 100
|
||||
|
||||
|
||||
static callbackListEnds DispatchSubMessage(char *pattern, OSCcontainer c) {
|
||||
callbackListEnds result;
|
||||
char *nextSlash, *restOfPattern;
|
||||
char offendingAddr[LONG_ADDR_LEN];
|
||||
int i;
|
||||
|
||||
result.begin = result.end = 0;
|
||||
nextSlash = NextSlashOrNull(pattern);
|
||||
|
||||
if (*nextSlash == '\0') {
|
||||
/* Base case: the pattern names methods of this container. */
|
||||
for (i = 0; i < c->numMethods; i++) {
|
||||
if (PatternMatch(pattern, c->methodNames[i])) {
|
||||
callbackList node = AllocCallbackListNode(c->methods[i]->callback,
|
||||
c->methods[i]->context,
|
||||
result.begin);
|
||||
if (node == 0) {
|
||||
/* Excuse the hairyness of the code to generate the error message. */
|
||||
if (OSCGetAddressString(offendingAddr,
|
||||
LONG_ADDR_LEN-strlen(c->methodNames[i]),
|
||||
c)) {
|
||||
strcat(offendingAddr, c->methodNames[i]);
|
||||
} else {
|
||||
strcpy(offendingAddr, c->methodNames[i]);
|
||||
}
|
||||
|
||||
OSCWarning("No memory for callback node; not invoking %s",
|
||||
offendingAddr);
|
||||
} else {
|
||||
if (result.end == 0) {
|
||||
result.end = node;
|
||||
}
|
||||
result.begin = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Recursive case: in the middle of an address, so the job at this
|
||||
step is to look for containers that match. We temporarily turn
|
||||
the next slash into a null so pattern will be a null-terminated
|
||||
string of the stuff between the slashes. */
|
||||
*nextSlash = '\0';
|
||||
restOfPattern = nextSlash + 1;
|
||||
|
||||
for (i = 0; i < c->numChildren; ++i) {
|
||||
if (PatternMatch(pattern, c->childrenNames[i])) {
|
||||
callbackListEnds subResult =
|
||||
DispatchSubMessage(restOfPattern, c->children[i]);
|
||||
if (result.end == 0) {
|
||||
result = subResult;
|
||||
} else {
|
||||
subResult.end->next = result.begin;
|
||||
result.begin = subResult.begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
*nextSlash = '/';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static char *NextSlashOrNull(char *p) {
|
||||
while (*p != '/' && *p != '\0') {
|
||||
p++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user