dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libtecla / common / cplmatch.c
blob181551e23278d49c8a08a9b6ea2bedf5c5824aca
1 /*
2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3 *
4 * All rights reserved.
5 *
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 * Standard includes.
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <errno.h>
48 * Local includes.
50 #include "libtecla.h"
51 #include "ioutil.h"
52 #include "stringrp.h"
53 #include "pathutil.h"
54 #include "cplfile.h"
55 #include "cplmatch.h"
56 #include "errmsg.h"
59 * Specify the number of strings to allocate when the string free-list
60 * is exhausted. This also sets the number of elements to expand the
61 * matches[] array by whenever it is found to be too small.
63 #define STR_BLK_FACT 100
66 * Set the default number of spaces place between columns when listing
67 * a set of completions.
69 #define CPL_COL_SEP 2
72 * Completion matches are recorded in containers of the following
73 * type.
75 struct WordCompletion {
76 ErrMsg *err; /* The error reporting buffer */
77 StringGroup *sg; /* Memory for a group of strings */
78 int matches_dim; /* The allocated size of result.matches[] */
79 CplMatches result; /* Completions to be returned to the caller */
80 #ifndef WITHOUT_FILE_SYSTEM
81 CompleteFile *cf; /* The resources used for filename completion */
82 #endif
85 static void cpl_sort_matches(WordCompletion *cpl);
86 static void cpl_zap_duplicates(WordCompletion *cpl);
87 static void cpl_clear_completions(WordCompletion *cpl);
88 static int cpl_cmp_matches(const void *v1, const void *v2);
89 static int cpl_cmp_suffixes(const void *v1, const void *v2);
92 * The new_CplFileConf() constructor sets the integer first member of
93 * the returned object to the following magic number. On seeing this,
94 * cpl_file_completions() knows when it is passed a valid CplFileConf
95 * object.
97 #define CFC_ID_CODE 4568
99 #ifndef WITHOUT_FILE_SYSTEM
101 * A pointer to a structure of the following type can be passed to
102 * the builtin file-completion callback function to modify its behavior.
104 struct CplFileConf {
105 int id; /* new_CplFileConf() sets this to CFC_ID_CODE */
106 int escaped; /* If none-zero, backslashes in the input line are */
107 /* interpreted as escaping special characters and */
108 /* spaces, and any special characters and spaces in */
109 /* the listed completions will also be escaped with */
110 /* added backslashes. This is the default behaviour. */
111 /* If zero, backslashes are interpreted as being */
112 /* literal parts of the filename, and none are added */
113 /* to the completion suffixes. */
114 int file_start; /* The index in the input line of the first character */
115 /* of the filename. If you specify -1 here, */
116 /* cpl_file_completions() identifies the */
117 /* the start of the filename by looking backwards for */
118 /* an unescaped space, or the beginning of the line. */
119 CplCheckFn *chk_fn; /* If not zero, this argument specifies a */
120 /* function to call to ask whether a given */
121 /* file should be included in the list */
122 /* of completions. */
123 void *chk_data; /* Anonymous data to be passed to check_fn(). */
126 static void cpl_init_FileConf(CplFileConf *cfc);
129 * When file-system access is being excluded, define a dummy structure
130 * to satisfy the typedef in libtecla.h.
132 #else
133 struct CplFileConf {int dummy;};
134 #endif
137 * Encapsulate the formatting information needed to layout a
138 * multi-column listing of completions.
140 typedef struct {
141 int term_width; /* The width of the terminal (characters) */
142 int column_width; /* The number of characters within in each column. */
143 int ncol; /* The number of columns needed */
144 int nline; /* The number of lines needed */
145 } CplListFormat;
148 * Given the current terminal width, and a list of completions, determine
149 * how to best use the terminal width to display a multi-column listing
150 * of completions.
152 static void cpl_plan_listing(CplMatches *result, int term_width,
153 CplListFormat *fmt);
156 * Display a given line of a multi-column list of completions.
158 static int cpl_format_line(CplMatches *result, CplListFormat *fmt, int lnum,
159 GlWriteFn *write_fn, void *data);
161 /*.......................................................................
162 * Create a new string-completion object.
164 * Output:
165 * return WordCompletion * The new object, or NULL on error.
167 WordCompletion *new_WordCompletion(void)
169 WordCompletion *cpl; /* The object to be returned */
171 * Allocate the container.
173 cpl = (WordCompletion *) malloc(sizeof(WordCompletion));
174 if(!cpl) {
175 errno = ENOMEM;
176 return NULL;
179 * Before attempting any operation that might fail, initialize the
180 * container at least up to the point at which it can safely be passed
181 * to del_WordCompletion().
183 cpl->err = NULL;
184 cpl->sg = NULL;
185 cpl->matches_dim = 0;
186 cpl->result.suffix = NULL;
187 cpl->result.cont_suffix = NULL;
188 cpl->result.matches = NULL;
189 cpl->result.nmatch = 0;
190 #ifndef WITHOUT_FILE_SYSTEM
191 cpl->cf = NULL;
192 #endif
194 * Allocate a place to record error messages.
196 cpl->err = _new_ErrMsg();
197 if(!cpl->err)
198 return del_WordCompletion(cpl);
200 * Allocate an object that allows a group of strings to be allocated
201 * efficiently by placing many of them in contiguous string segments.
203 #ifdef WITHOUT_FILE_SYSTEM
204 cpl->sg = _new_StringGroup(MAX_PATHLEN_FALLBACK);
205 #else
206 cpl->sg = _new_StringGroup(_pu_pathname_dim());
207 #endif
208 if(!cpl->sg)
209 return del_WordCompletion(cpl);
211 * Allocate an array for matching completions. This will be extended later
212 * if needed.
214 cpl->matches_dim = STR_BLK_FACT;
215 cpl->result.matches = (CplMatch *) malloc(sizeof(cpl->result.matches[0]) *
216 cpl->matches_dim);
217 if(!cpl->result.matches) {
218 errno = ENOMEM;
219 return del_WordCompletion(cpl);
222 * Allocate a filename-completion resource object.
224 #ifndef WITHOUT_FILE_SYSTEM
225 cpl->cf = _new_CompleteFile();
226 if(!cpl->cf)
227 return del_WordCompletion(cpl);
228 #endif
229 return cpl;
232 /*.......................................................................
233 * Delete a string-completion object.
235 * Input:
236 * cpl WordCompletion * The object to be deleted.
237 * Output:
238 * return WordCompletion * The deleted object (always NULL).
240 WordCompletion *del_WordCompletion(WordCompletion *cpl)
242 if(cpl) {
243 cpl->err = _del_ErrMsg(cpl->err);
244 cpl->sg = _del_StringGroup(cpl->sg);
245 if(cpl->result.matches) {
246 free(cpl->result.matches);
247 cpl->result.matches = NULL;
248 #ifndef WITHOUT_FILE_SYSTEM
249 cpl->cf = _del_CompleteFile(cpl->cf);
250 #endif
252 free(cpl);
254 return NULL;
257 /*.......................................................................
258 * This function is designed to be called by CplMatchFn callback
259 * functions. It adds one possible completion of the token that is being
260 * completed to an array of completions. If the completion needs any
261 * special quoting to be valid when displayed in the input line, this
262 * quoting must be included in the string.
264 * Input:
265 * cpl WordCompletion * The argument of the same name that was passed
266 * to the calling CplMatchFn callback function.
267 * line const char * The input line, as received by the callback
268 * function.
269 * word_start int The index within line[] of the start of the
270 * word that is being completed.
271 * word_end int The index within line[] of the character which
272 * follows the incomplete word, as received by the
273 * calling callback function.
274 * suffix const char * The appropriately quoted string that could
275 * be appended to the incomplete token to complete
276 * it. A copy of this string will be allocated
277 * internally.
278 * type_suffix const char * When listing multiple completions, gl_get_line()
279 * appends this string to the completion to indicate
280 * its type to the user. If not pertinent pass "".
281 * Otherwise pass a literal or static string.
282 * cont_suffix const char * If this turns out to be the only completion,
283 * gl_get_line() will append this string as
284 * a continuation. For example, the builtin
285 * file-completion callback registers a directory
286 * separator here for directory matches, and a
287 * space otherwise. If the match were a function
288 * name you might want to append an open
289 * parenthesis, etc.. If not relevant pass "".
290 * Otherwise pass a literal or static string.
291 * Output:
292 * return int 0 - OK.
293 * 1 - Error.
295 int cpl_add_completion(WordCompletion *cpl, const char *line,
296 int word_start, int word_end, const char *suffix,
297 const char *type_suffix, const char *cont_suffix)
299 CplMatch *match; /* The container of the new match */
300 char *string; /* A newly allocated copy of the completion string */
301 size_t len;
303 * Check the arguments.
305 if(!cpl)
306 return 1;
307 if(!suffix)
308 return 0;
309 if(!type_suffix)
310 type_suffix = "";
311 if(!cont_suffix)
312 cont_suffix = "";
314 * Do we need to extend the array of matches[]?
316 if(cpl->result.nmatch+1 > cpl->matches_dim) {
317 int needed = cpl->matches_dim + STR_BLK_FACT;
318 CplMatch *matches = (CplMatch *) reallocarray(cpl->result.matches,
319 needed,
320 sizeof(cpl->result.matches[0]));
321 if(!matches) {
322 _err_record_msg(cpl->err,
323 "Insufficient memory to extend array of matches.",
324 END_ERR_MSG);
325 return 1;
327 cpl->result.matches = matches;
328 cpl->matches_dim = needed;
331 * Allocate memory to store the combined completion prefix and the
332 * new suffix.
334 len = strlen(suffix);
335 string = _sg_alloc_string(cpl->sg, word_end-word_start + len);
336 if(!string) {
337 _err_record_msg(cpl->err, "Insufficient memory to extend array of matches.",
338 END_ERR_MSG);
339 return 1;
342 * Compose the string.
344 strncpy(string, line + word_start, word_end - word_start);
345 strlcpy(string + word_end - word_start, suffix, len + 1);
347 * Record the new match.
349 match = cpl->result.matches + cpl->result.nmatch++;
350 match->completion = string;
351 match->suffix = string + word_end - word_start;
352 match->type_suffix = type_suffix;
354 * Record the continuation suffix.
356 cpl->result.cont_suffix = cont_suffix;
357 return 0;
360 /*.......................................................................
361 * Sort the array of matches.
363 * Input:
364 * cpl WordCompletion * The completion resource object.
366 static void cpl_sort_matches(WordCompletion *cpl)
368 qsort(cpl->result.matches, cpl->result.nmatch,
369 sizeof(cpl->result.matches[0]), cpl_cmp_matches);
372 /*.......................................................................
373 * This is a qsort() comparison function used to sort matches.
375 * Input:
376 * v1, v2 void * Pointers to the two matches to be compared.
377 * Output:
378 * return int -1 -> v1 < v2.
379 * 0 -> v1 == v2
380 * 1 -> v1 > v2
382 static int cpl_cmp_matches(const void *v1, const void *v2)
384 const CplMatch *m1 = (const CplMatch *) v1;
385 const CplMatch *m2 = (const CplMatch *) v2;
386 return strcmp(m1->completion, m2->completion);
389 /*.......................................................................
390 * Sort the array of matches in order of their suffixes.
392 * Input:
393 * cpl WordCompletion * The completion resource object.
395 static void cpl_sort_suffixes(WordCompletion *cpl)
397 qsort(cpl->result.matches, cpl->result.nmatch,
398 sizeof(cpl->result.matches[0]), cpl_cmp_suffixes);
401 /*.......................................................................
402 * This is a qsort() comparison function used to sort matches in order of
403 * their suffixes.
405 * Input:
406 * v1, v2 void * Pointers to the two matches to be compared.
407 * Output:
408 * return int -1 -> v1 < v2.
409 * 0 -> v1 == v2
410 * 1 -> v1 > v2
412 static int cpl_cmp_suffixes(const void *v1, const void *v2)
414 const CplMatch *m1 = (const CplMatch *) v1;
415 const CplMatch *m2 = (const CplMatch *) v2;
416 return strcmp(m1->suffix, m2->suffix);
419 /*.......................................................................
420 * Find the common prefix of all of the matching completion matches,
421 * and record a pointer to it in cpl->result.suffix. Note that this has
422 * the side effect of sorting the matches into suffix order.
424 * Input:
425 * cpl WordCompletion * The completion resource object.
426 * Output:
427 * return int 0 - OK.
428 * 1 - Error.
430 static int cpl_common_suffix(WordCompletion *cpl)
432 CplMatches *result; /* The result container */
433 const char *first, *last; /* The first and last matching suffixes */
434 int length; /* The length of the common suffix */
436 * Get the container of the array of matching files.
438 result = &cpl->result;
440 * No matching completions?
442 if(result->nmatch < 1)
443 return 0;
445 * Sort th matches into suffix order.
447 cpl_sort_suffixes(cpl);
449 * Given that the array of matches is sorted, the first and last
450 * suffixes are those that differ most in their prefixes, so the common
451 * prefix of these strings is the longest common prefix of all of the
452 * suffixes.
454 first = result->matches[0].suffix;
455 last = result->matches[result->nmatch - 1].suffix;
457 * Find the point at which the first and last matching strings
458 * first difffer.
460 while(*first && *first == *last) {
461 first++;
462 last++;
465 * How long is the common suffix?
467 length = first - result->matches[0].suffix;
469 * Allocate memory to record the common suffix.
471 result->suffix = _sg_alloc_string(cpl->sg, length);
472 if(!result->suffix) {
473 _err_record_msg(cpl->err,
474 "Insufficient memory to record common completion suffix.",
475 END_ERR_MSG);
476 return 1;
479 * Record the common suffix.
481 strncpy(result->suffix, result->matches[0].suffix, length);
482 result->suffix[length] = '\0';
483 return 0;
486 /*.......................................................................
487 * Discard the contents of the array of possible completion matches.
489 * Input:
490 * cpl WordCompletion * The word-completion resource object.
492 static void cpl_clear_completions(WordCompletion *cpl)
495 * Discard all of the strings.
497 _clr_StringGroup(cpl->sg);
499 * Record the fact that the array is now empty.
501 cpl->result.nmatch = 0;
502 cpl->result.suffix = NULL;
503 cpl->result.cont_suffix = "";
505 * Also clear the error message.
507 _err_clear_msg(cpl->err);
508 return;
511 /*.......................................................................
512 * Given an input line and the point at which it completion is to be
513 * attempted, return an array of possible completions.
515 * Input:
516 * cpl WordCompletion * The completion resource object.
517 * line char * The current input line.
518 * word_end int The index of the character in line[] which
519 * follows the end of the token that is being
520 * completed.
521 * data void * Anonymous 'data' to be passed to match_fn().
522 * match_fn CplMatchFn * The function that will identify the prefix
523 * to be completed from the input line, and
524 * record completion matches.
525 * Output:
526 * return CplMatches * The container of the array of possible
527 * completions. The returned pointer refers
528 * to a container owned by the parent WordCompletion
529 * object, and its contents thus potentially
530 * change on every call to cpl_matches().
531 * On error, NULL is returned, and a description
532 * of the error can be acquired by calling
533 * cpl_last_error(cpl).
535 CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line,
536 int word_end, void *data,
537 CplMatchFn *match_fn)
539 int line_len; /* The total length of the input line */
541 * How long is the input line?
543 line_len = strlen(line);
545 * Check the arguments.
547 if(!cpl || !line || !match_fn || word_end < 0 || word_end > line_len) {
548 if(cpl) {
549 _err_record_msg(cpl->err, "cpl_complete_word: Invalid arguments.",
550 END_ERR_MSG);
552 return NULL;
555 * Clear the return container.
557 cpl_clear_completions(cpl);
559 * Have the matching function record possible completion matches in
560 * cpl->result.matches.
562 if(match_fn(cpl, data, line, word_end)) {
563 if(_err_get_msg(cpl->err)[0] == '\0')
564 _err_record_msg(cpl->err, "Error completing word.", END_ERR_MSG);
565 return NULL;
568 * Record a copy of the common initial part of all of the prefixes
569 * in cpl->result.common.
571 if(cpl_common_suffix(cpl))
572 return NULL;
574 * Sort the matches into lexicographic order.
576 cpl_sort_matches(cpl);
578 * Discard any duplicate matches.
580 cpl_zap_duplicates(cpl);
582 * If there is more than one match, discard the continuation suffix.
584 if(cpl->result.nmatch > 1)
585 cpl->result.cont_suffix = "";
587 * Return the array of matches.
589 return &cpl->result;
592 /*.......................................................................
593 * Recall the return value of the last call to cpl_complete_word().
595 * Input:
596 * cpl WordCompletion * The completion resource object.
597 * Output:
598 * return CplMatches * The container of the array of possible
599 * completions, as returned by the last call to
600 * cpl_complete_word(). The returned pointer refers
601 * to a container owned by the parent WordCompletion
602 * object, and its contents thus potentially
603 * change on every call to cpl_complete_word().
604 * On error, either in the execution of this
605 * function, or in the last call to
606 * cpl_complete_word(), NULL is returned, and a
607 * description of the error can be acquired by
608 * calling cpl_last_error(cpl).
610 CplMatches *cpl_recall_matches(WordCompletion *cpl)
612 return (!cpl || *_err_get_msg(cpl->err)!='\0') ? NULL : &cpl->result;
615 /*.......................................................................
616 * Print out an array of matching completions.
618 * Input:
619 * result CplMatches * The container of the sorted array of
620 * completions.
621 * fp FILE * The output stream to write to.
622 * term_width int The width of the terminal.
623 * Output:
624 * return int 0 - OK.
625 * 1 - Error.
627 int cpl_list_completions(CplMatches *result, FILE *fp, int term_width)
629 return _cpl_output_completions(result, _io_write_stdio, fp, term_width);
632 /*.......................................................................
633 * Print an array of matching completions via a callback function.
635 * Input:
636 * result CplMatches * The container of the sorted array of
637 * completions.
638 * write_fn GlWriteFn * The function to call to write the completions,
639 * or 0 to discard the output.
640 * data void * Anonymous data to pass to write_fn().
641 * term_width int The width of the terminal.
642 * Output:
643 * return int 0 - OK.
644 * 1 - Error.
646 int _cpl_output_completions(CplMatches *result, GlWriteFn *write_fn, void *data,
647 int term_width)
649 CplListFormat fmt; /* List formatting information */
650 int lnum; /* The sequential number of the line to print next */
652 * Not enough space to list anything?
654 if(term_width < 1)
655 return 0;
657 * Do we have a callback to write via, and any completions to be listed?
659 if(write_fn && result && result->nmatch>0) {
661 * Work out how to arrange the listing into fixed sized columns.
663 cpl_plan_listing(result, term_width, &fmt);
665 * Print the listing via the specified callback.
667 for(lnum=0; lnum < fmt.nline; lnum++) {
668 if(cpl_format_line(result, &fmt, lnum, write_fn, data))
669 return 1;
672 return 0;
675 /*.......................................................................
676 * Return a description of the string-completion error that occurred.
678 * Input:
679 * cpl WordCompletion * The string-completion resource object.
680 * Output:
681 * return const char * The description of the last error.
683 const char *cpl_last_error(WordCompletion *cpl)
685 return cpl ? _err_get_msg(cpl->err) : "NULL WordCompletion argument";
688 /*.......................................................................
689 * When an error occurs while performing a completion, you registerf a
690 * terse description of the error by calling cpl_record_error(). This
691 * message will then be returned on the next call to cpl_last_error().
693 * Input:
694 * cpl WordCompletion * The string-completion resource object that was
695 * originally passed to the callback.
696 * errmsg const char * The description of the error.
698 void cpl_record_error(WordCompletion *cpl, const char *errmsg)
700 if(cpl && errmsg)
701 _err_record_msg(cpl->err, errmsg, END_ERR_MSG);
704 /*.......................................................................
705 * This is the builtin completion callback function which performs file
706 * completion.
708 * Input:
709 * cpl WordCompletion * An opaque pointer to the object that will
710 * contain the matches. This should be filled
711 * via zero or more calls to cpl_add_completion().
712 * data void * Either NULL to request the default
713 * file-completion behavior, or a pointer to a
714 * CplFileConf structure, whose members specify
715 * a different behavior.
716 * line char * The current input line.
717 * word_end int The index of the character in line[] which
718 * follows the end of the token that is being
719 * completed.
720 * Output
721 * return int 0 - OK.
722 * 1 - Error.
724 CPL_MATCH_FN(cpl_file_completions)
726 #ifdef WITHOUT_FILE_SYSTEM
727 return 0;
728 #else
729 const char *start_path; /* The pointer to the start of the pathname */
730 /* in line[]. */
731 CplFileConf *conf; /* The new-style configuration object. */
733 * The following configuration object will be used if the caller didn't
734 * provide one.
736 CplFileConf default_conf;
738 * This function can be called externally, so check its arguments.
740 if(!cpl)
741 return 1;
742 if(!line || word_end < 0) {
743 _err_record_msg(cpl->err, "cpl_file_completions: Invalid arguments.",
744 END_ERR_MSG);
745 return 1;
748 * The 'data' argument is either a CplFileConf pointer, identifiable
749 * by having an integer id code as its first member, or the deprecated
750 * CplFileArgs pointer, or can be NULL to request the default
751 * configuration.
753 if(data && *(int *)data == CFC_ID_CODE) {
754 conf = (CplFileConf *) data;
755 } else {
757 * Select the defaults.
759 conf = &default_conf;
760 cpl_init_FileConf(&default_conf);
762 * If we have been passed an instance of the deprecated CplFileArgs
763 * structure, copy its configuration parameters over the defaults.
765 if(data) {
766 CplFileArgs *args = (CplFileArgs *) data;
767 conf->escaped = args->escaped;
768 conf->file_start = args->file_start;
772 * Get the start of the filename. If not specified by the caller
773 * identify it by searching backwards in the input line for an
774 * unescaped space or the start of the line.
776 if(conf->file_start < 0) {
777 start_path = _pu_start_of_path(line, word_end);
778 if(!start_path) {
779 _err_record_msg(cpl->err, "Unable to find the start of the filename.",
780 END_ERR_MSG);
781 return 1;
783 } else {
784 start_path = line + conf->file_start;
787 * Perform the completion.
789 if(_cf_complete_file(cpl, cpl->cf, line, start_path - line, word_end,
790 conf->escaped, conf->chk_fn, conf->chk_data)) {
791 cpl_record_error(cpl, _cf_last_error(cpl->cf));
792 return 1;
794 return 0;
795 #endif
798 /*.......................................................................
799 * Initialize a CplFileArgs structure with default configuration
800 * parameters. Note that the CplFileArgs configuration type is
801 * deprecated. The opaque CplFileConf object should be used in future
802 * applications.
804 * Input:
805 * cfa CplFileArgs * The configuration object of the
806 * cpl_file_completions() callback.
808 void cpl_init_FileArgs(CplFileArgs *cfa)
810 if(cfa) {
811 cfa->escaped = 1;
812 cfa->file_start = -1;
816 #ifndef WITHOUT_FILE_SYSTEM
817 /*.......................................................................
818 * Initialize a CplFileConf structure with default configuration
819 * parameters.
821 * Input:
822 * cfc CplFileConf * The configuration object of the
823 * cpl_file_completions() callback.
825 static void cpl_init_FileConf(CplFileConf *cfc)
827 if(cfc) {
828 cfc->id = CFC_ID_CODE;
829 cfc->escaped = 1;
830 cfc->file_start = -1;
831 cfc->chk_fn = 0;
832 cfc->chk_data = NULL;
835 #endif
837 /*.......................................................................
838 * Create a new CplFileConf object and initialize it with defaults.
840 * Output:
841 * return CplFileConf * The new object, or NULL on error.
843 CplFileConf *new_CplFileConf(void)
845 #ifdef WITHOUT_FILE_SYSTEM
846 errno = EINVAL;
847 return NULL;
848 #else
849 CplFileConf *cfc; /* The object to be returned */
851 * Allocate the container.
853 cfc = (CplFileConf *)malloc(sizeof(CplFileConf));
854 if(!cfc)
855 return NULL;
857 * Before attempting any operation that might fail, initialize the
858 * container at least up to the point at which it can safely be passed
859 * to del_CplFileConf().
861 cpl_init_FileConf(cfc);
862 return cfc;
863 #endif
866 /*.......................................................................
867 * Delete a CplFileConf object.
869 * Input:
870 * cfc CplFileConf * The object to be deleted.
871 * Output:
872 * return CplFileConf * The deleted object (always NULL).
874 CplFileConf *del_CplFileConf(CplFileConf *cfc)
876 #ifndef WITHOUT_FILE_SYSTEM
877 if(cfc) {
879 * Delete the container.
881 free(cfc);
883 #endif
884 return NULL;
887 /*.......................................................................
888 * If backslashes in the filename should be treated as literal
889 * characters, call the following function with literal=1. Otherwise
890 * the default is to treat them as escape characters, used for escaping
891 * spaces etc..
893 * Input:
894 * cfc CplFileConf * The cpl_file_completions() configuration object
895 * to be configured.
896 * literal int Pass non-zero here to enable literal interpretation
897 * of backslashes. Pass 0 to turn off literal
898 * interpretation.
900 void cfc_literal_escapes(CplFileConf *cfc, int literal)
902 #ifndef WITHOUT_FILE_SYSTEM
903 if(cfc)
904 cfc->escaped = !literal;
905 #endif
908 /*.......................................................................
909 * Call this function if you know where the index at which the
910 * filename prefix starts in the input line. Otherwise by default,
911 * or if you specify start_index to be -1, the filename is taken
912 * to start after the first unescaped space preceding the cursor,
913 * or the start of the line, which ever comes first.
915 * Input:
916 * cfc CplFileConf * The cpl_file_completions() configuration object
917 * to be configured.
918 * start_index int The index of the start of the filename in
919 * the input line, or -1 to select the default.
921 void cfc_file_start(CplFileConf *cfc, int start_index)
923 #ifndef WITHOUT_FILE_SYSTEM
924 if(cfc)
925 cfc->file_start = start_index;
926 #endif
929 /*.......................................................................
930 * If you only want certain types of files to be included in the
931 * list of completions, you use the following function to specify a
932 * callback function which will be called to ask whether a given file
933 * should be included.
935 * Input:
936 * cfc CplFileConf * The cpl_file_completions() configuration object
937 * to be configured.
938 * chk_fn CplCheckFn * Zero to disable filtering, or a pointer to a
939 * function that returns 1 if a given file should
940 * be included in the list of completions.
941 * chk_data void * Anonymous data to be passed to chk_fn()
942 * every time that it is called.
944 void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data)
946 #ifndef WITHOUT_FILE_SYSTEM
947 if(cfc) {
948 cfc->chk_fn = chk_fn;
949 cfc->chk_data = chk_data;
951 #endif
954 /*.......................................................................
955 * The following CplCheckFn callback returns non-zero if the specified
956 * filename is that of an executable.
958 CPL_CHECK_FN(cpl_check_exe)
960 #ifdef WITHOUT_FILE_SYSTEM
961 return 0;
962 #else
963 return _pu_path_is_exe(pathname);
964 #endif
967 /*.......................................................................
968 * Remove duplicates from a sorted array of matches.
970 * Input:
971 * cpl WordCompletion * The completion resource object.
973 static void cpl_zap_duplicates(WordCompletion *cpl)
975 CplMatch *matches; /* The array of matches */
976 int nmatch; /* The number of elements in matches[] */
977 const char *completion; /* The completion string of the last unique match */
978 const char *type_suffix; /* The type of the last unique match */
979 int src; /* The index of the match being considered */
980 int dst; /* The index at which to record the next */
981 /* unique match. */
983 * Get the array of matches and the number of matches that it
984 * contains.
986 matches = cpl->result.matches;
987 nmatch = cpl->result.nmatch;
989 * No matches?
991 if(nmatch < 1)
992 return;
994 * Initialize the comparison strings with the first match.
996 completion = matches[0].completion;
997 type_suffix = matches[0].type_suffix;
999 * Go through the array of matches, copying each new unrecorded
1000 * match at the head of the array, while discarding duplicates.
1002 for(src=dst=1; src<nmatch; src++) {
1003 CplMatch *match = matches + src;
1004 if(strcmp(completion, match->completion) != 0 ||
1005 strcmp(type_suffix, match->type_suffix) != 0) {
1006 if(src != dst)
1007 matches[dst] = *match;
1008 dst++;
1009 completion = match->completion;
1010 type_suffix = match->type_suffix;
1014 * Record the number of unique matches that remain.
1016 cpl->result.nmatch = dst;
1017 return;
1020 /*.......................................................................
1021 * Work out how to arrange a given array of completions into a listing
1022 * of one or more fixed size columns.
1024 * Input:
1025 * result CplMatches * The set of completions to be listed.
1026 * term_width int The width of the terminal. A lower limit of
1027 * zero is quietly enforced.
1028 * Input/Output:
1029 * fmt CplListFormat * The formatting information will be assigned
1030 * to the members of *fmt.
1032 static void cpl_plan_listing(CplMatches *result, int term_width,
1033 CplListFormat *fmt)
1035 int maxlen; /* The length of the longest matching string */
1036 int i;
1038 * Ensure that term_width >= 0.
1040 if(term_width < 0)
1041 term_width = 0;
1043 * Start by assuming the worst case, that either nothing will fit
1044 * on the screen, or that there are no matches to be listed.
1046 fmt->term_width = term_width;
1047 fmt->column_width = 0;
1048 fmt->nline = fmt->ncol = 0;
1050 * Work out the maximum length of the matching strings.
1052 maxlen = 0;
1053 for(i=0; i<result->nmatch; i++) {
1054 CplMatch *match = result->matches + i;
1055 int len = strlen(match->completion) + strlen(match->type_suffix);
1056 if(len > maxlen)
1057 maxlen = len;
1060 * Nothing to list?
1062 if(maxlen == 0)
1063 return;
1065 * Split the available terminal width into columns of
1066 * maxlen + CPL_COL_SEP characters.
1068 fmt->column_width = maxlen;
1069 fmt->ncol = fmt->term_width / (fmt->column_width + CPL_COL_SEP);
1071 * If the column width is greater than the terminal width, zero columns
1072 * will have been selected. Set a lower limit of one column. Leave it
1073 * up to the caller how to deal with completions who's widths exceed
1074 * the available terminal width.
1076 if(fmt->ncol < 1)
1077 fmt->ncol = 1;
1079 * How many lines of output will be needed?
1081 fmt->nline = (result->nmatch + fmt->ncol - 1) / fmt->ncol;
1082 return;
1085 /*.......................................................................
1086 * Render one line of a multi-column listing of completions, using a
1087 * callback function to pass the output to an arbitrary destination.
1089 * Input:
1090 * result CplMatches * The container of the sorted array of
1091 * completions.
1092 * fmt CplListFormat * Formatting information.
1093 * lnum int The index of the line to print, starting
1094 * from 0, and incrementing until the return
1095 * value indicates that there is nothing more
1096 * to be printed.
1097 * write_fn GlWriteFn * The function to call to write the line, or
1098 * 0 to discard the output.
1099 * data void * Anonymous data to pass to write_fn().
1100 * Output:
1101 * return int 0 - Line printed ok.
1102 * 1 - Nothing to print.
1104 static int cpl_format_line(CplMatches *result, CplListFormat *fmt, int lnum,
1105 GlWriteFn *write_fn, void *data)
1107 int col; /* The index of the list column being output */
1109 * If the line index is out of bounds, there is nothing to be written.
1111 if(lnum < 0 || lnum >= fmt->nline)
1112 return 1;
1114 * If no output function has been provided, return as though the
1115 * line had been printed.
1117 if(!write_fn)
1118 return 0;
1120 * Print the matches in 'ncol' columns, sorted in line order within each
1121 * column.
1123 for(col=0; col < fmt->ncol; col++) {
1124 int m = col*fmt->nline + lnum;
1126 * Is there another match to be written? Note that in general
1127 * the last line of a listing will have fewer filled columns
1128 * than the initial lines.
1130 if(m < result->nmatch) {
1131 CplMatch *match = result->matches + m;
1133 * How long are the completion and type-suffix strings?
1135 int clen = strlen(match->completion);
1136 int tlen = strlen(match->type_suffix);
1138 * Write the completion string.
1140 if(write_fn(data, match->completion, clen) != clen)
1141 return 1;
1143 * Write the type suffix, if any.
1145 if(tlen > 0 && write_fn(data, match->type_suffix, tlen) != tlen)
1146 return 1;
1148 * If another column follows the current one, pad to its start with spaces.
1150 if(col+1 < fmt->ncol) {
1152 * The following constant string of spaces is used to pad the output.
1154 static const char spaces[] = " ";
1155 static const int nspace = sizeof(spaces) - 1;
1157 * Pad to the next column, using as few sub-strings of the spaces[]
1158 * array as possible.
1160 int npad = fmt->column_width + CPL_COL_SEP - clen - tlen;
1161 while(npad>0) {
1162 int n = npad > nspace ? nspace : npad;
1163 if(write_fn(data, spaces + nspace - n, n) != n)
1164 return 1;
1165 npad -= n;
1171 * Start a new line.
1174 char s[] = "\r\n";
1175 int n = strlen(s);
1176 if(write_fn(data, s, n) != n)
1177 return 1;
1179 return 0;