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