~mathiaz/ubuntu/lucid/mysql-dfsg-5.1/zap-bug-552053

« back to all changes in this revision

Viewing changes to cmd-line-utils/libedit/filecomplete.c

  • Committer: Bazaar Package Importer
  • Author(s): Mathias Gug
  • Date: 2009-06-25 12:55:45 UTC
  • mfrom: (1.1.2 upstream) (0.1.3 experimental)
  • Revision ID: james.westby@ubuntu.com-20090625125545-m8ogs96zzsri74xe
Tags: 5.1.34-1ubuntu1
* Merge from debian experimental (and 5.0 from main), remaining changes:
  - debian/mysql-server-5.1.config:
    + ask for MySQL root password at priority high instead of medium so
      that the password prompt is seen on a default install. (LP: #319843)
    + don't ask for root password when upgrading from a 5.0 install.
  - debian/control:
    + Make libmysqlclient16-dev a transitional package depending on
      libmysqlclient-dev.
    + Make libmysqlclient-dev conflict with libmysqlclient15-dev.
    + Don't build mysql-server, mysql-client, mysql-common and
      libmysqlclient15-dev binary packages since they're still provided
      by mysql-dfsg-5.0.
    + Make mysql-{client,server}-5.1 packages conflict and
      replace mysql-{client,server}-5.0, but not provide
      mysql-{client,server}.
    + Depend on a specific version of mysql-common rather than the src
      version of mysql-dfsg-5.1 since mysql-common is currently part of
      mysql-dfsg-5.0.
    + Lower mailx from a Recommends to a Suggests to avoid pulling in
      a full MTA on all installs of mysql-server. (LP: #259477)
  - debian/rules:
    + added -fno-strict-aliasing to CFLAGS to get around mysql testsuite
      build failures.
    + install mysql-test and sql-bench to /usr/share/mysql/ rather than
      /usr/.
  - debian/additions/debian-start.inc.sh: support ANSI mode (LP: #310211)
  - Add AppArmor profile:
    - debian/apparmor-profile: apparmor profile.
    - debian/rules, debian/mysql-server-5.0.files: install apparmor profile.
    - debian/mysql-server-5.0.dirs: add etc/apparmor.d/force-complain
    - debian/mysql-server-5.0.postrm: remove symlink in force-complain/ on
      purge.
    - debian/mysql-server-5.1.README.Debian: add apparmor documentation.
    - debian/additions/my.cnf: Add warning about apparmor. (LP: #201799)
    - debian/mysql-server-5.1.postinst: reload apparmor profiles.
  - debian/additions/my.cnf: remove language option. Error message files are
    located in a different directory in MySQL 5.0. Setting the language
    option to use /usr/share/mysql/english breaks 5.0. Both 5.0 and 5.1
    use a default value that works. (LP: #316974)
  - debian/mysql-server-5.1.mysql.init:
    + Clearly indicate that we do not support running multiple instances
      of mysqld by duplicating the init script.
      (closes: #314785, #324834, #435165, #444216)
    + Properly parameterize all existing references to the mysql config
      file (/etc/mysql/my.cnf).
  - debian/mysql-server-5.0.postinst: Clear out the second password
    when setting up mysql. (LP: #344816)
  - mysql-server-core-5.1 package for files needed by Akonadi:
    + debian/control: create mysql-server-core-5.1 package.
    + debian/mysql-server-core-5.1.files, debian/mysql-server-5.1.files:
      move core mysqld files to mysql-server-core-5.1 package.
  - Don't package sql-bench and mysql-test file.
* Dropped changes:
  - debian/patches/92_ssl_test_cert.dpatch: certificate expiration in
    test suite (LP: #323755). Included upstream.
* Dropped from 5.0:
  - apparmor profile:
    - debian/control: Recommends apparmor >= 2.1+1075-0ubuntu6. All version
      of apparmor-profile (>hardy) are higher than this version.
    - debian/mysql-server-5.0.preinst: create symlink for force-complain/
      on pre-feisty upgrades, upgrades where apparmor-profiles profile is
      unchanged (ie non-enforcing) and upgrades where the profile
      doesn't exist. Support for pre-hardy upgrades is no longer needed.
* debian/mysql-server-5.1.postinst: fix debian-sys-maint user creation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*      $NetBSD: filecomplete.c,v 1.13 2009/01/26 17:32:41 apb Exp $    */
 
2
 
 
3
/*-
 
4
 * Copyright (c) 1997 The NetBSD Foundation, Inc.
 
5
 * All rights reserved.
 
6
 *
 
7
 * This code is derived from software contributed to The NetBSD Foundation
 
8
 * by Jaromir Dolecek.
 
9
 *
 
10
 * Redistribution and use in source and binary forms, with or without
 
11
 * modification, are permitted provided that the following conditions
 
12
 * are met:
 
13
 * 1. Redistributions of source code must retain the above copyright
 
14
 *    notice, this list of conditions and the following disclaimer.
 
15
 * 2. Redistributions in binary form must reproduce the above copyright
 
16
 *    notice, this list of conditions and the following disclaimer in the
 
17
 *    documentation and/or other materials provided with the distribution.
 
18
 *
 
19
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 
20
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 
21
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
22
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 
23
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
24
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
25
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
26
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
27
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
28
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
29
 * POSSIBILITY OF SUCH DAMAGE.
 
30
 */
 
31
 
 
32
/* AIX requires this to be the first thing in the file.  */
 
33
#if defined (_AIX) && !defined (__GNUC__)
 
34
 #pragma alloca
 
35
#endif
 
36
 
 
37
#include "config.h"
 
38
 
 
39
/* XXXMYSQL */
 
40
#ifdef __GNUC__
 
41
# undef alloca
 
42
# define alloca(n) __builtin_alloca (n)
 
43
#else
 
44
# ifdef HAVE_ALLOCA_H
 
45
#  include <alloca.h>
 
46
# else
 
47
#  ifndef _AIX
 
48
extern char *alloca ();
 
49
#  endif
 
50
# endif
 
51
#endif
 
52
 
 
53
#if !defined(lint) && !defined(SCCSID)
 
54
#endif /* not lint && not SCCSID */
 
55
 
 
56
#include <sys/types.h>
 
57
#include <sys/stat.h>
 
58
#include <stdio.h>
 
59
#include <dirent.h>
 
60
#include <string.h>
 
61
#include <pwd.h>
 
62
#include <ctype.h>
 
63
#include <stdlib.h>
 
64
#include <unistd.h>
 
65
#include <limits.h>
 
66
#include <errno.h>
 
67
#include <fcntl.h>
 
68
#ifdef HAVE_VIS_H
 
69
#include <vis.h>
 
70
#else
 
71
#include "np/vis.h"
 
72
#endif
 
73
#ifdef HAVE_ALLOCA_H
 
74
#include <alloca.h>
 
75
#endif
 
76
#include "el.h"
 
77
#include "fcns.h"               /* for EL_NUM_FCNS */
 
78
#include "histedit.h"
 
79
#include "filecomplete.h"
 
80
 
 
81
static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$',
 
82
    '>', '<', '=', ';', '|', '&', '{', '(', '\0' };
 
83
 
 
84
 
 
85
/********************************/
 
86
/* completion functions */
 
87
 
 
88
/*
 
89
 * does tilde expansion of strings of type ``~user/foo''
 
90
 * if ``user'' isn't valid user name or ``txt'' doesn't start
 
91
 * w/ '~', returns pointer to strdup()ed copy of ``txt''
 
92
 *
 
93
 * it's callers's responsibility to free() returned string
 
94
 */
 
95
char *
 
96
fn_tilde_expand(const char *txt)
 
97
{
 
98
        struct passwd pwres, *pass;
 
99
        char *temp;
 
100
        size_t len = 0;
 
101
        char pwbuf[1024];
 
102
 
 
103
        if (txt[0] != '~')
 
104
                return (strdup(txt));
 
105
 
 
106
        temp = strchr(txt + 1, '/');
 
107
        if (temp == NULL) {
 
108
                temp = strdup(txt + 1);
 
109
                if (temp == NULL)
 
110
                        return NULL;
 
111
        } else {
 
112
                len = temp - txt + 1;   /* text until string after slash */
 
113
                temp = malloc(len);
 
114
                if (temp == NULL)
 
115
                        return NULL;
 
116
                (void)strncpy(temp, txt + 1, len - 2);
 
117
                temp[len - 2] = '\0';
 
118
        }
 
119
        /* XXXMYSQL: use non-_r functions for now */
 
120
        if (temp[0] == 0) {
 
121
                pass = getpwuid(getuid());
 
122
        } else {
 
123
                pass = getpwnam(temp);
 
124
        }
 
125
        free(temp);             /* value no more needed */
 
126
        if (pass == NULL)
 
127
                return (strdup(txt));
 
128
 
 
129
        /* update pointer txt to point at string immedially following */
 
130
        /* first slash */
 
131
        txt += len;
 
132
 
 
133
        temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1);
 
134
        if (temp == NULL)
 
135
                return NULL;
 
136
        (void)sprintf(temp, "%s/%s", pass->pw_dir, txt);
 
137
 
 
138
        return (temp);
 
139
}
 
140
 
 
141
 
 
142
/*
 
143
 * return first found file name starting by the ``text'' or NULL if no
 
144
 * such file can be found
 
145
 * value of ``state'' is ignored
 
146
 *
 
147
 * it's caller's responsibility to free returned string
 
148
 */
 
149
char *
 
150
fn_filename_completion_function(const char *text, int state)
 
151
{
 
152
        static DIR *dir = NULL;
 
153
        static char *filename = NULL, *dirname = NULL, *dirpath = NULL;
 
154
        static size_t filename_len = 0;
 
155
        struct dirent *entry;
 
156
        char *temp;
 
157
        size_t len;
 
158
 
 
159
        if (state == 0 || dir == NULL) {
 
160
                temp = strrchr(text, '/');
 
161
                if (temp) {
 
162
                        char *nptr;
 
163
                        temp++;
 
164
                        nptr = realloc(filename, strlen(temp) + 1);
 
165
                        if (nptr == NULL) {
 
166
                                free(filename);
 
167
                                return NULL;
 
168
                        }
 
169
                        filename = nptr;
 
170
                        (void)strcpy(filename, temp);
 
171
                        len = temp - text;      /* including last slash */
 
172
                        nptr = realloc(dirname, len + 1);
 
173
                        if (nptr == NULL) {
 
174
                                free(filename);
 
175
                                return NULL;
 
176
                        }
 
177
                        dirname = nptr;
 
178
                        (void)strncpy(dirname, text, len);
 
179
                        dirname[len] = '\0';
 
180
                } else {
 
181
                        if (*text == 0)
 
182
                                filename = NULL;
 
183
                        else {
 
184
                                filename = strdup(text);
 
185
                                if (filename == NULL)
 
186
                                        return NULL;
 
187
                        }
 
188
                        dirname = NULL;
 
189
                }
 
190
 
 
191
                if (dir != NULL) {
 
192
                        (void)closedir(dir);
 
193
                        dir = NULL;
 
194
                }
 
195
 
 
196
                /* support for ``~user'' syntax */
 
197
                free(dirpath);
 
198
 
 
199
                if (dirname == NULL && (dirname = strdup("./")) == NULL)
 
200
                        return NULL;
 
201
 
 
202
                if (*dirname == '~')
 
203
                        dirpath = fn_tilde_expand(dirname);
 
204
                else
 
205
                        dirpath = strdup(dirname);
 
206
 
 
207
                if (dirpath == NULL)
 
208
                        return NULL;
 
209
 
 
210
                dir = opendir(dirpath);
 
211
                if (!dir)
 
212
                        return (NULL);  /* cannot open the directory */
 
213
 
 
214
                /* will be used in cycle */
 
215
                filename_len = filename ? strlen(filename) : 0;
 
216
        }
 
217
 
 
218
        /* find the match */
 
219
        while ((entry = readdir(dir)) != NULL) {
 
220
                /* skip . and .. */
 
221
                if (entry->d_name[0] == '.' && (!entry->d_name[1]
 
222
                    || (entry->d_name[1] == '.' && !entry->d_name[2])))
 
223
                        continue;
 
224
                if (filename_len == 0)
 
225
                        break;
 
226
                /* otherwise, get first entry where first */
 
227
                /* filename_len characters are equal      */
 
228
                if (entry->d_name[0] == filename[0]
 
229
#if HAVE_STRUCT_DIRENT_D_NAMLEN
 
230
                    && entry->d_namlen >= filename_len
 
231
#else
 
232
                    && strlen(entry->d_name) >= filename_len
 
233
#endif
 
234
                    && strncmp(entry->d_name, filename,
 
235
                        filename_len) == 0)
 
236
                        break;
 
237
        }
 
238
 
 
239
        if (entry) {            /* match found */
 
240
 
 
241
#if HAVE_STRUCT_DIRENT_D_NAMLEN
 
242
                len = entry->d_namlen;
 
243
#else
 
244
                len = strlen(entry->d_name);
 
245
#endif
 
246
 
 
247
                temp = malloc(strlen(dirname) + len + 1);
 
248
                if (temp == NULL)
 
249
                        return NULL;
 
250
                (void)sprintf(temp, "%s%s", dirname, entry->d_name);
 
251
        } else {
 
252
                (void)closedir(dir);
 
253
                dir = NULL;
 
254
                temp = NULL;
 
255
        }
 
256
 
 
257
        return (temp);
 
258
}
 
259
 
 
260
 
 
261
static const char *
 
262
append_char_function(const char *name)
 
263
{
 
264
        struct stat stbuf;
 
265
        char *expname = *name == '~' ? fn_tilde_expand(name) : NULL;
 
266
        const char *rs = " ";
 
267
 
 
268
        if (stat(expname ? expname : name, &stbuf) == -1)
 
269
                goto out;
 
270
        if (S_ISDIR(stbuf.st_mode))
 
271
                rs = "/";
 
272
out:
 
273
        if (expname)
 
274
                free(expname);
 
275
        return rs;
 
276
}
 
277
/*
 
278
 * returns list of completions for text given
 
279
 * non-static for readline.
 
280
 */
 
281
char ** completion_matches(const char *, char *(*)(const char *, int));
 
282
char **
 
283
completion_matches(const char *text, char *(*genfunc)(const char *, int))
 
284
{
 
285
        char **match_list = NULL, *retstr, *prevstr;
 
286
        size_t match_list_len, max_equal, which, i;
 
287
        size_t matches;
 
288
 
 
289
        matches = 0;
 
290
        match_list_len = 1;
 
291
        while ((retstr = (*genfunc) (text, (int)matches)) != NULL) {
 
292
                /* allow for list terminator here */
 
293
                if (matches + 3 >= match_list_len) {
 
294
                        char **nmatch_list;
 
295
                        while (matches + 3 >= match_list_len)
 
296
                                match_list_len <<= 1;
 
297
                        nmatch_list = realloc(match_list,
 
298
                            match_list_len * sizeof(char *));
 
299
                        if (nmatch_list == NULL) {
 
300
                                free(match_list);
 
301
                                return NULL;
 
302
                        }
 
303
                        match_list = nmatch_list;
 
304
 
 
305
                }
 
306
                match_list[++matches] = retstr;
 
307
        }
 
308
 
 
309
        if (!match_list)
 
310
                return NULL;    /* nothing found */
 
311
 
 
312
        /* find least denominator and insert it to match_list[0] */
 
313
        which = 2;
 
314
        prevstr = match_list[1];
 
315
        max_equal = strlen(prevstr);
 
316
        for (; which <= matches; which++) {
 
317
                for (i = 0; i < max_equal &&
 
318
                    prevstr[i] == match_list[which][i]; i++)
 
319
                        continue;
 
320
                max_equal = i;
 
321
        }
 
322
 
 
323
        retstr = malloc(max_equal + 1);
 
324
        if (retstr == NULL) {
 
325
                free(match_list);
 
326
                return NULL;
 
327
        }
 
328
        (void)strncpy(retstr, match_list[1], max_equal);
 
329
        retstr[max_equal] = '\0';
 
330
        match_list[0] = retstr;
 
331
 
 
332
        /* add NULL as last pointer to the array */
 
333
        match_list[matches + 1] = (char *) NULL;
 
334
 
 
335
        return (match_list);
 
336
}
 
337
 
 
338
/*
 
339
 * Sort function for qsort(). Just wrapper around strcasecmp().
 
340
 */
 
341
static int
 
342
_fn_qsort_string_compare(const void *i1, const void *i2)
 
343
{
 
344
        const char *s1 = ((const char * const *)i1)[0];
 
345
        const char *s2 = ((const char * const *)i2)[0];
 
346
 
 
347
        return strcasecmp(s1, s2);
 
348
}
 
349
 
 
350
/*
 
351
 * Display list of strings in columnar format on readline's output stream.
 
352
 * 'matches' is list of strings, 'len' is number of strings in 'matches',
 
353
 * 'max' is maximum length of string in 'matches'.
 
354
 */
 
355
void
 
356
fn_display_match_list (EditLine *el, char **matches, int len, int max)
 
357
{
 
358
        int i, idx, limit, count;
 
359
        int screenwidth = el->el_term.t_size.h;
 
360
 
 
361
        /*
 
362
         * Find out how many entries can be put on one line, count
 
363
         * with two spaces between strings.
 
364
         */
 
365
        limit = screenwidth / (max + 2);
 
366
        if (limit == 0)
 
367
                limit = 1;
 
368
 
 
369
        /* how many lines of output */
 
370
        count = len / limit;
 
371
        if (count * limit < len)
 
372
                count++;
 
373
 
 
374
        /* Sort the items if they are not already sorted. */
 
375
        qsort(&matches[1], (size_t)(len - 1), sizeof(char *),
 
376
            _fn_qsort_string_compare);
 
377
 
 
378
        idx = 1;
 
379
        for(; count > 0; count--) {
 
380
                for(i = 0; i < limit && matches[idx]; i++, idx++)
 
381
                        (void)fprintf(el->el_outfile, "%-*s  ", max,
 
382
                            matches[idx]);
 
383
                (void)fprintf(el->el_outfile, "\n");
 
384
        }
 
385
}
 
386
 
 
387
/*
 
388
 * Complete the word at or before point,
 
389
 * 'what_to_do' says what to do with the completion.
 
390
 * \t   means do standard completion.
 
391
 * `?' means list the possible completions.
 
392
 * `*' means insert all of the possible completions.
 
393
 * `!' means to do standard completion, and list all possible completions if
 
394
 * there is more than one.
 
395
 *
 
396
 * Note: '*' support is not implemented
 
397
 *       '!' could never be invoked
 
398
 */
 
399
int
 
400
fn_complete(EditLine *el,
 
401
        char *(*complet_func)(const char *, int),
 
402
        char **(*attempted_completion_function)(const char *, int, int),
 
403
        const char *word_break, const char *special_prefixes,
 
404
        const char *(*app_func)(const char *), int query_items,
 
405
        int *completion_type, int *over, int *point, int *end)
 
406
{
 
407
        const LineInfo *li;
 
408
        char *temp, **matches;
 
409
        const char *ctemp;
 
410
        size_t len;
 
411
        int what_to_do = '\t';
 
412
        int retval = CC_NORM;
 
413
 
 
414
        if (el->el_state.lastcmd == el->el_state.thiscmd)
 
415
                what_to_do = '?';
 
416
 
 
417
        /* readline's rl_complete() has to be told what we did... */
 
418
        if (completion_type != NULL)
 
419
                *completion_type = what_to_do;
 
420
 
 
421
        if (!complet_func)
 
422
                complet_func = fn_filename_completion_function;
 
423
        if (!app_func)
 
424
                app_func = append_char_function;
 
425
 
 
426
        /* We now look backwards for the start of a filename/variable word */
 
427
        li = el_line(el);
 
428
        ctemp = (const char *) li->cursor;
 
429
        while (ctemp > li->buffer
 
430
            && !strchr(word_break, ctemp[-1])
 
431
            && (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) )
 
432
                ctemp--;
 
433
 
 
434
        len = li->cursor - ctemp;
 
435
#if defined(__SSP__) || defined(__SSP_ALL__)
 
436
        temp = malloc(len + 1);
 
437
#else
 
438
        temp = alloca(len + 1);
 
439
#endif
 
440
        (void)strncpy(temp, ctemp, len);
 
441
        temp[len] = '\0';
 
442
 
 
443
        /* these can be used by function called in completion_matches() */
 
444
        /* or (*attempted_completion_function)() */
 
445
        if (point != 0)
 
446
                *point = li->cursor - li->buffer;
 
447
        if (end != NULL)
 
448
                *end = li->lastchar - li->buffer;
 
449
 
 
450
        if (attempted_completion_function) {
 
451
                int cur_off = li->cursor - li->buffer;
 
452
                matches = (*attempted_completion_function) (temp,
 
453
                    (int)(cur_off - len), cur_off);
 
454
        } else
 
455
                matches = 0;
 
456
        if (!attempted_completion_function || 
 
457
            (over != NULL && !*over && !matches))
 
458
                matches = completion_matches(temp, complet_func);
 
459
 
 
460
        if (over != NULL)
 
461
                *over = 0;
 
462
 
 
463
        if (matches) {
 
464
                int i;
 
465
                int matches_num, maxlen, match_len, match_display=1;
 
466
 
 
467
                retval = CC_REFRESH;
 
468
                /*
 
469
                 * Only replace the completed string with common part of
 
470
                 * possible matches if there is possible completion.
 
471
                 */
 
472
                if (matches[0][0] != '\0') {
 
473
                        el_deletestr(el, (int) len);
 
474
                        el_insertstr(el, matches[0]);
 
475
                }
 
476
 
 
477
                if (what_to_do == '?')
 
478
                        goto display_matches;
 
479
 
 
480
                if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) {
 
481
                        /*
 
482
                         * We found exact match. Add a space after
 
483
                         * it, unless we do filename completion and the
 
484
                         * object is a directory.
 
485
                         */
 
486
                        el_insertstr(el, (*app_func)(matches[0])); 
 
487
                } else if (what_to_do == '!') {
 
488
    display_matches:
 
489
                        /*
 
490
                         * More than one match and requested to list possible
 
491
                         * matches.
 
492
                         */
 
493
 
 
494
                        for(i=1, maxlen=0; matches[i]; i++) {
 
495
                                match_len = strlen(matches[i]);
 
496
                                if (match_len > maxlen)
 
497
                                        maxlen = match_len;
 
498
                        }
 
499
                        matches_num = i - 1;
 
500
                                
 
501
                        /* newline to get on next line from command line */
 
502
                        (void)fprintf(el->el_outfile, "\n");
 
503
 
 
504
                        /*
 
505
                         * If there are too many items, ask user for display
 
506
                         * confirmation.
 
507
                         */
 
508
                        if (matches_num > query_items) {
 
509
                                (void)fprintf(el->el_outfile,
 
510
                                    "Display all %d possibilities? (y or n) ",
 
511
                                    matches_num);
 
512
                                (void)fflush(el->el_outfile);
 
513
                                if (getc(stdin) != 'y')
 
514
                                        match_display = 0;
 
515
                                (void)fprintf(el->el_outfile, "\n");
 
516
                        }
 
517
 
 
518
                        if (match_display)
 
519
                                fn_display_match_list(el, matches, matches_num,
 
520
                                        maxlen);
 
521
                        retval = CC_REDISPLAY;
 
522
                } else if (matches[0][0]) {
 
523
                        /*
 
524
                         * There was some common match, but the name was
 
525
                         * not complete enough. Next tab will print possible
 
526
                         * completions.
 
527
                         */
 
528
                        el_beep(el);
 
529
                } else {
 
530
                        /* lcd is not a valid object - further specification */
 
531
                        /* is needed */
 
532
                        el_beep(el);
 
533
                        retval = CC_NORM;
 
534
                }
 
535
 
 
536
                /* free elements of array and the array itself */
 
537
                for (i = 0; matches[i]; i++)
 
538
                        free(matches[i]);
 
539
                free(matches);
 
540
                matches = NULL;
 
541
        }
 
542
#if defined(__SSP__) || defined(__SSP_ALL__)
 
543
        free(temp);
 
544
#endif
 
545
        return retval;
 
546
}
 
547
 
 
548
/*
 
549
 * el-compatible wrapper around rl_complete; needed for key binding
 
550
 */
 
551
/* ARGSUSED */
 
552
unsigned char
 
553
_el_fn_complete(EditLine *el, int ch __attribute__((__unused__)))
 
554
{
 
555
        return (unsigned char)fn_complete(el, NULL, NULL,
 
556
            break_chars, NULL, NULL, 100,
 
557
            NULL, NULL, NULL, NULL);
 
558
}