📄 expand.c
字号:
/* * Sort the pathnames that matched. */ qsort(ef->result.files, ef->result.nfile, sizeof(ef->result.files[0]), ef_cmp_strings); };/* * Return the result container. */ return &ef->result;}/*....................................................................... * Attempt to recursively match the given pattern with the contents of * the current directory, descending sub-directories as needed. * * Input: * ef ExpandFile * The pathname expansion resource object. * dr DirReader * The directory reader object of the directory * to be searched. * pattern const char * The pattern to match with files in the current * directory. * separate int When appending a filename from the specified * directory to ef->pathname, insert a directory * separator between the existing pathname and * the filename, unless separate is zero. * Output: * return int 0 - OK. * 1 - Error. */static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr, const char *pattern, int separate){ const char *nextp; /* The pointer to the character that follows the part */ /* of the pattern that is to be matched with files */ /* in the current directory. */ char *file; /* The name of the file being matched */ int pathlen; /* The length of ef->pathname[] on entry to this */ /* function *//* * Record the current length of the pathname string recorded in * ef->pathname[]. */ pathlen = strlen(ef->path->name);/* * Get a pointer to the character that follows the end of the part of * the pattern that should be matched to files within the current directory. * This will either point to a directory separator, or to the '\0' terminator * of the pattern string. */ for(nextp=pattern; *nextp && strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; nextp++) ;/* * Read each file from the directory, attempting to match it to the * current pattern. */ while((file=_dr_next_file(dr)) != NULL) {/* * Does the latest file match the pattern up to nextp? */ if(ef_string_matches_pattern(file, pattern, file[0]=='.', nextp)) {/* * Append the new directory entry to the current matching pathname. */ if((separate && _pn_append_to_path(ef->path, FS_DIR_SEP, -1, 0)==NULL) || _pn_append_to_path(ef->path, file, -1, 0)==NULL) { strcpy(ef->errmsg, "Insufficient memory to record path"); return 1; };/* * If we have reached the end of the pattern, record the accumulated * pathname in the list of matching files. */ if(*nextp == '\0') { if(ef_record_pathname(ef, ef->path->name, 0)) return 1;/* * If the matching directory entry is a subdirectory, and the * next character of the pattern is a directory separator, * recursively call the current function to scan the sub-directory * for matches. */ } else if(_pu_path_is_dir(ef->path->name) && strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) {/* * If the pattern finishes with the directory separator, then * record the pathame as matching. */ if(nextp[FS_DIR_SEP_LEN] == '\0') { if(ef_record_pathname(ef, ef->path->name, 0)) return 1;/* * Match files within the directory. */ } else { DirNode *subdnode = ef_open_dir(ef, ef->path->name); if(subdnode) { if(ef_match_relative_pathname(ef, subdnode->dr, nextp+FS_DIR_SEP_LEN, 1)) { subdnode = ef_close_dir(ef, subdnode); return 1; }; subdnode = ef_close_dir(ef, subdnode); }; }; };/* * Remove the latest filename from the pathname string, so that * another matching file can be appended. */ ef->path->name[pathlen] = '\0'; }; }; return 0;}/*....................................................................... * Record a new matching filename. * * Input: * ef ExpandFile * The filename-match resource object. * pathname const char * The pathname to record. * remove_escapes int If true, remove backslash escapes in the * recorded copy of the pathname. * Output: * return int 0 - OK. * 1 - Error (ef->errmsg will contain a * description of the error). */static int ef_record_pathname(ExpandFile *ef, const char *pathname, int remove_escapes){ char *copy; /* The recorded copy of pathname[] *//* * Attempt to make a copy of the pathname in the cache. */ copy = ef_cache_pathname(ef, pathname, remove_escapes); if(!copy) return 1;/* * If there isn't room to record a pointer to the recorded pathname in the * array of files, attempt to extend the array. */ if(ef->result.nfile + 1 > ef->files_dim) { int files_dim = ef->files_dim + MATCH_BLK_FACT; char **files = (char **) realloc(ef->result.files, files_dim * sizeof(files[0])); if(!files) { sprintf(ef->errmsg, "Insufficient memory to record all of the matching filenames"); return 1; }; ef->result.files = files; ef->files_dim = files_dim; };/* * Record a pointer to the new match. */ ef->result.files[ef->result.nfile++] = copy; return 0;}/*....................................................................... * Record a pathname in the cache. * * Input: * ef ExpandFile * The filename-match resource object. * pathname char * The pathname to record. * remove_escapes int If true, remove backslash escapes in the * copy of the pathname. * Output: * return char * The pointer to the copy of the pathname. * On error NULL is returned and a description * of the error is left in ef->errmsg[]. */static char *ef_cache_pathname(ExpandFile *ef, const char *pathname, int remove_escapes){ char *copy = _sg_store_string(ef->sg, pathname, remove_escapes); if(!copy) strcpy(ef->errmsg, "Insufficient memory to store pathname"); return copy;}/*....................................................................... * Clear the results of the previous expansion operation, ready for the * next. * * Input: * ef ExpandFile * The pathname expansion resource object. */static void ef_clear_files(ExpandFile *ef){ _clr_StringGroup(ef->sg); _pn_clear_path(ef->path); ef->result.exists = 0; ef->result.nfile = 0; ef->errmsg[0] = '\0'; return;}/*....................................................................... * Get a new directory reader object from the cache. * * Input: * ef ExpandFile * The pathname expansion resource object. * pathname const char * The pathname of the directory. * Output: * return DirNode * The cache entry of the new directory reader, * or NULL on error. On error, ef->errmsg will * contain a description of the error. */static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname){ char *errmsg = NULL; /* An error message from a called function */ DirNode *node; /* The cache node used *//* * Get the directory reader cache. */ DirCache *cache = &ef->cache;/* * Extend the cache if there are no free cache nodes. */ if(!cache->next) { node = (DirNode *) _new_FreeListNode(cache->mem); if(!node) { sprintf(ef->errmsg, "Insufficient memory to open a new directory"); return NULL; };/* * Initialize the cache node. */ node->next = NULL; node->prev = NULL; node->dr = NULL;/* * Allocate a directory reader object. */ node->dr = _new_DirReader(); if(!node->dr) { sprintf(ef->errmsg, "Insufficient memory to open a new directory"); node = (DirNode *) _del_FreeListNode(cache->mem, node); return NULL; };/* * Append the node to the cache list. */ node->prev = cache->tail; if(cache->tail) cache->tail->next = node; else cache->head = node; cache->next = cache->tail = node; };/* * Get the first unused node, but don't remove it from the list yet. */ node = cache->next;/* * Attempt to open the specified directory. */ if(_dr_open_dir(node->dr, pathname, &errmsg)) { strncpy(ef->errmsg, errmsg, ERRLEN); ef->errmsg[ERRLEN] = '\0'; return NULL; };/* * Now that we have successfully opened the specified directory, * remove the cache node from the list, and relink the list around it. */ cache->next = node->next; if(node->prev) node->prev->next = node->next; else cache->head = node->next; if(node->next) node->next->prev = node->prev; else cache->tail = node->prev; node->next = node->prev = NULL;/* * Return the successfully initialized cache node to the caller. */ return node;}/*....................................................................... * Return a directory reader object to the cache, after first closing * the directory that it was managing. * * Input: * ef ExpandFile * The pathname expansion resource object. * node DirNode * The cache entry of the directory reader, as returned * by ef_open_dir(). * Output: * return DirNode * The deleted DirNode (ie. allways NULL). */static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node){/* * Get the directory reader cache. */ DirCache *cache = &ef->cache;/* * Close the directory. */ _dr_close_dir(node->dr);/* * Return the node to the tail of the cache list. */ node->next = NULL; node->prev = cache->tail; if(cache->tail) cache->tail->next = node; else cache->head = cache->tail = node; if(!cache->next) cache->next = node; return NULL;}/*....................................................................... * Return non-zero if the specified file name matches a given glob * pattern. * * Input: * file const char * The file-name component to be matched to the pattern. * pattern const char * The start of the pattern to match against file[]. * xplicit int If non-zero, the first character must be matched * explicitly (ie. not with a wildcard). * nextp const char * The pointer to the the character following the * end of the pattern in pattern[]. * Output: * return int 0 - Doesn't match. * 1 - The file-name string matches the pattern. */static int ef_string_matches_pattern(const char *file, const char *pattern, int xplicit, const char *nextp){ const char *pptr = pattern; /* The pointer used to scan the pattern */ const char *fptr = file; /* The pointer used to scan the filename string *//* * Match each character of the pattern in turn. */ while(pptr < nextp) {/* * Handle the next character of the pattern. */ switch(*pptr) {/* * A match zero-or-more characters wildcard operator. */ case '*':/* * Skip the '*' character in the pattern. */ pptr++;/* * If wildcards aren't allowed, the pattern doesn't match. */ if(xplicit) return 0;/* * If the pattern ends with a the '*' wildcard, then the * rest of the filename matches this. */ if(pptr >= nextp) return 1;/* * Using the wildcard to match successively longer sections of * the remaining characters of the filename, attempt to match * the tail of the filename against the tail of the pattern. */ for( ; *fptr; fptr++) { if(ef_string_matches_pattern(fptr, pptr, 0, nextp)) return 1; }; return 0; /* The pattern following the '*' didn't match */ break;/* * A match-one-character wildcard operator. */ case '?':/* * If there is a character to be matched, skip it and advance the * pattern pointer. */ if(!xplicit && *fptr) { fptr++; pptr++;/* * If we hit the end of the filename string, there is no character * matching the operator, so the string doesn't match. */ } else { return 0; }; break;/* * A character range operator, with the character ranges enclosed * in matching square brackets. */ case '[': if(xplicit || !ef_matches_range(*fptr++, ++pptr, &pptr)) return 0; break;/* * A backslash in the pattern prevents the following character as * being seen as a special character. */ case '\\': pptr++; /* Note fallthrough to default *//* * A normal character to be matched explicitly. */ default: if(*fptr == *pptr) { fptr++; pptr++; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -