xref: /illumos-gate/usr/src/lib/libtecla/common/cplmatch.c (revision 1da57d551424de5a9d469760be7c4b4d4f10a755)
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.
15  *
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.
25  *
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.
30  */
31 
32 /*
33  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
34  * Use is subject to license terms.
35  */
36 
37 /*
38  * Standard includes.
39  */
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <errno.h>
44 
45 /*
46  * Local includes.
47  */
48 #include "libtecla.h"
49 #include "ioutil.h"
50 #include "stringrp.h"
51 #include "pathutil.h"
52 #include "cplfile.h"
53 #include "cplmatch.h"
54 #include "errmsg.h"
55 
56 /*
57  * Specify the number of strings to allocate when the string free-list
58  * is exhausted. This also sets the number of elements to expand the
59  * matches[] array by whenever it is found to be too small.
60  */
61 #define STR_BLK_FACT 100
62 
63 /*
64  * Set the default number of spaces place between columns when listing
65  * a set of completions.
66  */
67 #define CPL_COL_SEP 2
68 
69 /*
70  * Completion matches are recorded in containers of the following
71  * type.
72  */
73 struct WordCompletion {
74   ErrMsg *err;            /* The error reporting buffer */
75   StringGroup *sg;        /* Memory for a group of strings */
76   int matches_dim;        /* The allocated size of result.matches[] */
77   CplMatches result;      /* Completions to be returned to the caller */
78 #ifndef WITHOUT_FILE_SYSTEM
79   CompleteFile *cf;       /* The resources used for filename completion */
80 #endif
81 };
82 
83 static void cpl_sort_matches(WordCompletion *cpl);
84 static void cpl_zap_duplicates(WordCompletion *cpl);
85 static void cpl_clear_completions(WordCompletion *cpl);
86 static int cpl_cmp_matches(const void *v1, const void *v2);
87 static int cpl_cmp_suffixes(const void *v1, const void *v2);
88 
89 /*
90  * The new_CplFileConf() constructor sets the integer first member of
91  * the returned object to the following magic number. On seeing this,
92  * cpl_file_completions() knows when it is passed a valid CplFileConf
93  * object.
94  */
95 #define CFC_ID_CODE 4568
96 
97 #ifndef WITHOUT_FILE_SYSTEM
98 /*
99  * A pointer to a structure of the following type can be passed to
100  * the builtin file-completion callback function to modify its behavior.
101  */
102 struct CplFileConf {
103   int id;             /* new_CplFileConf() sets this to CFC_ID_CODE */
104   int escaped;        /* If none-zero, backslashes in the input line are */
105                       /*  interpreted as escaping special characters and */
106                       /*  spaces, and any special characters and spaces in */
107                       /*  the listed completions will also be escaped with */
108                       /*  added backslashes. This is the default behaviour. */
109                       /* If zero, backslashes are interpreted as being */
110                       /*  literal parts of the filename, and none are added */
111                       /*  to the completion suffixes. */
112   int file_start;     /* The index in the input line of the first character */
113                       /*  of the filename. If you specify -1 here, */
114                       /*  cpl_file_completions() identifies the */
115                       /*  the start of the filename by looking backwards for */
116                       /*  an unescaped space, or the beginning of the line. */
117   CplCheckFn *chk_fn; /* If not zero, this argument specifies a */
118                       /*  function to call to ask whether a given */
119                       /*  file should be included in the list */
120                       /*  of completions. */
121   void *chk_data;     /* Anonymous data to be passed to check_fn(). */
122 };
123 
124 static void cpl_init_FileConf(CplFileConf *cfc);
125 
126 /*
127  * When file-system access is being excluded, define a dummy structure
128  * to satisfy the typedef in libtecla.h.
129  */
130 #else
131 struct CplFileConf {int dummy;};
132 #endif
133 
134 /*
135  * Encapsulate the formatting information needed to layout a
136  * multi-column listing of completions.
137  */
138 typedef struct {
139   int term_width;     /* The width of the terminal (characters) */
140   int column_width;   /* The number of characters within in each column. */
141   int ncol;           /* The number of columns needed */
142   int nline;          /* The number of lines needed */
143 } CplListFormat;
144 
145 /*
146  * Given the current terminal width, and a list of completions, determine
147  * how to best use the terminal width to display a multi-column listing
148  * of completions.
149  */
150 static void cpl_plan_listing(CplMatches *result, int term_width,
151 			     CplListFormat *fmt);
152 
153 /*
154  * Display a given line of a multi-column list of completions.
155  */
156 static int cpl_format_line(CplMatches *result, CplListFormat *fmt, int lnum,
157 			   GlWriteFn *write_fn, void *data);
158 
159 /*.......................................................................
160  * Create a new string-completion object.
161  *
162  * Output:
163  *  return    WordCompletion *  The new object, or NULL on error.
164  */
new_WordCompletion(void)165 WordCompletion *new_WordCompletion(void)
166 {
167   WordCompletion *cpl;  /* The object to be returned */
168 /*
169  * Allocate the container.
170  */
171   cpl = (WordCompletion *) malloc(sizeof(WordCompletion));
172   if(!cpl) {
173     errno = ENOMEM;
174     return NULL;
175   };
176 /*
177  * Before attempting any operation that might fail, initialize the
178  * container at least up to the point at which it can safely be passed
179  * to del_WordCompletion().
180  */
181   cpl->err = NULL;
182   cpl->sg = NULL;
183   cpl->matches_dim = 0;
184   cpl->result.suffix = NULL;
185   cpl->result.cont_suffix = NULL;
186   cpl->result.matches = NULL;
187   cpl->result.nmatch = 0;
188 #ifndef WITHOUT_FILE_SYSTEM
189   cpl->cf = NULL;
190 #endif
191 /*
192  * Allocate a place to record error messages.
193  */
194   cpl->err = _new_ErrMsg();
195   if(!cpl->err)
196     return del_WordCompletion(cpl);
197 /*
198  * Allocate an object that allows a group of strings to be allocated
199  * efficiently by placing many of them in contiguous string segments.
200  */
201 #ifdef WITHOUT_FILE_SYSTEM
202   cpl->sg = _new_StringGroup(MAX_PATHLEN_FALLBACK);
203 #else
204   cpl->sg = _new_StringGroup(_pu_pathname_dim());
205 #endif
206   if(!cpl->sg)
207     return del_WordCompletion(cpl);
208 /*
209  * Allocate an array for matching completions. This will be extended later
210  * if needed.
211  */
212   cpl->matches_dim = STR_BLK_FACT;
213   cpl->result.matches = (CplMatch *) malloc(sizeof(cpl->result.matches[0]) *
214 					    cpl->matches_dim);
215   if(!cpl->result.matches) {
216     errno = ENOMEM;
217     return del_WordCompletion(cpl);
218   };
219 /*
220  * Allocate a filename-completion resource object.
221  */
222 #ifndef WITHOUT_FILE_SYSTEM
223   cpl->cf = _new_CompleteFile();
224   if(!cpl->cf)
225     return del_WordCompletion(cpl);
226 #endif
227   return cpl;
228 }
229 
230 /*.......................................................................
231  * Delete a string-completion object.
232  *
233  * Input:
234  *  cpl    WordCompletion *  The object to be deleted.
235  * Output:
236  *  return WordCompletion *  The deleted object (always NULL).
237  */
del_WordCompletion(WordCompletion * cpl)238 WordCompletion *del_WordCompletion(WordCompletion *cpl)
239 {
240   if(cpl) {
241     cpl->err = _del_ErrMsg(cpl->err);
242     cpl->sg = _del_StringGroup(cpl->sg);
243     if(cpl->result.matches) {
244       free(cpl->result.matches);
245       cpl->result.matches = NULL;
246 #ifndef WITHOUT_FILE_SYSTEM
247       cpl->cf = _del_CompleteFile(cpl->cf);
248 #endif
249     };
250     free(cpl);
251   };
252   return NULL;
253 }
254 
255 /*.......................................................................
256  * This function is designed to be called by CplMatchFn callback
257  * functions. It adds one possible completion of the token that is being
258  * completed to an array of completions. If the completion needs any
259  * special quoting to be valid when displayed in the input line, this
260  * quoting must be included in the string.
261  *
262  * Input:
263  *  cpl     WordCompletion *  The argument of the same name that was passed
264  *                            to the calling CplMatchFn callback function.
265  *  line        const char *  The input line, as received by the callback
266  *                            function.
267  *  word_start         int    The index within line[] of the start of the
268  *                            word that is being completed.
269  *  word_end           int    The index within line[] of the character which
270  *                            follows the incomplete word, as received by the
271  *                            calling callback function.
272  *  suffix      const char *  The appropriately quoted string that could
273  *                            be appended to the incomplete token to complete
274  *                            it. A copy of this string will be allocated
275  *                            internally.
276  *  type_suffix const char *  When listing multiple completions, gl_get_line()
277  *                            appends this string to the completion to indicate
278  *                            its type to the user. If not pertinent pass "".
279  *                            Otherwise pass a literal or static string.
280  *  cont_suffix const char *  If this turns out to be the only completion,
281  *                            gl_get_line() will append this string as
282  *                            a continuation. For example, the builtin
283  *                            file-completion callback registers a directory
284  *                            separator here for directory matches, and a
285  *                            space otherwise. If the match were a function
286  *                            name you might want to append an open
287  *                            parenthesis, etc.. If not relevant pass "".
288  *                            Otherwise pass a literal or static string.
289  * Output:
290  *  return             int    0 - OK.
291  *                            1 - Error.
292  */
cpl_add_completion(WordCompletion * cpl,const char * line,int word_start,int word_end,const char * suffix,const char * type_suffix,const char * cont_suffix)293 int cpl_add_completion(WordCompletion *cpl, const char *line,
294 		       int word_start, int word_end, const char *suffix,
295 		       const char *type_suffix, const char *cont_suffix)
296 {
297   CplMatch *match; /* The container of the new match */
298   char *string;    /* A newly allocated copy of the completion string */
299   size_t len;
300 /*
301  * Check the arguments.
302  */
303   if(!cpl)
304     return 1;
305   if(!suffix)
306     return 0;
307   if(!type_suffix)
308     type_suffix = "";
309   if(!cont_suffix)
310     cont_suffix = "";
311 /*
312  * Do we need to extend the array of matches[]?
313  */
314   if(cpl->result.nmatch+1 > cpl->matches_dim) {
315     int needed = cpl->matches_dim + STR_BLK_FACT;
316     CplMatch *matches = (CplMatch *) realloc(cpl->result.matches,
317 			    sizeof(cpl->result.matches[0]) * needed);
318     if(!matches) {
319       _err_record_msg(cpl->err,
320 		      "Insufficient memory to extend array of matches.",
321 		      END_ERR_MSG);
322       return 1;
323     };
324     cpl->result.matches = matches;
325     cpl->matches_dim = needed;
326   };
327 /*
328  * Allocate memory to store the combined completion prefix and the
329  * new suffix.
330  */
331   len = strlen(suffix);
332   string = _sg_alloc_string(cpl->sg, word_end-word_start + len);
333   if(!string) {
334     _err_record_msg(cpl->err, "Insufficient memory to extend array of matches.",
335 		    END_ERR_MSG);
336     return 1;
337   };
338 /*
339  * Compose the string.
340  */
341   strncpy(string, line + word_start, word_end - word_start);
342   strlcpy(string + word_end - word_start, suffix, len + 1);
343 /*
344  * Record the new match.
345  */
346   match = cpl->result.matches + cpl->result.nmatch++;
347   match->completion = string;
348   match->suffix = string + word_end - word_start;
349   match->type_suffix = type_suffix;
350 /*
351  * Record the continuation suffix.
352  */
353   cpl->result.cont_suffix = cont_suffix;
354   return 0;
355 }
356 
357 /*.......................................................................
358  * Sort the array of matches.
359  *
360  * Input:
361  *  cpl   WordCompletion *  The completion resource object.
362  */
cpl_sort_matches(WordCompletion * cpl)363 static void cpl_sort_matches(WordCompletion *cpl)
364 {
365   qsort(cpl->result.matches, cpl->result.nmatch,
366 	sizeof(cpl->result.matches[0]), cpl_cmp_matches);
367 }
368 
369 /*.......................................................................
370  * This is a qsort() comparison function used to sort matches.
371  *
372  * Input:
373  *  v1, v2   void *  Pointers to the two matches to be compared.
374  * Output:
375  *  return    int    -1 -> v1 < v2.
376  *                    0 -> v1 == v2
377  *                    1 -> v1 > v2
378  */
cpl_cmp_matches(const void * v1,const void * v2)379 static int cpl_cmp_matches(const void *v1, const void *v2)
380 {
381   const CplMatch *m1 = (const CplMatch *) v1;
382   const CplMatch *m2 = (const CplMatch *) v2;
383   return strcmp(m1->completion, m2->completion);
384 }
385 
386 /*.......................................................................
387  * Sort the array of matches in order of their suffixes.
388  *
389  * Input:
390  *  cpl   WordCompletion *  The completion resource object.
391  */
cpl_sort_suffixes(WordCompletion * cpl)392 static void cpl_sort_suffixes(WordCompletion *cpl)
393 {
394   qsort(cpl->result.matches, cpl->result.nmatch,
395 	sizeof(cpl->result.matches[0]), cpl_cmp_suffixes);
396 }
397 
398 /*.......................................................................
399  * This is a qsort() comparison function used to sort matches in order of
400  * their suffixes.
401  *
402  * Input:
403  *  v1, v2   void *  Pointers to the two matches to be compared.
404  * Output:
405  *  return    int    -1 -> v1 < v2.
406  *                    0 -> v1 == v2
407  *                    1 -> v1 > v2
408  */
cpl_cmp_suffixes(const void * v1,const void * v2)409 static int cpl_cmp_suffixes(const void *v1, const void *v2)
410 {
411   const CplMatch *m1 = (const CplMatch *) v1;
412   const CplMatch *m2 = (const CplMatch *) v2;
413   return strcmp(m1->suffix, m2->suffix);
414 }
415 
416 /*.......................................................................
417  * Find the common prefix of all of the matching completion matches,
418  * and record a pointer to it in cpl->result.suffix. Note that this has
419  * the side effect of sorting the matches into suffix order.
420  *
421  * Input:
422  *  cpl   WordCompletion *  The completion resource object.
423  * Output:
424  *  return           int    0 - OK.
425  *                          1 - Error.
426  */
cpl_common_suffix(WordCompletion * cpl)427 static int cpl_common_suffix(WordCompletion *cpl)
428 {
429   CplMatches *result;       /* The result container */
430   const char *first, *last; /* The first and last matching suffixes */
431   int length;               /* The length of the common suffix */
432 /*
433  * Get the container of the array of matching files.
434  */
435   result = &cpl->result;
436 /*
437  * No matching completions?
438  */
439   if(result->nmatch < 1)
440     return 0;
441 /*
442  * Sort th matches into suffix order.
443  */
444   cpl_sort_suffixes(cpl);
445 /*
446  * Given that the array of matches is sorted, the first and last
447  * suffixes are those that differ most in their prefixes, so the common
448  * prefix of these strings is the longest common prefix of all of the
449  * suffixes.
450  */
451   first = result->matches[0].suffix;
452   last = result->matches[result->nmatch - 1].suffix;
453 /*
454  * Find the point at which the first and last matching strings
455  * first difffer.
456  */
457   while(*first && *first == *last) {
458     first++;
459     last++;
460   };
461 /*
462  * How long is the common suffix?
463  */
464   length = first - result->matches[0].suffix;
465 /*
466  * Allocate memory to record the common suffix.
467  */
468   result->suffix = _sg_alloc_string(cpl->sg, length);
469   if(!result->suffix) {
470     _err_record_msg(cpl->err,
471 		    "Insufficient memory to record common completion suffix.",
472 		    END_ERR_MSG);
473     return 1;
474   };
475 /*
476  * Record the common suffix.
477  */
478   strncpy(result->suffix, result->matches[0].suffix, length);
479   result->suffix[length] = '\0';
480   return 0;
481 }
482 
483 /*.......................................................................
484  * Discard the contents of the array of possible completion matches.
485  *
486  * Input:
487  *  cpl   WordCompletion *  The word-completion resource object.
488  */
cpl_clear_completions(WordCompletion * cpl)489 static void cpl_clear_completions(WordCompletion *cpl)
490 {
491 /*
492  * Discard all of the strings.
493  */
494   _clr_StringGroup(cpl->sg);
495 /*
496  * Record the fact that the array is now empty.
497  */
498   cpl->result.nmatch = 0;
499   cpl->result.suffix = NULL;
500   cpl->result.cont_suffix = "";
501 /*
502  * Also clear the error message.
503  */
504   _err_clear_msg(cpl->err);
505   return;
506 }
507 
508 /*.......................................................................
509  * Given an input line and the point at which it completion is to be
510  * attempted, return an array of possible completions.
511  *
512  * Input:
513  *  cpl    WordCompletion *  The completion resource object.
514  *  line             char *  The current input line.
515  *  word_end          int    The index of the character in line[] which
516  *                           follows the end of the token that is being
517  *                           completed.
518  *  data             void *  Anonymous 'data' to be passed to match_fn().
519  *  match_fn   CplMatchFn *  The function that will identify the prefix
520  *                           to be completed from the input line, and
521  *                           record completion matches.
522  * Output:
523  *  return     CplMatches *  The container of the array of possible
524  *                           completions. The returned pointer refers
525  *                           to a container owned by the parent WordCompletion
526  *                           object, and its contents thus potentially
527  *                           change on every call to cpl_matches().
528  *                           On error, NULL is returned, and a description
529  *                           of the error can be acquired by calling
530  *                           cpl_last_error(cpl).
531  */
cpl_complete_word(WordCompletion * cpl,const char * line,int word_end,void * data,CplMatchFn * match_fn)532 CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line,
533 			      int word_end, void *data,
534 			      CplMatchFn *match_fn)
535 {
536   int line_len;   /* The total length of the input line */
537 /*
538  * How long is the input line?
539  */
540   line_len = strlen(line);
541 /*
542  * Check the arguments.
543  */
544   if(!cpl || !line || !match_fn || word_end < 0 || word_end > line_len) {
545     if(cpl) {
546       _err_record_msg(cpl->err, "cpl_complete_word: Invalid arguments.",
547 		      END_ERR_MSG);
548     };
549     return NULL;
550   };
551 /*
552  * Clear the return container.
553  */
554   cpl_clear_completions(cpl);
555 /*
556  * Have the matching function record possible completion matches in
557  * cpl->result.matches.
558  */
559   if(match_fn(cpl, data, line, word_end)) {
560     if(_err_get_msg(cpl->err)[0] == '\0')
561       _err_record_msg(cpl->err, "Error completing word.", END_ERR_MSG);
562     return NULL;
563   };
564 /*
565  * Record a copy of the common initial part of all of the prefixes
566  * in cpl->result.common.
567  */
568   if(cpl_common_suffix(cpl))
569     return NULL;
570 /*
571  * Sort the matches into lexicographic order.
572  */
573   cpl_sort_matches(cpl);
574 /*
575  * Discard any duplicate matches.
576  */
577   cpl_zap_duplicates(cpl);
578 /*
579  * If there is more than one match, discard the continuation suffix.
580  */
581   if(cpl->result.nmatch > 1)
582     cpl->result.cont_suffix = "";
583 /*
584  * Return the array of matches.
585  */
586   return &cpl->result;
587 }
588 
589 /*.......................................................................
590  * Recall the return value of the last call to cpl_complete_word().
591  *
592  * Input:
593  *  cpl    WordCompletion *  The completion resource object.
594  * Output:
595  *  return     CplMatches *  The container of the array of possible
596  *                           completions, as returned by the last call to
597  *                           cpl_complete_word(). The returned pointer refers
598  *                           to a container owned by the parent WordCompletion
599  *                           object, and its contents thus potentially
600  *                           change on every call to cpl_complete_word().
601  *                           On error, either in the execution of this
602  *                           function, or in the last call to
603  *                           cpl_complete_word(), NULL is returned, and a
604  *                           description of the error can be acquired by
605  *                           calling cpl_last_error(cpl).
606  */
cpl_recall_matches(WordCompletion * cpl)607 CplMatches *cpl_recall_matches(WordCompletion *cpl)
608 {
609   return (!cpl || *_err_get_msg(cpl->err)!='\0') ? NULL : &cpl->result;
610 }
611 
612 /*.......................................................................
613  * Print out an array of matching completions.
614  *
615  * Input:
616  *  result  CplMatches *   The container of the sorted array of
617  *                         completions.
618  *  fp            FILE *   The output stream to write to.
619  *  term_width     int     The width of the terminal.
620  * Output:
621  *  return         int     0 - OK.
622  *                         1 - Error.
623  */
cpl_list_completions(CplMatches * result,FILE * fp,int term_width)624 int cpl_list_completions(CplMatches *result, FILE *fp, int term_width)
625 {
626   return _cpl_output_completions(result, _io_write_stdio, fp, term_width);
627 }
628 
629 /*.......................................................................
630  * Print an array of matching completions via a callback function.
631  *
632  * Input:
633  *  result   CplMatches *  The container of the sorted array of
634  *                         completions.
635  *  write_fn  GlWriteFn *  The function to call to write the completions,
636  *                         or 0 to discard the output.
637  *  data           void *  Anonymous data to pass to write_fn().
638  *  term_width      int    The width of the terminal.
639  * Output:
640  *  return          int     0 - OK.
641  *                          1 - Error.
642  */
_cpl_output_completions(CplMatches * result,GlWriteFn * write_fn,void * data,int term_width)643 int _cpl_output_completions(CplMatches *result, GlWriteFn *write_fn, void *data,
644 			    int term_width)
645 {
646   CplListFormat fmt; /* List formatting information */
647   int lnum;          /* The sequential number of the line to print next */
648 /*
649  * Not enough space to list anything?
650  */
651   if(term_width < 1)
652     return 0;
653 /*
654  * Do we have a callback to write via, and any completions to be listed?
655  */
656   if(write_fn && result && result->nmatch>0) {
657 /*
658  * Work out how to arrange the listing into fixed sized columns.
659  */
660     cpl_plan_listing(result, term_width, &fmt);
661 /*
662  * Print the listing via the specified callback.
663  */
664     for(lnum=0; lnum < fmt.nline; lnum++) {
665       if(cpl_format_line(result, &fmt, lnum, write_fn, data))
666 	return 1;
667     };
668   };
669   return 0;
670 }
671 
672 /*.......................................................................
673  * Return a description of the string-completion error that occurred.
674  *
675  * Input:
676  *  cpl   WordCompletion *  The string-completion resource object.
677  * Output:
678  *  return    const char *  The description of the last error.
679  */
cpl_last_error(WordCompletion * cpl)680 const char *cpl_last_error(WordCompletion *cpl)
681 {
682   return cpl ? _err_get_msg(cpl->err) : "NULL WordCompletion argument";
683 }
684 
685 /*.......................................................................
686  * When an error occurs while performing a completion, you registerf a
687  * terse description of the error by calling cpl_record_error(). This
688  * message will then be returned on the next call to cpl_last_error().
689  *
690  * Input:
691  *  cpl   WordCompletion *  The string-completion resource object that was
692  *                          originally passed to the callback.
693  *  errmsg    const char *  The description of the error.
694  */
cpl_record_error(WordCompletion * cpl,const char * errmsg)695 void cpl_record_error(WordCompletion *cpl, const char *errmsg)
696 {
697   if(cpl && errmsg)
698     _err_record_msg(cpl->err, errmsg, END_ERR_MSG);
699 }
700 
701 /*.......................................................................
702  * This is the builtin completion callback function which performs file
703  * completion.
704  *
705  * Input:
706  *  cpl  WordCompletion *  An opaque pointer to the object that will
707  *                         contain the matches. This should be filled
708  *                         via zero or more calls to cpl_add_completion().
709  *  data           void *  Either NULL to request the default
710  *                         file-completion behavior, or a pointer to a
711  *                         CplFileConf structure, whose members specify
712  *                         a different behavior.
713  *  line           char *  The current input line.
714  *  word_end        int    The index of the character in line[] which
715  *                         follows the end of the token that is being
716  *                         completed.
717  * Output
718  *  return          int    0 - OK.
719  *                         1 - Error.
720  */
CPL_MATCH_FN(cpl_file_completions)721 CPL_MATCH_FN(cpl_file_completions)
722 {
723 #ifdef WITHOUT_FILE_SYSTEM
724   return 0;
725 #else
726   const char *start_path;  /* The pointer to the start of the pathname */
727                            /*  in line[]. */
728   CplFileConf *conf;       /* The new-style configuration object. */
729 /*
730  * The following configuration object will be used if the caller didn't
731  * provide one.
732  */
733   CplFileConf default_conf;
734 /*
735  * This function can be called externally, so check its arguments.
736  */
737   if(!cpl)
738     return 1;
739   if(!line || word_end < 0) {
740     _err_record_msg(cpl->err, "cpl_file_completions: Invalid arguments.",
741 		    END_ERR_MSG);
742     return 1;
743   };
744 /*
745  * The 'data' argument is either a CplFileConf pointer, identifiable
746  * by having an integer id code as its first member, or the deprecated
747  * CplFileArgs pointer, or can be NULL to request the default
748  * configuration.
749  */
750   if(data && *(int *)data == CFC_ID_CODE) {
751     conf = (CplFileConf *) data;
752   } else {
753 /*
754  * Select the defaults.
755  */
756     conf = &default_conf;
757     cpl_init_FileConf(&default_conf);
758 /*
759  * If we have been passed an instance of the deprecated CplFileArgs
760  * structure, copy its configuration parameters over the defaults.
761  */
762     if(data) {
763       CplFileArgs *args = (CplFileArgs *) data;
764       conf->escaped = args->escaped;
765       conf->file_start = args->file_start;
766     };
767   };
768 /*
769  * Get the start of the filename. If not specified by the caller
770  * identify it by searching backwards in the input line for an
771  * unescaped space or the start of the line.
772  */
773   if(conf->file_start < 0) {
774     start_path = _pu_start_of_path(line, word_end);
775     if(!start_path) {
776       _err_record_msg(cpl->err, "Unable to find the start of the filename.",
777 		      END_ERR_MSG);
778       return 1;
779     };
780   } else {
781     start_path = line + conf->file_start;
782   };
783 /*
784  * Perform the completion.
785  */
786   if(_cf_complete_file(cpl, cpl->cf, line, start_path - line, word_end,
787 		      conf->escaped, conf->chk_fn, conf->chk_data)) {
788     cpl_record_error(cpl, _cf_last_error(cpl->cf));
789     return 1;
790   };
791   return 0;
792 #endif
793 }
794 
795 /*.......................................................................
796  * Initialize a CplFileArgs structure with default configuration
797  * parameters. Note that the CplFileArgs configuration type is
798  * deprecated. The opaque CplFileConf object should be used in future
799  * applications.
800  *
801  * Input:
802  *  cfa  CplFileArgs *  The configuration object of the
803  *                      cpl_file_completions() callback.
804  */
cpl_init_FileArgs(CplFileArgs * cfa)805 void cpl_init_FileArgs(CplFileArgs *cfa)
806 {
807   if(cfa) {
808     cfa->escaped = 1;
809     cfa->file_start = -1;
810   };
811 }
812 
813 #ifndef WITHOUT_FILE_SYSTEM
814 /*.......................................................................
815  * Initialize a CplFileConf structure with default configuration
816  * parameters.
817  *
818  * Input:
819  *  cfc  CplFileConf *  The configuration object of the
820  *                      cpl_file_completions() callback.
821  */
cpl_init_FileConf(CplFileConf * cfc)822 static void cpl_init_FileConf(CplFileConf *cfc)
823 {
824   if(cfc) {
825     cfc->id = CFC_ID_CODE;
826     cfc->escaped = 1;
827     cfc->file_start = -1;
828     cfc->chk_fn = 0;
829     cfc->chk_data = NULL;
830   };
831 }
832 #endif
833 
834 /*.......................................................................
835  * Create a new CplFileConf object and initialize it with defaults.
836  *
837  * Output:
838  *  return  CplFileConf *  The new object, or NULL on error.
839  */
new_CplFileConf(void)840 CplFileConf *new_CplFileConf(void)
841 {
842 #ifdef WITHOUT_FILE_SYSTEM
843   errno = EINVAL;
844   return NULL;
845 #else
846   CplFileConf *cfc;  /* The object to be returned */
847 /*
848  * Allocate the container.
849  */
850   cfc = (CplFileConf *)malloc(sizeof(CplFileConf));
851   if(!cfc)
852     return NULL;
853 /*
854  * Before attempting any operation that might fail, initialize the
855  * container at least up to the point at which it can safely be passed
856  * to del_CplFileConf().
857  */
858   cpl_init_FileConf(cfc);
859   return cfc;
860 #endif
861 }
862 
863 /*.......................................................................
864  * Delete a CplFileConf object.
865  *
866  * Input:
867  *  cfc    CplFileConf *  The object to be deleted.
868  * Output:
869  *  return CplFileConf *  The deleted object (always NULL).
870  */
del_CplFileConf(CplFileConf * cfc)871 CplFileConf *del_CplFileConf(CplFileConf *cfc)
872 {
873 #ifndef WITHOUT_FILE_SYSTEM
874   if(cfc) {
875 /*
876  * Delete the container.
877  */
878     free(cfc);
879   };
880 #endif
881   return NULL;
882 }
883 
884 /*.......................................................................
885  * If backslashes in the filename should be treated as literal
886  * characters, call the following function with literal=1. Otherwise
887  * the default is to treat them as escape characters, used for escaping
888  * spaces etc..
889  *
890  * Input:
891  *  cfc    CplFileConf *  The cpl_file_completions() configuration object
892  *                        to be configured.
893  *  literal        int    Pass non-zero here to enable literal interpretation
894  *                        of backslashes. Pass 0 to turn off literal
895  *                        interpretation.
896  */
cfc_literal_escapes(CplFileConf * cfc,int literal)897 void cfc_literal_escapes(CplFileConf *cfc, int literal)
898 {
899 #ifndef WITHOUT_FILE_SYSTEM
900   if(cfc)
901     cfc->escaped = !literal;
902 #endif
903 }
904 
905 /*.......................................................................
906  * Call this function if you know where the index at which the
907  * filename prefix starts in the input line. Otherwise by default,
908  * or if you specify start_index to be -1, the filename is taken
909  * to start after the first unescaped space preceding the cursor,
910  * or the start of the line, which ever comes first.
911  *
912  * Input:
913  *  cfc    CplFileConf *  The cpl_file_completions() configuration object
914  *                        to be configured.
915  *  start_index    int    The index of the start of the filename in
916  *                        the input line, or -1 to select the default.
917  */
cfc_file_start(CplFileConf * cfc,int start_index)918 void cfc_file_start(CplFileConf *cfc, int start_index)
919 {
920 #ifndef WITHOUT_FILE_SYSTEM
921   if(cfc)
922     cfc->file_start = start_index;
923 #endif
924 }
925 
926 /*.......................................................................
927  * If you only want certain types of files to be included in the
928  * list of completions, you use the following function to specify a
929  * callback function which will be called to ask whether a given file
930  * should be included.
931  *
932  * Input:
933  *  cfc    CplFileConf *  The cpl_file_completions() configuration object
934  *                        to be configured.
935  *  chk_fn  CplCheckFn *  Zero to disable filtering, or a pointer to a
936  *                        function that returns 1 if a given file should
937  *                        be included in the list of completions.
938  *  chk_data      void *  Anonymous data to be passed to chk_fn()
939  *                        every time that it is called.
940  */
cfc_set_check_fn(CplFileConf * cfc,CplCheckFn * chk_fn,void * chk_data)941 void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data)
942 {
943 #ifndef WITHOUT_FILE_SYSTEM
944   if(cfc) {
945     cfc->chk_fn = chk_fn;
946     cfc->chk_data = chk_data;
947   };
948 #endif
949 }
950 
951 /*.......................................................................
952  * The following CplCheckFn callback returns non-zero if the specified
953  * filename is that of an executable.
954  */
CPL_CHECK_FN(cpl_check_exe)955 CPL_CHECK_FN(cpl_check_exe)
956 {
957 #ifdef WITHOUT_FILE_SYSTEM
958   return 0;
959 #else
960   return _pu_path_is_exe(pathname);
961 #endif
962 }
963 
964 /*.......................................................................
965  * Remove duplicates from a sorted array of matches.
966  *
967  * Input:
968  *  cpl   WordCompletion *  The completion resource object.
969  */
cpl_zap_duplicates(WordCompletion * cpl)970 static void cpl_zap_duplicates(WordCompletion *cpl)
971 {
972   CplMatch *matches;       /* The array of matches */
973   int nmatch;              /* The number of elements in matches[] */
974   const char *completion;  /* The completion string of the last unique match */
975   const char *type_suffix; /* The type of the last unique match */
976   int src;                 /* The index of the match being considered */
977   int dst;                 /* The index at which to record the next */
978                            /*  unique match. */
979 /*
980  * Get the array of matches and the number of matches that it
981  * contains.
982  */
983   matches = cpl->result.matches;
984   nmatch = cpl->result.nmatch;
985 /*
986  * No matches?
987  */
988   if(nmatch < 1)
989     return;
990 /*
991  * Initialize the comparison strings with the first match.
992  */
993   completion = matches[0].completion;
994   type_suffix = matches[0].type_suffix;
995 /*
996  * Go through the array of matches, copying each new unrecorded
997  * match at the head of the array, while discarding duplicates.
998  */
999   for(src=dst=1; src<nmatch; src++) {
1000     CplMatch *match = matches + src;
1001     if(strcmp(completion, match->completion) != 0 ||
1002        strcmp(type_suffix, match->type_suffix) != 0) {
1003       if(src != dst)
1004 	matches[dst] = *match;
1005       dst++;
1006       completion = match->completion;
1007       type_suffix = match->type_suffix;
1008     };
1009   };
1010 /*
1011  * Record the number of unique matches that remain.
1012  */
1013   cpl->result.nmatch = dst;
1014   return;
1015 }
1016 
1017 /*.......................................................................
1018  * Work out how to arrange a given array of completions into a listing
1019  * of one or more fixed size columns.
1020  *
1021  * Input:
1022  *  result   CplMatches *   The set of completions to be listed.
1023  *  term_width      int     The width of the terminal. A lower limit of
1024  *                          zero is quietly enforced.
1025  * Input/Output:
1026  *  fmt   CplListFormat *   The formatting information will be assigned
1027  *                          to the members of *fmt.
1028  */
cpl_plan_listing(CplMatches * result,int term_width,CplListFormat * fmt)1029 static void cpl_plan_listing(CplMatches *result, int term_width,
1030 			     CplListFormat *fmt)
1031 {
1032   int maxlen;    /* The length of the longest matching string */
1033   int i;
1034 /*
1035  * Ensure that term_width >= 0.
1036  */
1037   if(term_width < 0)
1038     term_width = 0;
1039 /*
1040  * Start by assuming the worst case, that either nothing will fit
1041  * on the screen, or that there are no matches to be listed.
1042  */
1043   fmt->term_width = term_width;
1044   fmt->column_width = 0;
1045   fmt->nline = fmt->ncol = 0;
1046 /*
1047  * Work out the maximum length of the matching strings.
1048  */
1049   maxlen = 0;
1050   for(i=0; i<result->nmatch; i++) {
1051     CplMatch *match = result->matches + i;
1052     int len = strlen(match->completion) + strlen(match->type_suffix);
1053     if(len > maxlen)
1054       maxlen = len;
1055   };
1056 /*
1057  * Nothing to list?
1058  */
1059   if(maxlen == 0)
1060     return;
1061 /*
1062  * Split the available terminal width into columns of
1063  * maxlen + CPL_COL_SEP characters.
1064  */
1065   fmt->column_width = maxlen;
1066   fmt->ncol = fmt->term_width / (fmt->column_width + CPL_COL_SEP);
1067 /*
1068  * If the column width is greater than the terminal width, zero columns
1069  * will have been selected. Set a lower limit of one column. Leave it
1070  * up to the caller how to deal with completions who's widths exceed
1071  * the available terminal width.
1072  */
1073   if(fmt->ncol < 1)
1074     fmt->ncol = 1;
1075 /*
1076  * How many lines of output will be needed?
1077  */
1078   fmt->nline = (result->nmatch + fmt->ncol - 1) / fmt->ncol;
1079   return;
1080 }
1081 
1082 /*.......................................................................
1083  * Render one line of a multi-column listing of completions, using a
1084  * callback function to pass the output to an arbitrary destination.
1085  *
1086  * Input:
1087  *  result      CplMatches *  The container of the sorted array of
1088  *                            completions.
1089  *  fmt      CplListFormat *  Formatting information.
1090  *  lnum               int    The index of the line to print, starting
1091  *                            from 0, and incrementing until the return
1092  *                            value indicates that there is nothing more
1093  *                            to be printed.
1094  *  write_fn     GlWriteFn *  The function to call to write the line, or
1095  *                            0 to discard the output.
1096  *  data              void *  Anonymous data to pass to write_fn().
1097  * Output:
1098  *  return             int    0 - Line printed ok.
1099  *                            1 - Nothing to print.
1100  */
cpl_format_line(CplMatches * result,CplListFormat * fmt,int lnum,GlWriteFn * write_fn,void * data)1101 static int cpl_format_line(CplMatches *result, CplListFormat *fmt, int lnum,
1102 			   GlWriteFn *write_fn, void *data)
1103 {
1104   int col;             /* The index of the list column being output */
1105 /*
1106  * If the line index is out of bounds, there is nothing to be written.
1107  */
1108   if(lnum < 0 || lnum >= fmt->nline)
1109     return 1;
1110 /*
1111  * If no output function has been provided, return as though the
1112  * line had been printed.
1113  */
1114   if(!write_fn)
1115     return 0;
1116 /*
1117  * Print the matches in 'ncol' columns, sorted in line order within each
1118  * column.
1119  */
1120   for(col=0; col < fmt->ncol; col++) {
1121     int m = col*fmt->nline + lnum;
1122 /*
1123  * Is there another match to be written? Note that in general
1124  * the last line of a listing will have fewer filled columns
1125  * than the initial lines.
1126  */
1127     if(m < result->nmatch) {
1128       CplMatch *match = result->matches + m;
1129 /*
1130  * How long are the completion and type-suffix strings?
1131  */
1132       int clen = strlen(match->completion);
1133       int tlen = strlen(match->type_suffix);
1134 /*
1135  * Write the completion string.
1136  */
1137       if(write_fn(data, match->completion, clen) != clen)
1138 	return 1;
1139 /*
1140  * Write the type suffix, if any.
1141  */
1142       if(tlen > 0 && write_fn(data, match->type_suffix, tlen) != tlen)
1143 	return 1;
1144 /*
1145  * If another column follows the current one, pad to its start with spaces.
1146  */
1147       if(col+1 < fmt->ncol) {
1148 /*
1149  * The following constant string of spaces is used to pad the output.
1150  */
1151 	static const char spaces[] = "                    ";
1152 	static const int nspace = sizeof(spaces) - 1;
1153 /*
1154  * Pad to the next column, using as few sub-strings of the spaces[]
1155  * array as possible.
1156  */
1157 	int npad = fmt->column_width + CPL_COL_SEP - clen - tlen;
1158 	while(npad>0) {
1159 	  int n = npad > nspace ? nspace : npad;
1160 	  if(write_fn(data, spaces + nspace - n, n) != n)
1161 	    return 1;
1162 	  npad -= n;
1163 	};
1164       };
1165     };
1166   };
1167 /*
1168  * Start a new line.
1169  */
1170   {
1171     char s[] = "\r\n";
1172     int n = strlen(s);
1173     if(write_fn(data, s, n) != n)
1174       return 1;
1175   };
1176   return 0;
1177 }
1178