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