2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, and/or sell copies of the Software, and to permit persons
11 * to whom the Software is furnished to do so, provided that the above
12 * copyright notice(s) and this permission notice appear in all copies of
13 * the Software and that both the above copyright notice(s) and this
14 * permission notice appear in supporting documentation.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 * Except as contained in this notice, the name of a copyright holder
27 * shall not be used in advertising or otherwise to promote the sale, use
28 * or other dealings in this Software without prior written authorization
29 * of the copyright holder.
33 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
34 * Use is subject to license terms.
37 #pragma ident "%Z%%M% %I% %E% SMI"
40 * If file-system access is to be excluded, this module has no function,
41 * so all of its code should be excluded.
43 #ifndef WITHOUT_FILE_SYSTEM
66 * Set the maximum length allowed for usernames.
72 * Set the maximum length allowed for environment variable names.
77 * The resources needed to complete a filename are maintained in objects
78 * of the following type.
81 ErrMsg
*err
; /* The error reporting buffer */
82 DirReader
*dr
; /* A directory reader */
83 HomeDir
*home
; /* A home directory expander */
84 PathName
*path
; /* The buffer in which to accumulate the path */
85 PathName
*buff
; /* A pathname work buffer */
86 char usrnam
[USR_LEN
+1]; /* The buffer used when reading the names of */
88 char envnam
[ENV_LEN
+1]; /* The buffer used when reading the names of */
89 /* environment variables. */
92 static int cf_expand_home_dir(CompleteFile
*cf
, const char *user
);
93 static int cf_complete_username(CompleteFile
*cf
, WordCompletion
*cpl
,
94 const char *prefix
, const char *line
,
95 int word_start
, int word_end
, int escaped
);
96 static HOME_DIR_FN(cf_homedir_callback
);
97 static int cf_complete_entry(CompleteFile
*cf
, WordCompletion
*cpl
,
98 const char *line
, int word_start
, int word_end
,
99 int escaped
, CplCheckFn
*check_fn
,
101 static char *cf_read_name(CompleteFile
*cf
, const char *type
,
102 const char *string
, int slen
,
103 char *nambuf
, int nammax
);
104 static int cf_prepare_suffix(CompleteFile
*cf
, const char *suffix
,
108 * A stack based object of the following type is used to pass data to the
109 * cf_homedir_callback() function.
112 CompleteFile
*cf
; /* The file-completion resource object */
113 WordCompletion
*cpl
; /* The string-completion rsource object */
114 size_t prefix_len
; /* The length of the prefix being completed */
115 const char *line
; /* The line from which the prefix was extracted */
116 int word_start
; /* The index in line[] of the start of the username */
117 int word_end
; /* The index in line[] following the end of the prefix */
118 int escaped
; /* If true, add escapes to the completion suffixes */
121 /*.......................................................................
122 * Create a new file-completion object.
125 * return CompleteFile * The new object, or NULL on error.
127 CompleteFile
*_new_CompleteFile(void)
129 CompleteFile
*cf
; /* The object to be returned */
131 * Allocate the container.
133 cf
= (CompleteFile
*) malloc(sizeof(CompleteFile
));
139 * Before attempting any operation that might fail, initialize the
140 * container at least up to the point at which it can safely be passed
141 * to _del_CompleteFile().
148 cf
->usrnam
[0] = '\0';
149 cf
->envnam
[0] = '\0';
151 * Allocate a place to record error messages.
153 cf
->err
= _new_ErrMsg();
155 return _del_CompleteFile(cf
);
157 * Create the object that is used for reading directories.
159 cf
->dr
= _new_DirReader();
161 return _del_CompleteFile(cf
);
163 * Create the object that is used to lookup home directories.
165 cf
->home
= _new_HomeDir();
167 return _del_CompleteFile(cf
);
169 * Create the buffer in which the completed pathname is accumulated.
171 cf
->path
= _new_PathName();
173 return _del_CompleteFile(cf
);
175 * Create a pathname work buffer.
177 cf
->buff
= _new_PathName();
179 return _del_CompleteFile(cf
);
183 /*.......................................................................
184 * Delete a file-completion object.
187 * cf CompleteFile * The object to be deleted.
189 * return CompleteFile * The deleted object (always NULL).
191 CompleteFile
*_del_CompleteFile(CompleteFile
*cf
)
194 cf
->err
= _del_ErrMsg(cf
->err
);
195 cf
->dr
= _del_DirReader(cf
->dr
);
196 cf
->home
= _del_HomeDir(cf
->home
);
197 cf
->path
= _del_PathName(cf
->path
);
198 cf
->buff
= _del_PathName(cf
->buff
);
204 /*.......................................................................
205 * Look up the possible completions of the incomplete filename that
206 * lies between specified indexes of a given command-line string.
209 * cpl WordCompletion * The object in which to record the completions.
210 * cf CompleteFile * The filename-completion resource object.
211 * line const char * The string containing the incomplete filename.
212 * word_start int The index of the first character in line[]
213 * of the incomplete filename.
214 * word_end int The index of the character in line[] that
215 * follows the last character of the incomplete
217 * escaped int If true, backslashes in line[] are
218 * interpreted as escaping the characters
219 * that follow them, and any spaces, tabs,
220 * backslashes, or wildcard characters in the
221 * returned suffixes will be similarly escaped.
222 * If false, backslashes will be interpreted as
223 * literal parts of the file name, and no
224 * backslashes will be added to the returned
226 * check_fn CplCheckFn * If not zero, this argument specifies a
227 * function to call to ask whether a given
228 * file should be included in the list
230 * check_data void * Anonymous data to be passed to check_fn().
233 * 1 - Error. A description of the error can be
234 * acquired by calling _cf_last_error(cf).
236 int _cf_complete_file(WordCompletion
*cpl
, CompleteFile
*cf
,
237 const char *line
, int word_start
, int word_end
,
238 int escaped
, CplCheckFn
*check_fn
, void *check_data
)
240 const char *lptr
; /* A pointer into line[] */
241 int nleft
; /* The number of characters still to be processed */
244 * Check the arguments.
246 if(!cpl
|| !cf
|| !line
|| word_end
< word_start
) {
248 _err_record_msg(cf
->err
, "_cf_complete_file: Invalid arguments",
254 * Clear the buffer in which the filename will be constructed.
256 _pn_clear_path(cf
->path
);
258 * How many characters are to be processed?
260 nleft
= word_end
- word_start
;
262 * Get a pointer to the start of the incomplete filename.
264 lptr
= line
+ word_start
;
266 * If the first character is a tilde, then perform home-directory
269 if(nleft
> 0 && *lptr
== '~') {
271 if(!cf_read_name(cf
, "User", ++lptr
, --nleft
, cf
->usrnam
, USR_LEN
))
274 * Advance over the username in the input line.
276 slen
= strlen(cf
->usrnam
);
280 * If we haven't hit the end of the input string then we have a complete
281 * username to translate to the corresponding home directory.
284 if(cf_expand_home_dir(cf
, cf
->usrnam
))
287 * ~user and ~ are usually followed by a directory separator to
288 * separate them from the file contained in the home directory.
289 * If the home directory is the root directory, then we don't want
290 * to follow the home directory by a directory separator, so we should
291 * skip over it so that it doesn't get copied into the filename.
293 if(strcmp(cf
->path
->name
, FS_ROOT_DIR
) == 0 &&
294 strncmp(lptr
, FS_DIR_SEP
, FS_DIR_SEP_LEN
) == 0) {
295 lptr
+= FS_DIR_SEP_LEN
;
296 nleft
-= FS_DIR_SEP_LEN
;
299 * If we have reached the end of the input string, then the username
300 * may be incomplete, and we should attempt to complete it.
304 * Look up the possible completions of the username.
306 return cf_complete_username(cf
, cpl
, cf
->usrnam
, line
, word_start
+1,
311 * Copy the rest of the path, stopping to expand $envvar expressions
315 int seglen
; /* The length of the next segment to be copied */
317 * Find the length of the next segment to be copied, stopping if an
318 * unescaped '$' is seen, or the end of the path is reached.
320 for(seglen
=0; seglen
< nleft
; seglen
++) {
321 int c
= lptr
[seglen
];
322 if(escaped
&& c
== '\\')
327 * We will be completing the last component of the file name,
328 * so whenever a directory separator is seen, assume that it
329 * might be the start of the last component, and mark the character
330 * that follows it as the start of the name that is to be completed.
332 if(nleft
>= FS_DIR_SEP_LEN
&&
333 strncmp(lptr
+ seglen
, FS_DIR_SEP
, FS_DIR_SEP_LEN
)==0) {
334 word_start
= (lptr
+ seglen
) - line
+ FS_DIR_SEP_LEN
;
338 * We have reached either the end of the filename or the start of
339 * $environment_variable expression. Record the newly checked
340 * segment of the filename in the output filename, removing
341 * backslash-escapes where needed.
343 if(_pn_append_to_path(cf
->path
, lptr
, seglen
, escaped
) == NULL
) {
344 _err_record_msg(cf
->err
, "Insufficient memory to complete filename",
351 * If the above loop finished before we hit the end of the filename,
352 * then this was because an unescaped $ was seen. In this case, interpolate
353 * the value of the environment variable that follows it into the output
357 char *value
; /* The value of the environment variable */
358 int vlen
; /* The length of the value string */
359 int nlen
; /* The length of the environment variable name */
361 * Read the name of the environment variable.
363 if(!cf_read_name(cf
, "Environment", ++lptr
, --nleft
, cf
->envnam
, ENV_LEN
))
366 * Advance over the environment variable name in the input line.
368 nlen
= strlen(cf
->envnam
);
372 * Get the value of the environment variable.
374 value
= getenv(cf
->envnam
);
376 _err_record_msg(cf
->err
, "Unknown environment variable: ", cf
->envnam
,
380 vlen
= strlen(value
);
382 * If we are at the start of the filename and the first character of the
383 * environment variable value is a '~', attempt home-directory
386 if(cf
->path
->name
[0] == '\0' && value
[0] == '~') {
387 if(!cf_read_name(cf
, "User", value
+1, vlen
-1, cf
->usrnam
, USR_LEN
) ||
388 cf_expand_home_dir(cf
, cf
->usrnam
))
391 * If the home directory is the root directory, and the ~usrname expression
392 * was followed by a directory separator, prevent the directory separator
393 * from being appended to the root directory by skipping it in the
396 if(strcmp(cf
->path
->name
, FS_ROOT_DIR
) == 0 &&
397 strncmp(lptr
, FS_DIR_SEP
, FS_DIR_SEP_LEN
) == 0) {
398 lptr
+= FS_DIR_SEP_LEN
;
399 nleft
-= FS_DIR_SEP_LEN
;
403 * Append the value of the environment variable to the output path.
405 if(_pn_append_to_path(cf
->path
, value
, strlen(value
), escaped
)==NULL
) {
406 _err_record_msg(cf
->err
, "Insufficient memory to complete filename",
411 * Prevent extra directory separators from being added.
413 if(nleft
>= FS_DIR_SEP_LEN
&&
414 strcmp(cf
->path
->name
, FS_ROOT_DIR
) == 0 &&
415 strncmp(lptr
, FS_DIR_SEP
, FS_DIR_SEP_LEN
) == 0) {
416 lptr
+= FS_DIR_SEP_LEN
;
417 nleft
-= FS_DIR_SEP_LEN
;
418 } else if(vlen
> FS_DIR_SEP_LEN
&&
419 strcmp(value
+ vlen
- FS_DIR_SEP_LEN
, FS_DIR_SEP
)==0) {
420 cf
->path
->name
[vlen
-FS_DIR_SEP_LEN
] = '\0';
424 * If adding the environment variable didn't form a valid directory,
425 * we can't complete the line, since there is no way to separate append
426 * a partial filename to an environment variable reference without
427 * that appended part of the name being seen later as part of the
428 * environment variable name. Thus if the currently constructed path
429 * isn't a directory, quite now with no completions having been
432 if(!_pu_path_is_dir(cf
->path
->name
))
435 * For the reasons given above, if we have reached the end of the filename
436 * with the expansion of an environment variable, the only allowed
437 * completion involves the addition of a directory separator.
440 if(cpl_add_completion(cpl
, line
, lptr
-line
, word_end
, FS_DIR_SEP
,
442 _err_record_msg(cf
->err
, cpl_last_error(cpl
), END_ERR_MSG
);
450 * Complete the filename if possible.
452 return cf_complete_entry(cf
, cpl
, line
, word_start
, word_end
, escaped
,
453 check_fn
, check_data
);
456 /*.......................................................................
457 * Return a description of the last path-completion error that occurred.
460 * cf CompleteFile * The path-completion resource object.
462 * return const char * The description of the last error.
464 const char *_cf_last_error(CompleteFile
*cf
)
466 return cf
? _err_get_msg(cf
->err
) : "NULL CompleteFile argument";
469 /*.......................................................................
470 * Lookup the home directory of the specified user, or the current user
471 * if no name is specified, appending it to output pathname.
474 * cf CompleteFile * The pathname completion resource object.
475 * user const char * The username to lookup, or "" to lookup the
481 static int cf_expand_home_dir(CompleteFile
*cf
, const char *user
)
484 * Attempt to lookup the home directory.
486 const char *home_dir
= _hd_lookup_home_dir(cf
->home
, user
);
491 _err_record_msg(cf
->err
, _hd_last_home_dir_error(cf
->home
), END_ERR_MSG
);
495 * Append the home directory to the pathname string.
497 if(_pn_append_to_path(cf
->path
, home_dir
, -1, 0) == NULL
) {
498 _err_record_msg(cf
->err
, "Insufficient memory for home directory expansion",
505 /*.......................................................................
506 * Lookup and report all completions of a given username prefix.
509 * cf CompleteFile * The filename-completion resource object.
510 * cpl WordCompletion * The object in which to record the completions.
511 * prefix const char * The prefix of the usernames to lookup.
512 * line const char * The command-line in which the username appears.
513 * word_start int The index within line[] of the start of the
514 * username that is being completed.
515 * word_end int The index within line[] of the character which
516 * follows the incomplete username.
517 * escaped int True if the completions need to have special
518 * characters escaped.
523 static int cf_complete_username(CompleteFile
*cf
, WordCompletion
*cpl
,
524 const char *prefix
, const char *line
,
525 int word_start
, int word_end
, int escaped
)
528 * Set up a container of anonymous arguments to be sent to the
529 * username-lookup iterator.
534 args
.prefix_len
= strlen(prefix
);
536 args
.word_start
= word_start
;
537 args
.word_end
= word_end
;
538 args
.escaped
= escaped
;
540 * Iterate through the list of users, recording those which start
541 * with the specified prefix.
543 if(_hd_scan_user_home_dirs(cf
->home
, prefix
, &args
, cf_homedir_callback
)) {
544 _err_record_msg(cf
->err
, _hd_last_home_dir_error(cf
->home
), END_ERR_MSG
);
550 /*.......................................................................
551 * The user/home-directory scanner callback function (see homedir.h)
552 * used by cf_complete_username().
554 static HOME_DIR_FN(cf_homedir_callback
)
557 * Get the file-completion resources from the anonymous data argument.
559 CfHomeArgs
*args
= (CfHomeArgs
*) data
;
560 WordCompletion
*cpl
= args
->cpl
;
561 CompleteFile
*cf
= args
->cf
;
563 * Copy the username into the pathname work buffer, adding backslash
564 * escapes where needed.
566 if(cf_prepare_suffix(cf
, usrnam
+args
->prefix_len
, args
->escaped
)) {
567 strncpy(errmsg
, _err_get_msg(cf
->err
), maxerr
);
568 errmsg
[maxerr
] = '\0';
572 * Report the completion suffix that was copied above.
574 if(cpl_add_completion(cpl
, args
->line
, args
->word_start
, args
->word_end
,
575 cf
->buff
->name
, FS_DIR_SEP
, FS_DIR_SEP
)) {
576 strncpy(errmsg
, cpl_last_error(cpl
), maxerr
);
577 errmsg
[maxerr
] = '\0';
583 /*.......................................................................
584 * Report possible completions of the filename in cf->path->name[].
587 * cf CompleteFile * The file-completion resource object.
588 * cpl WordCompletion * The object in which to record the completions.
589 * line const char * The input line, as received by the callback
591 * word_start int The index within line[] of the start of the
592 * last component of the filename that is being
594 * word_end int The index within line[] of the character which
595 * follows the incomplete filename.
596 * escaped int If true, escape special characters in the
597 * completion suffixes.
598 * check_fn CplCheckFn * If not zero, this argument specifies a
599 * function to call to ask whether a given
600 * file should be included in the list
602 * check_data void * Anonymous data to be passed to check_fn().
607 static int cf_complete_entry(CompleteFile
*cf
, WordCompletion
*cpl
,
608 const char *line
, int word_start
, int word_end
,
609 int escaped
, CplCheckFn
*check_fn
,
612 const char *dirpath
; /* The name of the parent directory */
613 int start
; /* The index of the start of the last filename */
614 /* component in the transcribed filename. */
615 const char *prefix
; /* The filename prefix to be completed */
616 int prefix_len
; /* The length of the filename prefix */
617 const char *file_name
; /* The lastest filename being compared */
618 int waserr
= 0; /* True after errors */
619 int terminated
=0; /* True if the directory part had to be terminated */
621 * Get the pathname string and its current length.
623 char *pathname
= cf
->path
->name
;
624 int pathlen
= strlen(pathname
);
626 * Locate the start of the final component of the pathname.
628 for(start
=pathlen
- 1; start
>= 0 &&
629 strncmp(pathname
+ start
, FS_DIR_SEP
, FS_DIR_SEP_LEN
) != 0; start
--)
632 * Is the parent directory the root directory?
635 (start
< 0 && strncmp(pathname
, FS_ROOT_DIR
, FS_ROOT_DIR_LEN
) == 0)) {
636 dirpath
= FS_ROOT_DIR
;
637 start
+= FS_ROOT_DIR_LEN
;
639 * If we found a directory separator then the part which precedes the
640 * last component is the name of the directory to be opened.
642 } else if(start
> 0) {
644 * The _dr_open_dir() function requires the directory name to be '\0'
645 * terminated, so temporarily do this by overwriting the first character
646 * of the directory separator.
648 pathname
[start
] = '\0';
652 * We reached the start of the pathname before finding a directory
653 * separator, so arrange to open the current working directory.
660 * Attempt to open the directory.
662 if(_dr_open_dir(cf
->dr
, dirpath
, NULL
)) {
663 _err_record_msg(cf
->err
, "Can't open directory: ", dirpath
, END_ERR_MSG
);
667 * If removed above, restore the directory separator and skip over it
668 * to the start of the filename.
671 memcpy(pathname
+ start
, FS_DIR_SEP
, FS_DIR_SEP_LEN
);
672 start
+= FS_DIR_SEP_LEN
;
675 * Get the filename prefix and its length.
677 prefix
= pathname
+ start
;
678 prefix_len
= strlen(prefix
);
680 * Traverse the directory, looking for files who's prefixes match the
681 * last component of the pathname.
683 while((file_name
= _dr_next_file(cf
->dr
)) != NULL
&& !waserr
) {
684 int name_len
= strlen(file_name
);
686 * Is the latest filename a possible completion of the filename prefix?
688 if(name_len
>= prefix_len
&& strncmp(prefix
, file_name
, prefix_len
)==0) {
690 * When listing all files in a directory, don't list files that start
691 * with '.'. This is how hidden files are denoted in UNIX.
693 if(prefix_len
> 0 || file_name
[0] != '.') {
695 * Copy the completion suffix into the work pathname cf->buff->name,
696 * adding backslash escapes if needed.
698 if(cf_prepare_suffix(cf
, file_name
+ prefix_len
, escaped
)) {
702 * We want directories to be displayed with directory suffixes,
703 * and other fully completed filenames to be followed by spaces.
704 * To check the type of the file, append the current suffix
705 * to the path being completed, check the filetype, then restore
706 * the path to its original form.
708 const char *cont_suffix
= ""; /* The suffix to add if fully */
710 const char *type_suffix
= ""; /* The suffix to add when listing */
711 if(_pn_append_to_path(cf
->path
, file_name
+ prefix_len
,
712 -1, escaped
) == NULL
) {
713 _err_record_msg(cf
->err
,
714 "Insufficient memory to complete filename.",
719 * Specify suffixes according to the file type.
721 if(_pu_path_is_dir(cf
->path
->name
)) {
722 cont_suffix
= FS_DIR_SEP
;
723 type_suffix
= FS_DIR_SEP
;
724 } else if(!check_fn
|| check_fn(check_data
, cf
->path
->name
)) {
727 cf
->path
->name
[pathlen
] = '\0';
731 * Remove the temporarily added suffix.
733 cf
->path
->name
[pathlen
] = '\0';
735 * Record the latest completion.
737 if(cpl_add_completion(cpl
, line
, word_start
, word_end
, cf
->buff
->name
,
738 type_suffix
, cont_suffix
))
745 * Close the directory.
747 _dr_close_dir(cf
->dr
);
751 /*.......................................................................
752 * Read a username or environment variable name, stopping when a directory
753 * separator is seen, when the end of the string is reached, or the
754 * output buffer overflows.
757 * cf CompleteFile * The file-completion resource object.
758 * type char * The capitalized name of the type of name being read.
759 * string char * The string who's prefix contains the name.
760 * slen int The number of characters in string[].
761 * nambuf char * The output name buffer.
762 * nammax int The longest string that will fit in nambuf[], excluding
763 * the '\0' terminator.
765 * return char * A pointer to nambuf on success. On error NULL is
766 * returned and a description of the error is recorded
769 static char *cf_read_name(CompleteFile
*cf
, const char *type
,
770 const char *string
, int slen
,
771 char *nambuf
, int nammax
)
773 int namlen
; /* The number of characters in nambuf[] */
774 const char *sptr
; /* A pointer into string[] */
776 * Work out the max number of characters that should be copied.
778 int nmax
= nammax
< slen
? nammax
: slen
;
780 * Get the environment variable name that follows the dollar.
782 for(sptr
=string
,namlen
=0;
783 namlen
< nmax
&& (slen
-namlen
< FS_DIR_SEP_LEN
||
784 strncmp(sptr
, FS_DIR_SEP
, FS_DIR_SEP_LEN
) != 0);
786 nambuf
[namlen
] = *sptr
++;
789 * Did the name overflow the buffer?
791 if(namlen
>= nammax
) {
792 _err_record_msg(cf
->err
, type
, " name too long", END_ERR_MSG
);
796 * Terminate the string.
798 nambuf
[namlen
] = '\0';
802 /*.......................................................................
803 * Using the work buffer cf->buff, make a suitably escaped copy of a
804 * given completion suffix, ready to be passed to cpl_add_completion().
807 * cf CompleteFile * The file-completion resource object.
808 * suffix char * The suffix to be copied.
809 * add_escapes int If true, escape special characters.
814 static int cf_prepare_suffix(CompleteFile
*cf
, const char *suffix
,
817 const char *sptr
; /* A pointer into suffix[] */
818 int nbsl
; /* The number of backslashes to add to the suffix */
821 * How long is the suffix?
823 int suffix_len
= strlen(suffix
);
825 * Clear the work buffer.
827 _pn_clear_path(cf
->buff
);
829 * Count the number of backslashes that will have to be added to
830 * escape spaces, tabs, backslashes and wildcard characters.
834 for(sptr
= suffix
; *sptr
; sptr
++) {
836 case ' ': case '\t': case '\\': case '*': case '?': case '[':
843 * Arrange for the output path buffer to have sufficient room for the
844 * both the suffix and any backslashes that have to be inserted.
846 if(_pn_resize_path(cf
->buff
, suffix_len
+ nbsl
) == NULL
) {
847 _err_record_msg(cf
->err
, "Insufficient memory to complete filename",
852 * If the suffix doesn't need any escapes, copy it directly into the
856 strlcpy(cf
->buff
->name
, suffix
, cf
->buff
->dim
);
859 * Make a copy with special characters escaped?
862 const char *src
= suffix
;
863 char *dst
= cf
->buff
->name
;
864 for(i
=0; i
<suffix_len
; i
++) {
866 case ' ': case '\t': case '\\': case '*': case '?': case '[':
877 #endif /* ifndef WITHOUT_FILE_SYSTEM */