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