diff options
Diffstat (limited to 'libtecla-1.4.1/cplfile.c')
-rw-r--r-- | libtecla-1.4.1/cplfile.c | 874 |
1 files changed, 0 insertions, 874 deletions
diff --git a/libtecla-1.4.1/cplfile.c b/libtecla-1.4.1/cplfile.c deleted file mode 100644 index 73eef1d..0000000 --- a/libtecla-1.4.1/cplfile.c +++ /dev/null @@ -1,874 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -/* - * Standard includes. - */ -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include <errno.h> -#include <string.h> -#include <ctype.h> - -/* - * Local includes. - */ -#include "libtecla.h" -#include "direader.h" -#include "homedir.h" -#include "pathutil.h" -#include "cplfile.h" - -/* - * Set the maximum length allowed for usernames. - * names. - */ -#define USR_LEN 100 - -/* - * Set the maximum length allowed for environment variable names. - */ -#define ENV_LEN 100 - -/* - * Set the max length of the error-reporting string. There is no point - * in this being longer than the width of a typical terminal window. - * In composing error messages, I have assumed that this number is - * at least 80, so you don't decrease it below this number. - */ -#define ERRLEN 200 - -/* - * The resources needed to complete a filename are maintained in objects - * of the following type. - */ -struct CompleteFile { - DirReader *dr; /* A directory reader */ - HomeDir *home; /* A home directory expander */ - PathName *path; /* The buffer in which to accumulate the path */ - PathName *buff; /* A pathname work buffer */ - char usrnam[USR_LEN+1]; /* The buffer used when reading the names of */ - /* users. */ - char envnam[ENV_LEN+1]; /* The buffer used when reading the names of */ - /* environment variables. */ - char errmsg[ERRLEN+1]; /* The error-report buffer */ -}; - -static int cf_expand_home_dir(CompleteFile *cf, const char *user); -static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, - const char *prefix, const char *line, - int word_start, int word_end, int escaped); -static HOME_DIR_FN(cf_homedir_callback); -static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, - const char *line, int word_start, int word_end, - int escaped, CplCheckFn *check_fn, - void *check_data); -static char *cf_read_name(CompleteFile *cf, const char *type, - const char *string, int slen, - char *nambuf, int nammax); -static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, - int add_escapes); - -/* - * A stack based object of the following type is used to pass data to the - * cf_homedir_callback() function. - */ -typedef struct { - CompleteFile *cf; /* The file-completion resource object */ - WordCompletion *cpl; /* The string-completion rsource object */ - const char *prefix; /* The username prefix to be completed */ - const char *line; /* The line from which the prefix was extracted */ - int word_start; /* The index in line[] of the start of the username */ - int word_end; /* The index in line[] following the end of the prefix */ - int escaped; /* If true, add escapes to the completion suffixes */ -} CfHomeArgs; - -/*....................................................................... - * Create a new file-completion object. - * - * Output: - * return CompleteFile * The new object, or NULL on error. - */ -CompleteFile *_new_CompleteFile(void) -{ - CompleteFile *cf; /* The object to be returned */ -/* - * Allocate the container. - */ - cf = (CompleteFile *) malloc(sizeof(CompleteFile)); - if(!cf) { - fprintf(stderr, "_new_CompleteFile: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to _del_CompleteFile(). - */ - cf->dr = NULL; - cf->home = NULL; - cf->path = NULL; - cf->buff = NULL; - cf->usrnam[0] = '\0'; - cf->envnam[0] = '\0'; - cf->errmsg[0] = '\0'; -/* - * Create the object that is used for reading directories. - */ - cf->dr = _new_DirReader(); - if(!cf->dr) - return _del_CompleteFile(cf); -/* - * Create the object that is used to lookup home directories. - */ - cf->home = _new_HomeDir(); - if(!cf->home) - return _del_CompleteFile(cf); -/* - * Create the buffer in which the completed pathname is accumulated. - */ - cf->path = _new_PathName(); - if(!cf->path) - return _del_CompleteFile(cf); -/* - * Create a pathname work buffer. - */ - cf->buff = _new_PathName(); - if(!cf->buff) - return _del_CompleteFile(cf); - return cf; -} - -/*....................................................................... - * Delete a file-completion object. - * - * Input: - * cf CompleteFile * The object to be deleted. - * Output: - * return CompleteFile * The deleted object (always NULL). - */ -CompleteFile *_del_CompleteFile(CompleteFile *cf) -{ - if(cf) { - cf->dr = _del_DirReader(cf->dr); - cf->home = _del_HomeDir(cf->home); - cf->path = _del_PathName(cf->path); - cf->buff = _del_PathName(cf->buff); - free(cf); - }; - return NULL; -} - -/*....................................................................... - * Look up the possible completions of the incomplete filename that - * lies between specified indexes of a given command-line string. - * - * Input: - * cpl WordCompletion * The object in which to record the completions. - * cf CompleteFile * The filename-completion resource object. - * line const char * The string containing the incomplete filename. - * word_start int The index of the first character in line[] - * of the incomplete filename. - * word_end int The index of the character in line[] that - * follows the last character of the incomplete - * filename. - * escaped int If true, backslashes in line[] are - * interpreted as escaping the characters - * that follow them, and any spaces, tabs, - * backslashes, or wildcard characters in the - * returned suffixes will be similarly escaped. - * If false, backslashes will be interpreted as - * literal parts of the file name, and no - * backslashes will be added to the returned - * suffixes. - * check_fn CplCheckFn * If not zero, this argument specifies a - * function to call to ask whether a given - * file should be included in the list - * of completions. - * check_data void * Anonymous data to be passed to check_fn(). - * Output: - * return int 0 - OK. - * 1 - Error. A description of the error can be - * acquired by calling _cf_last_error(cf). - */ -int _cf_complete_file(WordCompletion *cpl, CompleteFile *cf, - const char *line, int word_start, int word_end, - int escaped, CplCheckFn *check_fn, void *check_data) -{ - const char *lptr; /* A pointer into line[] */ - int nleft; /* The number of characters still to be processed */ - /* in line[]. */ -/* - * Check the arguments. - */ - if(!cpl || !cf || !line || word_end < word_start) { - if(cf) - strcpy(cf->errmsg, "_cf_complete_file: Invalid arguments"); - return 1; - }; -/* - * Clear the buffer in which the filename will be constructed. - */ - _pn_clear_path(cf->path); -/* - * How many characters are to be processed? - */ - nleft = word_end - word_start; -/* - * Get a pointer to the start of the incomplete filename. - */ - lptr = line + word_start; -/* - * If the first character is a tilde, then perform home-directory - * interpolation. - */ - if(nleft > 0 && *lptr == '~') { - int slen; - if(!cf_read_name(cf, "User", ++lptr, --nleft, cf->usrnam, USR_LEN)) - return 1; -/* - * Advance over the username in the input line. - */ - slen = strlen(cf->usrnam); - lptr += slen; - nleft -= slen; -/* - * If we haven't hit the end of the input string then we have a complete - * username to translate to the corresponding home directory. - */ - if(nleft > 0) { - if(cf_expand_home_dir(cf, cf->usrnam)) - return 1; -/* - * ~user and ~ are usually followed by a directory separator to - * separate them from the file contained in the home directory. - * If the home directory is the root directory, then we don't want - * to follow the home directory by a directory separator, so we should - * skip over it so that it doesn't get copied into the filename. - */ - if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && - strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { - lptr += FS_DIR_SEP_LEN; - nleft -= FS_DIR_SEP_LEN; - }; -/* - * If we have reached the end of the input string, then the username - * may be incomplete, and we should attempt to complete it. - */ - } else { -/* - * Look up the possible completions of the username. - */ - return cf_complete_username(cf, cpl, cf->usrnam, line, word_start+1, - word_end, escaped); - }; - }; -/* - * Copy the rest of the path, stopping to expand $envvar expressions - * where encountered. - */ - while(nleft > 0) { - int seglen; /* The length of the next segment to be copied */ -/* - * Find the length of the next segment to be copied, stopping if an - * unescaped '$' is seen, or the end of the path is reached. - */ - for(seglen=0; seglen < nleft; seglen++) { - int c = lptr[seglen]; - if(escaped && c == '\\') - seglen++; - else if(c == '$') - break; -/* - * We will be completing the last component of the file name, - * so whenever a directory separator is seen, assume that it - * might be the start of the last component, and mark the character - * that follows it as the start of the name that is to be completed. - */ - if(nleft >= FS_DIR_SEP_LEN && - strncmp(lptr + seglen, FS_DIR_SEP, FS_DIR_SEP_LEN)==0) { - word_start = (lptr + seglen) - line + FS_DIR_SEP_LEN; - }; - }; -/* - * We have reached either the end of the filename or the start of - * $environment_variable expression. Record the newly checked - * segment of the filename in the output filename, removing - * backslash-escapes where needed. - */ - if(_pn_append_to_path(cf->path, lptr, seglen, escaped) == NULL) { - strcpy(cf->errmsg, "Insufficient memory to complete filename"); - return 1; - }; - lptr += seglen; - nleft -= seglen; -/* - * If the above loop finished before we hit the end of the filename, - * then this was because an unescaped $ was seen. In this case, interpolate - * the value of the environment variable that follows it into the output - * filename. - */ - if(nleft > 0) { - char *value; /* The value of the environment variable */ - int vlen; /* The length of the value string */ - int nlen; /* The length of the environment variable name */ -/* - * Read the name of the environment variable. - */ - if(!cf_read_name(cf, "Environment", ++lptr, --nleft, cf->envnam, ENV_LEN)) - return 1; -/* - * Advance over the environment variable name in the input line. - */ - nlen = strlen(cf->envnam); - lptr += nlen; - nleft -= nlen; -/* - * Get the value of the environment variable. - */ - value = getenv(cf->envnam); - if(!value) { - const char *fmt = "Unknown environment variable: %.*s"; - sprintf(cf->errmsg, fmt, ERRLEN - strlen(fmt), cf->envnam); - return 1; - }; - vlen = strlen(value); -/* - * If we are at the start of the filename and the first character of the - * environment variable value is a '~', attempt home-directory - * interpolation. - */ - if(cf->path->name[0] == '\0' && value[0] == '~') { - if(!cf_read_name(cf, "User", value+1, vlen-1, cf->usrnam, USR_LEN) || - cf_expand_home_dir(cf, cf->usrnam)) - return 1; -/* - * If the home directory is the root directory, and the ~usrname expression - * was followed by a directory separator, prevent the directory separator - * from being appended to the root directory by skipping it in the - * input line. - */ - if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && - strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { - lptr += FS_DIR_SEP_LEN; - nleft -= FS_DIR_SEP_LEN; - }; - } else { -/* - * Append the value of the environment variable to the output path. - */ - if(_pn_append_to_path(cf->path, value, strlen(value), escaped)==NULL) { - strcpy(cf->errmsg, "Insufficient memory to complete filename"); - return 1; - }; -/* - * Prevent extra directory separators from being added. - */ - if(nleft >= FS_DIR_SEP_LEN && - strcmp(cf->path->name, FS_ROOT_DIR) == 0 && - strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { - lptr += FS_DIR_SEP_LEN; - nleft -= FS_DIR_SEP_LEN; - } else if(vlen > FS_DIR_SEP_LEN && - strcmp(value + vlen - FS_DIR_SEP_LEN, FS_DIR_SEP)==0) { - cf->path->name[vlen-FS_DIR_SEP_LEN] = '\0'; - }; - }; -/* - * If adding the environment variable didn't form a valid directory, - * we can't complete the line, since there is no way to separate append - * a partial filename to an environment variable reference without - * that appended part of the name being seen later as part of the - * environment variable name. Thus if the currently constructed path - * isn't a directory, quite now with no completions having been - * registered. - */ - if(!_pu_path_is_dir(cf->path->name)) - return 0; -/* - * For the reasons given above, if we have reached the end of the filename - * with the expansion of an environment variable, the only allowed - * completion involves the addition of a directory separator. - */ - if(nleft == 0) { - if(cpl_add_completion(cpl, line, lptr-line, word_end, FS_DIR_SEP, - "", "")) { - strncpy(cf->errmsg, cpl_last_error(cpl), ERRLEN); - cf->errmsg[ERRLEN] = '\0'; - return 1; - }; - return 0; - }; - }; - }; -/* - * Complete the filename if possible. - */ - return cf_complete_entry(cf, cpl, line, word_start, word_end, escaped, - check_fn, check_data); -} - -/*....................................................................... - * Return a description of the last path-completion error that occurred. - * - * Input: - * cf CompleteFile * The path-completion resource object. - * Output: - * return const char * The description of the last error. - */ -const char *_cf_last_error(CompleteFile *cf) -{ - return cf ? cf->errmsg : "NULL CompleteFile argument"; -} - -/*....................................................................... - * Lookup the home directory of the specified user, or the current user - * if no name is specified, appending it to output pathname. - * - * Input: - * cf CompleteFile * The pathname completion resource object. - * user const char * The username to lookup, or "" to lookup the - * current user. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int cf_expand_home_dir(CompleteFile *cf, const char *user) -{ -/* - * Attempt to lookup the home directory. - */ - const char *home_dir = _hd_lookup_home_dir(cf->home, user); -/* - * Failed? - */ - if(!home_dir) { - strncpy(cf->errmsg, _hd_last_home_dir_error(cf->home), ERRLEN); - cf->errmsg[ERRLEN] = '\0'; - return 1; - }; -/* - * Append the home directory to the pathname string. - */ - if(_pn_append_to_path(cf->path, home_dir, -1, 0) == NULL) { - strcpy(cf->errmsg, "Insufficient memory for home directory expansion"); - return 1; - }; - return 0; -} - -/*....................................................................... - * Lookup and report all completions of a given username prefix. - * - * Input: - * cf CompleteFile * The filename-completion resource object. - * cpl WordCompletion * The object in which to record the completions. - * prefix const char * The prefix of the usernames to lookup. - * line const char * The command-line in which the username appears. - * word_start int The index within line[] of the start of the - * username that is being completed. - * word_end int The index within line[] of the character which - * follows the incomplete username. - * escaped int True if the completions need to have special - * characters escaped. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, - const char *prefix, const char *line, - int word_start, int word_end, int escaped) -{ -/* - * Set up a container of anonymous arguments to be sent to the - * username-lookup iterator. - */ - CfHomeArgs args; - args.cf = cf; - args.cpl = cpl; - args.prefix = prefix; - args.line = line; - args.word_start = word_start; - args.word_end = word_end; - args.escaped = escaped; -/* - * Iterate through the list of users, recording those which start - * with the specified prefix. - */ - if(_hd_scan_user_home_dirs(cf->home, &args, cf_homedir_callback)) { - strncpy(cf->errmsg, _hd_last_home_dir_error(cf->home), ERRLEN); - cf->errmsg[ERRLEN] = '\0'; - return 1; - }; - return 0; -} - -/*....................................................................... - * The user/home-directory scanner callback function (see homedir.h) - * used by cf_complete_username(). - */ -static HOME_DIR_FN(cf_homedir_callback) -{ -/* - * Get the file-completion resources from the anonymous data argument. - */ - CfHomeArgs *args = (CfHomeArgs *) data; - WordCompletion *cpl = args->cpl; - CompleteFile *cf = args->cf; -/* - * Get the length of the username prefix. - */ - int prefix_len = strlen(args->prefix); -/* - * Get the length of the latest user name that is to be compared to - * the prefix. - */ - int name_len = strlen(usrnam); -/* - * See if the latest username starts with the prefix that we are - * searching for, and record its suffix in the array of matches if so. - */ - if(name_len >= prefix_len && strncmp(args->prefix, usrnam, prefix_len)==0) { -/* - * Copy the username into the pathname work buffer, adding backslash - * escapes where needed. - */ - if(cf_prepare_suffix(cf, usrnam+prefix_len, args->escaped)) { - strncpy(errmsg, cf->errmsg, maxerr); - errmsg[maxerr] = '\0'; - return 1; - }; -/* - * Report the completion suffix that was copied above. - */ - if(cpl_add_completion(cpl, args->line, args->word_start, args->word_end, - cf->buff->name, FS_DIR_SEP, FS_DIR_SEP)) { - strncpy(errmsg, cpl_last_error(cpl), maxerr); - errmsg[maxerr] = '\0'; - return 1; - }; - }; - return 0; -} - -/*....................................................................... - * Report possible completions of the filename in cf->path->name[]. - * - * Input: - * cf CompleteFile * The file-completion resource object. - * cpl WordCompletion * The object in which to record the completions. - * line const char * The input line, as received by the callback - * function. - * word_start int The index within line[] of the start of the - * last component of the filename that is being - * completed. - * word_end int The index within line[] of the character which - * follows the incomplete filename. - * escaped int If true, escape special characters in the - * completion suffixes. - * check_fn CplCheckFn * If not zero, this argument specifies a - * function to call to ask whether a given - * file should be included in the list - * of completions. - * check_data void * Anonymous data to be passed to check_fn(). - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, - const char *line, int word_start, int word_end, - int escaped, CplCheckFn *check_fn, - void *check_data) -{ - const char *dirpath; /* The name of the parent directory */ - int start; /* The index of the start of the last filename */ - /* component in the transcribed filename. */ - const char *prefix; /* The filename prefix to be completed */ - int prefix_len; /* The length of the filename prefix */ - const char *file_name; /* The lastest filename being compared */ - int waserr = 0; /* True after errors */ - int terminated=0; /* True if the directory part had to be terminated */ -/* - * Get the pathname string and its current length. - */ - char *pathname = cf->path->name; - int pathlen = strlen(pathname); -/* - * Locate the start of the final component of the pathname. - */ - for(start=pathlen - 1; start >= 0 && - strncmp(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; start--) - ; -/* - * Is the parent directory the root directory? - */ - if(start==0 || - (start < 0 && strncmp(pathname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0)) { - dirpath = FS_ROOT_DIR; - start += FS_ROOT_DIR_LEN; -/* - * If we found a directory separator then the part which precedes the - * last component is the name of the directory to be opened. - */ - } else if(start > 0) { -/* - * The _dr_open_dir() function requires the directory name to be '\0' - * terminated, so temporarily do this by overwriting the first character - * of the directory separator. - */ - pathname[start] = '\0'; - dirpath = pathname; - terminated = 1; -/* - * We reached the start of the pathname before finding a directory - * separator, so arrange to open the current working directory. - */ - } else { - start = 0; - dirpath = FS_PWD; - }; -/* - * Attempt to open the directory. - */ - if(_dr_open_dir(cf->dr, dirpath, NULL)) { - const char *fmt = "Can't open directory: %.*s"; - sprintf(cf->errmsg, fmt, ERRLEN - strlen(fmt), dirpath); - return 1; - }; -/* - * If removed above, restore the directory separator and skip over it - * to the start of the filename. - */ - if(terminated) { - memcpy(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN); - start += FS_DIR_SEP_LEN; - }; -/* - * Get the filename prefix and its length. - */ - prefix = pathname + start; - prefix_len = strlen(prefix); -/* - * Traverse the directory, looking for files who's prefixes match the - * last component of the pathname. - */ - while((file_name = _dr_next_file(cf->dr)) != NULL && !waserr) { - int name_len = strlen(file_name); -/* - * Is the latest filename a possible completion of the filename prefix? - */ - if(name_len >= prefix_len && strncmp(prefix, file_name, prefix_len)==0) { -/* - * When listing all files in a directory, don't list files that start - * with '.'. This is how hidden files are denoted in UNIX. - */ - if(prefix_len > 0 || file_name[0] != '.') { -/* - * Copy the completion suffix into the work pathname cf->buff->name, - * adding backslash escapes if needed. - */ - if(cf_prepare_suffix(cf, file_name + prefix_len, escaped)) { - waserr = 1; - } else { -/* - * We want directories to be displayed with directory suffixes, - * and other fully completed filenames to be followed by spaces. - * To check the type of the file, append the current suffix - * to the path being completed, check the filetype, then restore - * the path to its original form. - */ - const char *cont_suffix = ""; /* The suffix to add if fully */ - /* completed. */ - const char *type_suffix = ""; /* The suffix to add when listing */ - if(_pn_append_to_path(cf->path, file_name + prefix_len, - -1, escaped) == NULL) { - strcpy(cf->errmsg, "Insufficient memory to complete filename."); - return 1; - }; -/* - * Specify suffixes according to the file type. - */ - if(_pu_path_is_dir(cf->path->name)) { - cont_suffix = FS_DIR_SEP; - type_suffix = FS_DIR_SEP; - } else if(!check_fn || check_fn(check_data, cf->path->name)) { - cont_suffix = " "; - } else { - cf->path->name[pathlen] = '\0'; - continue; - }; -/* - * Remove the temporarily added suffix. - */ - cf->path->name[pathlen] = '\0'; -/* - * Record the latest completion. - */ - if(cpl_add_completion(cpl, line, word_start, word_end, cf->buff->name, - type_suffix, cont_suffix)) - waserr = 1; - }; - }; - }; - }; -/* - * Close the directory. - */ - _dr_close_dir(cf->dr); - return waserr; -} - -/*....................................................................... - * Read a username or environment variable name, stopping when a directory - * separator is seen, when the end of the string is reached, or the - * output buffer overflows. - * - * Input: - * cf CompleteFile * The file-completion resource object. - * type char * The capitalized name of the type of name being read. - * string char * The string who's prefix contains the name. - * slen int The number of characters in string[]. - * nambuf char * The output name buffer. - * nammax int The longest string that will fit in nambuf[], excluding - * the '\0' terminator. - * Output: - * return char * A pointer to nambuf on success. On error NULL is - * returned and a description of the error is recorded - * in cf->errmsg[]. - */ -static char *cf_read_name(CompleteFile *cf, const char *type, - const char *string, int slen, - char *nambuf, int nammax) -{ - int namlen; /* The number of characters in nambuf[] */ - const char *sptr; /* A pointer into string[] */ -/* - * Work out the max number of characters that should be copied. - */ - int nmax = nammax < slen ? nammax : slen; -/* - * Get the environment variable name that follows the dollar. - */ - for(sptr=string,namlen=0; - namlen < nmax && (slen-namlen < FS_DIR_SEP_LEN || - strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0); - namlen++) { - nambuf[namlen] = *sptr++; - }; -/* - * Did the name overflow the buffer? - */ - if(namlen >= nammax) { - const char *fmt = "%.*s name too long"; - sprintf(cf->errmsg, fmt, ERRLEN - strlen(fmt), type); - return NULL; - }; -/* - * Terminate the string. - */ - nambuf[namlen] = '\0'; - return nambuf; -} - -/*....................................................................... - * Using the work buffer cf->buff, make a suitably escaped copy of a - * given completion suffix, ready to be passed to cpl_add_completion(). - * - * Input: - * cf CompleteFile * The file-completion resource object. - * suffix char * The suffix to be copied. - * add_escapes int If true, escape special characters. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, - int add_escapes) -{ - const char *sptr; /* A pointer into suffix[] */ - int nbsl; /* The number of backslashes to add to the suffix */ - int i; -/* - * How long is the suffix? - */ - int suffix_len = strlen(suffix); -/* - * Clear the work buffer. - */ - _pn_clear_path(cf->buff); -/* - * Count the number of backslashes that will have to be added to - * escape spaces, tabs, backslashes and wildcard characters. - */ - nbsl = 0; - if(add_escapes) { - for(sptr = suffix; *sptr; sptr++) { - switch(*sptr) { - case ' ': case '\t': case '\\': case '*': case '?': case '[': - nbsl++; - break; - }; - }; - }; -/* - * Arrange for the output path buffer to have sufficient room for the - * both the suffix and any backslashes that have to be inserted. - */ - if(_pn_resize_path(cf->buff, suffix_len + nbsl) == NULL) { - strcpy(cf->errmsg, "Insufficient memory to complete filename"); - return 1; - }; -/* - * If the suffix doesn't need any escapes, copy it directly into the - * work buffer. - */ - if(nbsl==0) { - strcpy(cf->buff->name, suffix); - } else { -/* - * Make a copy with special characters escaped? - */ - if(nbsl > 0) { - const char *src = suffix; - char *dst = cf->buff->name; - for(i=0; i<suffix_len; i++) { - switch(*src) { - case ' ': case '\t': case '\\': case '*': case '?': case '[': - *dst++ = '\\'; - }; - *dst++ = *src++; - }; - *dst = '\0'; - }; - }; - return 0; -} |