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