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