1 /* $NetBSD: complete.c,v 1.18 2009/02/11 19:22:22 christos Exp $ */
4 * Copyright (c) 1997-2000,2005,2006 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
33 * Most of this is derived or copied from src/usr.bin/ftp/complete.c (1.41).
38 #include <sys/cdefs.h>
40 __RCSID("$NetBSD: complete.c,v 1.18 2009/02/11 19:22:22 christos Exp $");
44 * FTP user program - command and file completion routines
55 #include <stringlist.h>
59 #include <sys/param.h>
62 #include "rcv.h" /* includes "glob.h" */
78 static int doglob
= 1; /* glob local file names */
81 #define ttywidth screenwidth /* in "glob.h" */
82 #define ttyheight screenheight /* in "glob.h" */
84 /************************************************************************/
85 /* from src/usr.bin/ftp/utils.h (1.135) - begin */
88 * List words in stringlist, vertically arranged
91 list_vertical(StringList
*sl
)
94 size_t i
, j
, columns
, lines
;
100 for (i
= 0; i
< sl
->sl_cur
; i
++) {
101 w
= strlen(sl
->sl_str
[i
]);
105 width
= (width
+ 8) &~ 7;
107 columns
= ttywidth
/ width
;
110 lines
= (sl
->sl_cur
+ columns
- 1) / columns
;
112 for (i
= 0; i
< lines
; i
++) {
113 for (j
= 0; j
< columns
; j
++) {
114 p
= sl
->sl_str
[j
* lines
+ i
];
116 (void)fputs(p
, ttyout
);
117 if (j
* lines
+ i
+ lines
>= sl
->sl_cur
) {
118 (void)putc('\n', ttyout
);
125 (void)putc('\t', ttyout
);
129 if (ttyheight
> 2 && ++k
== ttyheight
- 2) {
132 (void)fputs("--more--", ttyout
);
133 while ((ch
= getchar()) != EOF
&& ch
!= ' ' && ch
!= 'q')
134 (void)putc(BELL
, ttyout
);
135 (void)fputs("\r \r", ttyout
);
143 * Copy characters from src into dst, \ quoting characters that require it
146 ftpvis(char *dst
, size_t dstlen
, const char *src
, size_t srclen
)
151 src
[si
] != '\0' && di
< dstlen
&& si
< srclen
;
172 * sl_init() with inbuilt error checking
181 err(1, "Unable to allocate memory for stringlist");
187 * sl_add() with inbuilt error checking
190 mail_sl_add(StringList
*sl
, char *i
)
193 if (sl_add(sl
, i
) == -1)
194 err(1, "Unable to add `%s' to stringlist", i
);
199 * Glob a local file name specification with the expectation of a single
200 * return value. Can't control multiple values being expanded from the
201 * expression, we return only the first.
202 * Returns NULL on error, or a pointer to a buffer containing the filename
203 * that's the caller's responsiblity to free(3) when finished with.
206 globulize(const char *pattern
)
213 return estrdup(pattern
);
215 flags
= GLOB_BRACE
|GLOB_NOCHECK
|GLOB_TILDE
;
216 (void)memset(&gl
, 0, sizeof(gl
));
217 if (glob(pattern
, flags
, NULL
, &gl
) || gl
.gl_pathc
== 0) {
218 warnx("%s: not found", pattern
);
222 p
= estrdup(gl
.gl_pathv
[0]);
227 /* from src/usr.bin/ftp/utils.h (1.135) - end */
228 /************************************************************************/
231 comparstr(const void *a
, const void *b
)
233 return strcmp(*(const char * const *)a
, *(const char * const *)b
);
237 * Determine if complete is ambiguous. If unique, insert.
238 * If no choices, error. If unambiguous prefix, insert that.
239 * Otherwise, list choices. words is assumed to be filtered
240 * to only contain possible choices.
242 * word word which started the match
243 * dolist list by default
244 * words stringlist containing possible matches
245 * Returns a result as per el_set(EL_ADDFN, ...)
248 complete_ambiguous(EditLine
*el
, char *word
, int dolist
, StringList
*words
)
250 char insertstr
[MAXPATHLEN
];
252 size_t i
, j
, matchlen
, wordlen
;
254 wordlen
= strlen(word
);
255 if (words
->sl_cur
== 0)
256 return CC_ERROR
; /* no choices available */
258 if (words
->sl_cur
== 1) { /* only once choice available */
259 p
= words
->sl_str
[0] + wordlen
;
260 if (*p
== '\0') /* at end of word? */
262 ftpvis(insertstr
, sizeof(insertstr
), p
, strlen(p
));
263 if (el_insertstr(el
, insertstr
) == -1)
271 lastmatch
= words
->sl_str
[0];
272 matchlen
= strlen(lastmatch
);
273 for (i
= 1; i
< words
->sl_cur
; i
++) {
274 for (j
= wordlen
; j
< strlen(words
->sl_str
[i
]); j
++)
275 if (lastmatch
[j
] != words
->sl_str
[i
][j
])
280 if (matchlen
>= wordlen
) {
281 ftpvis(insertstr
, sizeof(insertstr
),
282 lastmatch
+ wordlen
, matchlen
- wordlen
);
283 if (el_insertstr(el
, insertstr
) == -1)
286 return CC_REFRESH_BEEP
;
290 (void)putc('\n', ttyout
);
291 qsort(words
->sl_str
, words
->sl_cur
, sizeof(char *), comparstr
);
293 list_vertical(words
);
298 * Complete a mail command.
301 complete_command(EditLine
*el
, char *word
, int dolist
)
308 words
= mail_sl_init();
309 wordlen
= strlen(word
);
311 for (c
= cmdtab
; c
->c_name
!= NULL
; c
++) {
312 if (wordlen
> strlen(c
->c_name
))
314 if (strncmp(word
, c
->c_name
, wordlen
) == 0)
315 mail_sl_add(words
, __UNCONST(c
->c_name
));
318 rv
= complete_ambiguous(el
, word
, dolist
, words
);
319 if (rv
== CC_REFRESH
) {
320 if (el_insertstr(el
, " ") == -1)
328 * Complete a local filename.
331 complete_filename(EditLine
*el
, char *word
, int dolist
)
334 char dir
[MAXPATHLEN
];
341 if ((fname
= strrchr(word
, '/')) == NULL
) {
350 len
= fname
- word
+ 1;
351 (void)estrlcpy(dir
, word
, sizeof(dir
));
359 if ((p
= globulize(dir
)) == NULL
)
361 (void)estrlcpy(dir
, p
, sizeof(dir
));
365 if ((dd
= opendir(dir
)) == NULL
)
368 words
= mail_sl_init();
371 for (dp
= readdir(dd
); dp
!= NULL
; dp
= readdir(dd
)) {
372 if (!strcmp(dp
->d_name
, ".") || !strcmp(dp
->d_name
, ".."))
375 #if defined(DIRENT_MISSING_D_NAMLEN)
376 if (len
> strlen(dp
->d_name
))
379 if (len
> dp
->d_namlen
)
382 if (strncmp(fname
, dp
->d_name
, len
) == 0) {
385 tcp
= estrdup(dp
->d_name
);
386 mail_sl_add(words
, tcp
);
391 rv
= complete_ambiguous(el
, fname
, dolist
, words
);
392 if (rv
== CC_REFRESH
) {
394 char path
[MAXPATHLEN
];
396 (void)estrlcpy(path
, dir
, sizeof(path
));
397 (void)estrlcat(path
, "/", sizeof(path
));
398 (void)estrlcat(path
, words
->sl_str
[0], sizeof(path
));
400 if (stat(path
, &sb
) >= 0) {
401 char suffix
[2] = " ";
403 if (S_ISDIR(sb
.st_mode
))
405 if (el_insertstr(el
, suffix
) == -1)
414 find_execs(char *word
, char *path
, StringList
*list
)
420 size_t len
= strlen(word
);
421 uid_t uid
= getuid();
422 gid_t gid
= getgid();
424 for (sep
= dir
; sep
; dir
= sep
+ 1) {
425 if ((sep
=strchr(dir
, ':')) != NULL
) {
429 if ((dd
= opendir(dir
)) == NULL
) {
434 for (dp
= readdir(dd
); dp
!= NULL
; dp
= readdir(dd
)) {
436 if (!strcmp(dp
->d_name
, ".") || !strcmp(dp
->d_name
, ".."))
439 #if defined(DIRENT_MISSING_D_NAMLEN)
440 if (len
> strlen(dp
->d_name
))
443 if (len
> dp
->d_namlen
)
447 if (strncmp(word
, dp
->d_name
, len
) == 0) {
449 char pathname
[ MAXPATHLEN
];
452 (void)snprintf(pathname
, sizeof(pathname
),
453 "%s/%s", dir
, dp
->d_name
);
454 if (stat(pathname
, &sb
) != 0) {
460 if (sb
.st_uid
== uid
) mask
|= 0100;
461 if (sb
.st_gid
== gid
) mask
|= 0010;
463 if ((sb
.st_mode
& mask
) != 0) {
465 tcp
= estrdup(dp
->d_name
);
466 mail_sl_add(list
, tcp
);
480 * Complete a local executable
483 complete_executable(EditLine
*el
, char *word
, int dolist
)
486 char dir
[ MAXPATHLEN
];
492 if ((fname
= strrchr(word
, '/')) == NULL
) {
493 dir
[0] = '\0'; /* walk the path */
501 (void)strncpy(dir
, word
, len
);
502 dir
[fname
- word
] = '\0';
509 if (*dir
== '\0') { /* walk path */
512 env
= getenv("PATH");
514 path
= salloc(len
+ 1);
515 (void)strcpy(path
, env
);
516 error
= find_execs(word
, path
, words
);
518 else { /* check specified dir only */
519 error
= find_execs(word
, dir
, words
);
524 rv
= complete_ambiguous(el
, fname
, dolist
, words
);
525 if (rv
== CC_REFRESH
) {
526 if (el_insertstr(el
, " ") == -1)
536 complete_set(EditLine
*el
, char *word
, int dolist
)
543 size_t len
= strlen(word
);
549 /* allocate space for variables table */
551 for (h
= 0; h
< HSHSIZE
; h
++)
552 for (vp
= variables
[h
]; vp
!= NULL
; vp
= vp
->v_link
)
554 ap
= salloc(s
* sizeof(*ap
));
556 /* save the pointers */
557 for (h
= 0, p
= ap
; h
< HSHSIZE
; h
++)
558 for (vp
= variables
[h
]; vp
!= NULL
; vp
= vp
->v_link
)
562 for (p
= ap
; *p
!= NULL
; p
++)
563 if (len
== 0 || strncmp(*p
, word
, len
) == 0)
564 mail_sl_add(words
, estrdup(*p
));
566 rv
= complete_ambiguous(el
, word
, dolist
, words
);
575 complete_alias(EditLine
*el
, char *word
, int dolist
)
577 struct grouphead
*gh
;
582 size_t len
= strlen(word
);
588 /* allocate space for alias table */
590 for (h
= 0; h
< HSHSIZE
; h
++)
591 for (gh
= groups
[h
]; gh
!= NULL
; gh
= gh
->g_link
)
593 ap
= salloc(s
* sizeof(*ap
));
597 for (h
= 0; h
< HSHSIZE
; h
++)
598 for (gh
= groups
[h
]; gh
!= NULL
; gh
= gh
->g_link
)
604 for (p
= ap
; *p
!= NULL
; p
++)
605 if (len
== 0 || strncmp(*p
, word
, len
) == 0)
606 mail_sl_add(words
, estrdup(*p
));
608 rv
= complete_ambiguous(el
, word
, dolist
, words
);
609 if (rv
== CC_REFRESH
) {
610 if (el_insertstr(el
, " ") == -1)
619 complete_smopts(EditLine
*el
, char *word
, int dolist
)
621 struct grouphead
*gh
;
635 /* count the entries in the smoptstbl and groups (alias) tables */
638 for (h
= 0; h
< HSHSIZE
; h
++) {
639 for (sp
= smoptstbl
[h
]; sp
!= NULL
; sp
= sp
->s_link
)
641 for (gh
= groups
[h
]; gh
!= NULL
; gh
= gh
->g_link
)
645 /* allocate sufficient space for the pointers */
646 ap
= salloc(MAX(s1
, s2
) * sizeof(*ap
));
649 * First do the smoptstbl pointers. (case _insensitive_)
652 for (h
= 0; h
< HSHSIZE
; h
++)
653 for (sp
= smoptstbl
[h
]; sp
!= NULL
; sp
= sp
->s_link
)
657 for (p
= ap
; *p
!= NULL
; p
++)
658 if (len
== 0 || strncasecmp(*p
, word
, len
) == 0)
659 mail_sl_add(words
, estrdup(*p
));
662 * Now do the groups (alias) pointers. (case sensitive)
665 for (h
= 0; h
< HSHSIZE
; h
++)
666 for (gh
= groups
[h
]; gh
!= NULL
; gh
= gh
->g_link
)
670 for (p
= ap
; *p
!= NULL
; p
++)
671 if (len
== 0 || strncmp(*p
, word
, len
) == 0)
672 mail_sl_add(words
, estrdup(*p
));
674 rv
= complete_ambiguous(el
, word
, dolist
, words
);
682 #ifdef THREAD_SUPPORT
684 complete_thread_key(EditLine
*el
, char *word
, int dolist
)
698 /* count the entries in the table */
699 /* XXX - have a function return this rather than counting? */
700 cnt
= 1; /* count the NULL terminator */
702 while (thread_next_key_name(&cookie
) != NULL
)
705 /* allocate sufficient space for the pointers */
706 ap
= salloc(cnt
* sizeof(*ap
));
711 while ((name
= thread_next_key_name(&cookie
)) != NULL
)
715 for (p
= ap
; *p
!= NULL
; p
++)
716 if (len
== 0 || strncmp(*p
, word
, len
) == 0)
717 mail_sl_add(words
, estrdup(*p
));
719 rv
= complete_ambiguous(el
, word
, dolist
, words
);
725 #endif /* THREAD_SUPPORT */
727 /* from /usr/src/usr.bin/ftp/main.c(1.101) - end */
728 /************************************************************************/
730 /* Some people like to bind file completion to CTRL-D. In emacs mode,
731 * CTRL-D is also used to delete the current character, we have to
732 * special case this situation.
734 #define EMACS_CTRL_D_BINDING_HACK
736 #ifdef EMACS_CTRL_D_BINDING_HACK
738 is_emacs_mode(EditLine
*el
)
742 if (el_get(el
, EL_EDITOR
, &mode
) == -1)
744 return equal(mode
, "emacs");
748 emacs_ctrl_d(EditLine
*el
, const LineInfo
*lf
, int ch
)
750 static char delunder
[3] = { CTRL('f'), CTRL('h'), '\0' };
752 if (ch
== CTRL('d') && is_emacs_mode(el
)) { /* CTRL-D is special */
753 if (lf
->buffer
== lf
->lastchar
)
755 if (lf
->cursor
!= lf
->lastchar
) { /* delete without using ^D */
756 el_push(el
, delunder
); /* ^F^H */
762 #endif /* EMACS_CTRL_D_BINDING_HACK */
765 * Check if this is the second request made for this line indicating
766 * the need to list all the completion possibilities.
769 get_dolist(const LineInfo
*lf
)
771 static char last_line
[LINESIZE
];
772 static char *last_cursor_pos
;
777 len
= lf
->lastchar
- lf
->buffer
;
778 if (len
>= sizeof(last_line
) - 1)
781 cursor_pos
= last_line
+ (lf
->cursor
- lf
->buffer
);
783 cursor_pos
== last_cursor_pos
&&
784 strncmp(last_line
, lf
->buffer
, len
) == 0;
786 (void)strlcpy(last_line
, lf
->buffer
, len
+ 1);
787 last_cursor_pos
= cursor_pos
;
793 * Take the full line (lf) including the command and split it into a
794 * sub-line (returned) and a completion context (cmplarray).
797 split_line(const char **cmplarray
, const LineInfo
*lf
)
806 len
= lf
->cursor
- lf
->buffer
;
807 if (len
+ 1 > sizeof(line
))
810 (void)strlcpy(line
, lf
->buffer
, len
+ 1);
812 li
.cursor
= line
+ len
;
813 li
.lastchar
= line
+ len
;
816 cmdname
= get_cmdname(cp
);
817 cp
+= strlen(cmdname
);
819 if (cp
== li
.cursor
) {
829 *cmplarray
= c
->c_complete
;
832 if ((cp2
= shellpr(cp
)) != NULL
) {
834 # define XX(a) ((a) + ((a)[1] == '>' ? 2 : 1))
835 while ((cp2
= shellpr(XX(cp
))) != NULL
)
840 cp
= skip_WSP(cp
+ 1);
844 cp
= skip_WSP(XX(cp
));
855 * Split a sub-line and a completion context into a word and a
856 * completion type. Use the editline tokenizer to handle the quoting
860 split_word(int *cmpltype
, const char *cmplarray
, LineInfo
*li
)
862 static Tokenizer
*t
= NULL
;
873 if ((t
= tok_init(NULL
)) == NULL
)
874 err(EXIT_FAILURE
, "tok_init");
876 if (tok_line(t
, li
, &argc
, &argv
, &cursorc
, &cursoro
) == -1)
877 err(EXIT_FAILURE
, "tok_line");
880 word
= __UNCONST("");
882 word
= salloc((size_t)cursoro
+ 1);
883 (void)strlcpy(word
, argv
[cursorc
], (size_t)cursoro
+ 1);
886 /* check for 'continuation' completes (which are uppercase) */
887 arraylen
= (int)strlen(cmplarray
);
888 if (cursorc
>= arraylen
&&
890 isupper((unsigned char)cmplarray
[arraylen
- 1]))
891 cursorc
= arraylen
- 1;
893 if (cursorc
>= arraylen
)
896 *cmpltype
= cmplarray
[cursorc
];
901 * A generic complete routine for the mail command line.
904 mail_complete(EditLine
*el
, int ch
)
908 const char *cmplarray
;
915 #ifdef EMACS_CTRL_D_BINDING_HACK
918 if ((cc_ret
= emacs_ctrl_d(el
, lf
, ch
)) != -1)
921 #endif /* EMACS_CTRL_D_BINDING_HACK */
923 if ((dolist
= get_dolist(lf
)) == -1)
926 if ((li
= split_line(&cmplarray
, lf
)) == NULL
)
929 if ((word
= split_word(&cmpltype
, cmplarray
, li
)) == NULL
)
933 case 'a': /* alias complete */
935 return complete_alias(el
, word
, dolist
);
937 case 'c': /* command complete */
939 return complete_command(el
, word
, dolist
);
941 case 'f': /* filename complete */
943 return complete_filename(el
, word
, dolist
);
947 return complete_smopts(el
, word
, dolist
);
949 case 'n': /* no complete */
950 case 'N': /* no complete */
955 return complete_set(el
, word
, dolist
);
956 #ifdef THREAD_SUPPORT
959 return complete_thread_key(el
, word
, dolist
);
961 case 'x': /* executable complete */
963 return complete_executable(el
, word
, dolist
);
966 warnx("unknown complete type `%c'", cmpltype
);
968 assert(/*CONSTCOND*/0);
977 * A generic file completion routine.
980 file_complete(EditLine
*el
, int ch
)
982 static char word
[LINESIZE
];
989 #ifdef EMACS_CTRL_D_BINDING_HACK
992 if ((cc_ret
= emacs_ctrl_d(el
, lf
, ch
)) != -1)
995 #endif /* EMACS_CTRL_D_BINDING_HACK */
997 word_len
= lf
->cursor
- lf
->buffer
;
998 if (word_len
+ 1 > sizeof(word
))
1001 (void)strlcpy(word
, lf
->buffer
, word_len
+ 1); /* do not use estrlcpy here! */
1003 if ((dolist
= get_dolist(lf
)) == -1)
1006 return complete_filename(el
, word
, dolist
);
1012 * Complete mime_transfer_encoding type.
1014 static unsigned char
1015 mime_enc_complete(EditLine
*el
, int ch
)
1017 static char word
[LINESIZE
];
1026 #ifdef EMACS_CTRL_D_BINDING_HACK
1029 if ((cc_ret
= emacs_ctrl_d(el
, lf
, ch
)) != -1)
1032 #endif /* EMACS_CTRL_D_BINDING_HACK */
1034 word_len
= lf
->cursor
- lf
->buffer
;
1035 if (word_len
>= sizeof(word
) - 1)
1038 words
= mail_sl_init();
1043 for (ename
= mime_next_encoding_name(&cookie
);
1045 ename
= mime_next_encoding_name(&cookie
))
1046 if (word_len
== 0 ||
1047 strncmp(lf
->buffer
, ename
, word_len
) == 0) {
1049 cp
= estrdup(ename
);
1050 mail_sl_add(words
, cp
);
1053 (void)strlcpy(word
, lf
->buffer
, word_len
+ 1);
1055 if ((dolist
= get_dolist(lf
)) == -1)
1058 rv
= complete_ambiguous(el
, word
, dolist
, words
);
1063 #endif /* MIME_SUPPORT */
1066 /*************************************************************************
1067 * Our public interface to el_gets():
1070 * Initializes of all editline and completion data strutures.
1073 * Displays prompt, calls el_gets() and deals with history.
1074 * Returns the next line of input as a NULL termnated string
1075 * without the trailing newline, or NULL if el_gets() sees is an
1079 static const char *el_prompt
;
1083 show_prompt(EditLine
*e __unused
)
1089 * Write the current INTR character to fp in a friendly form.
1094 struct termios ttybuf
;
1099 if (tcgetattr(fileno(stdin
), &ttybuf
) == -1)
1102 (void)vis(buf
, ttybuf
.c_cc
[VINTR
], VIS_SAFE
| VIS_NOSLASH
, 0);
1103 (void)fprintf(fp
, "%s", buf
);
1108 static sig_t old_sigint
;
1110 comp_intr(int signo
)
1118 my_gets(el_mode_t
*em
, const char *prompt
, char *string
)
1120 static char line
[LINE_MAX
];
1130 el_push(em
->el
, string
);
1133 * Let el_gets() deal with flow control. Also, make sure we
1134 * output a ^C when we get a SIGINT as el_gets() doesn't echo
1137 old_sigint
= sig_signal(SIGINT
, comp_intr
);
1138 buf
= el_gets(em
->el
, &cnt
);
1139 (void)sig_signal(SIGINT
, old_sigint
);
1147 if (buf
[cnt
- 1] == '\n')
1148 cnt
--; /* trash the trailing LF */
1150 len
= MIN(sizeof(line
) - 1, (size_t)cnt
);
1151 (void)memcpy(line
, buf
, len
);
1154 /* enter non-empty lines into history */
1159 if (*p
&& history(em
->hist
, &ev
, H_ENTER
, line
) == 0)
1160 (void)printf("Failed history entry: %s", line
);
1168 const char *el_editor
,
1169 unsigned char (*completer
)(EditLine
*, int),
1176 (void)memset(&em
, 0, sizeof(em
));
1178 if ((nullfp
= fopen(_PATH_DEVNULL
, "w")) == NULL
)
1179 err(EXIT_FAILURE
, "Cannot open `%s'", _PATH_DEVNULL
);
1181 if ((em
.el
= el_init(getprogname(), stdin
, stdout
, nullfp
)) == NULL
) {
1185 (void)fflush(nullfp
);
1186 (void)dup2(STDERR_FILENO
, fileno(nullfp
));
1188 (void)el_set(em
.el
, EL_PROMPT
, show_prompt
);
1189 (void)el_set(em
.el
, EL_SIGNAL
, 1); /* editline handles the signals. */
1192 (void)el_set(em
.el
, EL_EDITOR
, el_editor
);
1196 (void)el_set(em
.el
, EL_ADDFN
, "mail-complete",
1197 "Context sensitive argument completion", completer
);
1198 for (np
= keys
; np
; np
= np
->n_flink
)
1199 (void)el_set(em
.el
, EL_BIND
, np
->n_name
,
1200 "mail-complete", NULL
);
1205 if ((em
.hist
= history_init()) == NULL
) {
1206 warn("history_init");
1209 if (history(em
.hist
, &ev
, H_SETSIZE
, history_size
) == -1)
1210 (void)printf("history: %s\n", ev
.str
);
1211 (void)el_set(em
.el
, EL_HIST
, history
, em
.hist
);
1214 (void)el_source(em
.el
, NULL
); /* read ~/.editrc */
1220 struct el_modes_s elm
= {
1221 .command
= { .el
= NULL
, .hist
= NULL
, },
1222 .string
= { .el
= NULL
, .hist
= NULL
, },
1223 .filec
= { .el
= NULL
, .hist
= NULL
, },
1225 .mime_enc
= { .el
= NULL
, .hist
= NULL
, },
1237 mode
= value(ENAME_EL_EDITOR
);
1239 cp
= value(ENAME_EL_HISTORY_SIZE
);
1240 hist_size
= cp
? atoi(cp
) : 0;
1242 cp
= value(ENAME_EL_COMPLETION_KEYS
);
1243 keys
= cp
&& *cp
? lexpand(cp
, 0) : NULL
;
1245 elm
.command
= init_el_mode(mode
, mail_complete
, keys
, hist_size
);
1246 elm
.filec
= init_el_mode(mode
, file_complete
, keys
, 0);
1247 elm
.string
= init_el_mode(mode
, NULL
, NULL
, 0);
1249 elm
.mime_enc
= init_el_mode(mode
, mime_enc_complete
, keys
, 0);
1254 #endif /* USE_EDITLINE */