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