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