/*--------------------------------------------------------------------*/ /* path.c */ /* Author: Christopher Moretti */ /*--------------------------------------------------------------------*/ #include #include #include #include "dynarray.h" #include "path.h" /* An absolute path */ struct path { /* The string representation of the path, which uses '/' as the component delimiter */ const char *pcPath; /* The string length of pcPath */ size_t ulLength; /* The ordered collection of component strings in the path */ DynArray_T oDComponents; }; /* Frees pcStr. This wrapper is used to match the requirements of the callback function pointer passed to DynArray_map. pvExtra is unused. */ static void Path_freeString(char *pcStr, void *pvExtra) { /* pcStr may be NULL, as this is a no-op to free. pvExtra may be NULL, as it is unused. */ free(pcStr); } /* Sets *poDComponents to be an ordered collection of component strings in pcPath, or NULL if an error occurs. Returns one of the following statuses: * SUCCESS if no error occurrs * BAD_PATH if pcPath is the empty string, or begins or ends with a '/', or contains consecutive '/' delimiters * MEMORY_ERROR if memory could not be allocated to complete request */ static int Path_split(const char *pcPath, DynArray_T *poDComponents) { const char *pcStart = pcPath; const char *pcEnd = pcPath; char *pcCopy; DynArray_T oDSubstrings; assert(pcPath != NULL); assert(poDComponents != NULL); /* path cannot be empty string */ if(*pcPath == '\0') { *poDComponents = NULL; return BAD_PATH; } oDSubstrings = DynArray_new(0); if(oDSubstrings == NULL) { *poDComponents = NULL; return MEMORY_ERROR; } /* validate and split pcPath */ while(*pcEnd != '\0') { pcEnd = pcStart; /* component can't start with delimiter */ if(*pcEnd == '/') { DynArray_map(oDSubstrings, (void (*)(void*, void*)) Path_freeString, NULL); DynArray_free(oDSubstrings); *poDComponents = NULL; return BAD_PATH; } /* advance pcEnd to end of next token */ while(*pcEnd != '/' && *pcEnd != '\0') pcEnd++; /* final component can't end with slash */ if(*pcEnd == '\0' && *(pcEnd-1) == '/') { DynArray_map(oDSubstrings, (void (*)(void*, void*)) Path_freeString, NULL); DynArray_free(oDSubstrings); *poDComponents = NULL; return BAD_PATH; } pcCopy = calloc((size_t)(pcEnd-pcStart+1), sizeof(char)); if(pcCopy == NULL) { DynArray_map(oDSubstrings, (void (*)(void*, void*)) Path_freeString, NULL); DynArray_free(oDSubstrings); *poDComponents = NULL; return MEMORY_ERROR; } if( DynArray_add(oDSubstrings, pcCopy) == 0) { DynArray_map(oDSubstrings, (void (*)(void*, void*)) Path_freeString, NULL); DynArray_free(oDSubstrings); *poDComponents = NULL; return MEMORY_ERROR; } while(pcStart != pcEnd) { *pcCopy = *pcStart; pcCopy++; pcStart++; } pcStart++; } *poDComponents = oDSubstrings; return SUCCESS; } int Path_new(const char *pcPath, Path_T *poPResult) { struct path *psNew; int iSplitResult; assert(pcPath != NULL); assert(poPResult != NULL); psNew = calloc(1, sizeof(struct path)); if(psNew == NULL) { *poPResult = NULL; return MEMORY_ERROR; } /* instantiate and fill list of components */ iSplitResult = Path_split(pcPath, &psNew->oDComponents); if(iSplitResult != SUCCESS) { Path_free(psNew); *poPResult = NULL; return iSplitResult; } psNew->ulLength = strlen(pcPath); psNew->pcPath = malloc(psNew->ulLength+1); if(psNew->pcPath == NULL) { Path_free(psNew); *poPResult = NULL; return MEMORY_ERROR; } strcpy((char *)psNew->pcPath, pcPath); *poPResult = psNew; return SUCCESS; } int Path_prefix(Path_T oPPath, size_t ulDepth, Path_T *poPResult) { struct path *psNew; size_t ulIndex, ulLength, ulSum; const char *pcComponent; char *pcCopy; char *pcBuild; char *pcInsert; assert(oPPath != NULL); assert(poPResult != NULL); /* cannot build empty path */ if(ulDepth == 0) { *poPResult = NULL; return NO_SUCH_PATH; } /* cannot have a prefix longer than oPPath */ if(Path_getDepth(oPPath) < ulDepth) { *poPResult = NULL; return NO_SUCH_PATH; } psNew = calloc(1, sizeof(struct path)); if(psNew == NULL) { *poPResult = NULL; return MEMORY_ERROR; } psNew->oDComponents = DynArray_new(ulDepth); if(psNew->oDComponents == NULL) { Path_free(psNew); *poPResult = NULL; return MEMORY_ERROR; } pcBuild = calloc(Path_getStrLength(oPPath)+1, sizeof(char)); if(pcBuild == NULL) { Path_free(psNew); *poPResult = NULL; return MEMORY_ERROR; } pcInsert = pcBuild; ulSum = 0; for(ulIndex = 0; ulIndex < ulDepth; ulIndex++) { /* deep copy each component to new DynArray */ pcComponent = Path_getComponent(oPPath, ulIndex); ulLength = strlen(pcComponent); pcCopy = calloc(ulLength + 1, sizeof(char)); if(pcCopy == NULL) { free(pcBuild); Path_free(psNew); *poPResult = NULL; return MEMORY_ERROR; } strcpy(pcCopy, pcComponent); (void) DynArray_set(psNew->oDComponents, ulIndex, pcCopy); /* construct prefix's pathname string */ strcpy(pcInsert, pcComponent); pcInsert[ulLength] = '/'; ulSum += ulLength + 1; pcInsert += ulLength + 1; } pcBuild[ulSum-1] = '\0'; /* shrink allocation to fit prefix's pathname string if needed */ pcInsert = realloc(pcBuild, ulSum); if(pcInsert == NULL) { free(pcBuild); Path_free(psNew); *poPResult = NULL; return MEMORY_ERROR; } psNew->ulLength = ulSum-1; psNew->pcPath = pcInsert; *poPResult = psNew; return SUCCESS; } int Path_dup(Path_T oPPath, Path_T *poPResult) { assert(oPPath != NULL); assert(poPResult != NULL); return Path_prefix(oPPath, Path_getDepth(oPPath), poPResult); } void Path_free(Path_T oPPath) { if(oPPath != NULL) { free((char *)oPPath->pcPath); if(oPPath->oDComponents != NULL) { DynArray_map(oPPath->oDComponents, (void (*)(void*, void*)) Path_freeString, NULL); DynArray_free(oPPath->oDComponents); } } free((struct path*) oPPath); } const char *Path_getPathname(Path_T oPPath) { assert(oPPath != NULL); return oPPath->pcPath; } size_t Path_getStrLength(Path_T oPPath) { assert(oPPath != NULL); return oPPath->ulLength; } int Path_comparePath(Path_T oPPath1, Path_T oPPath2) { assert(oPPath1 != NULL); assert(oPPath2 != NULL); return strcmp(oPPath1->pcPath, oPPath2->pcPath); } int Path_compareString(Path_T oPPath, const char *pcStr) { assert(oPPath != NULL); assert(pcStr != NULL); return strcmp(oPPath->pcPath, pcStr); } size_t Path_getDepth(Path_T oPPath) { assert(oPPath != NULL); return DynArray_getLength(oPPath->oDComponents); } size_t Path_getSharedPrefixDepth(Path_T oPPath1, Path_T oPPath2) { size_t ulDepth1, ulDepth2, ulMin, i; assert(oPPath1 != NULL); assert(oPPath2 != NULL); ulDepth1 = Path_getDepth(oPPath1); ulDepth2 = Path_getDepth(oPPath2); if(ulDepth1 < ulDepth2) ulMin = ulDepth1; else ulMin = ulDepth2; for(i = 0; i < ulMin; i++) { if(strcmp(Path_getComponent(oPPath1, i), Path_getComponent(oPPath2, i))) return i; } return ulMin; } const char *Path_getComponent(Path_T oPPath, size_t ulLevel) { assert(oPPath != NULL); if(ulLevel >= Path_getDepth(oPPath)) return NULL; return DynArray_get(oPPath->oDComponents, ulLevel); }