1 /* @(#)inputc.c 1.110 19/04/07 Copyright 1982, 1984-2019 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)inputc.c 1.110 19/04/07 Copyright 1982, 1984-2019 J. Schilling";
6 #endif
7 /*
8 * inputc.c
9 *
10 * characterwise input for bsh
11 * line-editing
12 * history using cursor-block
13 *
14 * This is the second implementation that has been integrated into bsh.
15 * It offeres exactly the same functionality as you could find in the
16 * first implementation that was integrated into bsh and has been finished
17 * around August 1984.
18 *
19 * Both implementations are based on a simple shell prototype that I wrote
20 * in 1982 and 1983. This prototype only contained the editor and called
21 * shell commands via system().
22 *
23 * Copyright (c) 1982, 1984-2019 J. Schilling
24 * This version was first coded August 1984 and rewritten 01/22/85
25 *
26 * Exported functions:
27 * getinfile() Return the current input FILE *
28 * get_histlen() Return the max. number of history entries
29 * chghistory() Change history size to string argument
30 * init_input() Init editor data structures
31 * getnextc() Noninterruptable getc() -> export to map.c
32 * nextc() Read mapped input from map.c (Input to inputc.c)
33 * _nextwc() Read mapped wide char input from map.c (Input
34 * to inputc.c)
35 * space() Output n spaces
36 * append_line() Append a line into history (for hashcmd.c #lh)
37 * match_hist() Return matched history line for csh: !line
38 * make_line() Read a line from a file and return allocated
39 * string
40 * get_line() -> Write Prompt, then return edited line
41 * put_history() Put out history to FILE *
42 * save_history() Save the history to ~/.history
43 * read_init_history() Initialize the history from ~/.history
44 * readhistory() Read history from a file
45 */
46 /*
47 * The contents of this file are subject to the terms of the
48 * Common Development and Distribution License, Version 1.0 only
49 * (the "License"). You may not use this file except in compliance
50 * with the License.
51 *
52 * See the file CDDL.Schily.txt in this distribution for details.
53 * A copy of the CDDL is also available via the Internet at
54 * http://www.opensource.org/licenses/cddl1.txt
55 *
56 * When distributing Covered Code, include this CDDL HEADER in each
57 * file and include the License file CDDL.Schily.txt from this distribution.
58 */
59
60 #define NO_SCHILY_STDIO_H /* Try to avoid to #include schily/stdio.h */
61 #include <schily/string.h>
62 #include <schily/stdlib.h>
63 #include <schily/fcntl.h>
64 #include <schily/limits.h> /* for MB_LEN_MAX */
65 #include <schily/pwd.h> /* #includes <stdio.h> */
66 #include <schily/wchar.h> /* wchar_t #includes <stdio.h> */
67 #include <schily/wctype.h> /* For iswprint() */
68 #include <schily/patmatch.h> /* Past wchar.h to enable patchwmatch() */
69 #undef NO_SCHILY_STDIO_H
70 #include <schily/stdio.h> /* Past wchar.h to allow to redefine stdin */
71 #define putch dos_putch /* Avoid DOS/curses putch() type clash */
72 #define ungetch dos_ungetch /* Avoid DOS/curses ungetch() type clash */
73 #include <schily/termios.h> /* For WIN-DOS test and USE_GETCH */
74 #undef putch /* Restore our old value */
75 #undef ungetch /* Restore our old value */
76 #include "bsh.h"
77 #include "map.h"
78 #include "node.h"
79 #include "str.h"
80 #include "strsubs.h"
81 #ifndef USE_WCHAR /* Use the system wctype.h if using wchars */
82 #define REDEFINE_CTYPE
83 #include "ctype.h"
84 #endif /* USE_WCHAR */
85 #undef toint /* Atari MiNT has this nonstandard definition */
86 #ifdef LIB_SHEDIT
87 #define toint shell_toint
88 #define MYFILE int
89 #else
90 #define MYFILE FILE
91 #endif
92
93 #ifdef XDEBUG /* eXpand Debug */
94 #define DO_DEBUG
95 #else
96 #endif
97
98 #ifdef CWDEBUG /* Clear word Debug */
99 #define DO_DEBUG
100 #else
101 #endif
102
103 #ifdef USE_WCHAR
104 #ifndef MB_LEN_MAX
105 #define MB_LEN_MAX 10
106 #endif
107 #else /* !USE_WCHAR */
108 #ifndef MB_LEN_MAX
109 #define MB_LEN_MAX 1
110 #endif
111 #undef towupper
112 #define towupper toupper
113 #define wcsrchr strchr
114 #define wcscmp strcmp
115 #define wcsncmp strncmp
116 #define wcscpy strcpy
117 #define wcslen strlen
118 #define wcsncpy strncpy
119 #define patwcompile patcompile
120 #define patwmatch patmatch
121 #endif /* !USE_WCHAR */
122
123 #ifdef INTERACTIVE
124
125 #define _isdigit(c) ((c) >= '0' && (c) <= '9')
126
127 /*
128 * The output buffer for the line editor.
129 * It uses multi byte characters.
130 */
131 #define BUFSIZE 256
132
133 typedef struct {
134 int b_index; /* Index in b_buf */
135 char b_buf[BUFSIZE+MB_LEN_MAX]; /* The buffer space */
136 } BUF;
137
138 #define LINEQUANT 64
139 #define BEGINLINE 1 /* ^A Move Cursor leftmost */
140 #define SHOW 2 /* ^B Show possible file name completion(s) */
141 #define CTRLC 3 /* ^C Logical INTR */
142 #define CTRLD 4 /* ^D Logical EOF */
143 #define ENDLINE 5 /* ^E Move Cursor rightmost */
144 #define FORWARD 6 /* ^F Move Corsor one position to the right */
145 #define BEEP 7 /* ^G Audible Bell */
146 #define BACKSPACE 8 /* ^H Mode Cursor one position to the left */
147 #define TAB 9 /* ^I TAB is an alias for EXPAND */
148 /* ^J New Line */
149 #define DOWNWARD 14 /* ^N Scroll down to next line in history */
150 #define UPWARD 16 /* ^P Scroll up to previous line in history */
151 #define RETYPE 18 /* ^R Redisplay current edit line */
152 #define EXPAND TAB /* ^I Do file name completion */
153 #define CTRLU 21 /* ^U Clear current edit line */
154 #define CTRLV 22 /* ^V VNEXT quote next char */
155 #define CTRLW 23 /* ^W Backwards erase one word */
156 #define ESC 27 /* ^[ Lead in character for Escape sequence */
157 #define QUOTECH 30 /* ^^ Quote next character */
158 #define UNDO 31 /* ^_ Undo last remove operation */
159 #define BLANK ' '
160 #define BACKSLASH '\\'
161 #define DELETE 127 /* ^? Backwards erase one character */
162
163 #define BYTEMASK 0xFF
164 #define SEARCH 0x100
165 #define RESTORE 0x200
166
167 #define SEARCHUP (SEARCH|UPWARD)
168 #define SEARCHDOWN (SEARCH|DOWNWARD)
169
170 typedef struct histptr {
171 struct histptr *h_prev; /* Previous element in revolver */
172 struct histptr *h_next; /* Next element in revolver */
173 wchar_t *h_line; /* Space to store the line */
174 unsigned h_len; /* Number of wchar_t elements in line */
175 unsigned h_pos; /* wchar_t based offset in line */
176 unsigned h_number; /* Number used for POSIX "fc" */
177 unsigned char h_flags; /* Flags, see below */
178 } _HISTPTR, *HISTPTR;
179
180 /*
181 * Flags used in h_flags:
182 */
183 #define F_TMP 0x01 /* Claimed by a tmp pointer */
184
185 EXPORT MYFILE *getinfile __PR((void));
186 EXPORT int get_histlen __PR((void));
187 EXPORT void chghistory __PR((char *cp));
188 LOCAL void changehistory __PR((int n));
189 EXPORT void init_input __PR((void));
190 EXPORT int getnextc __PR((void));
191 EXPORT int nextc __PR((void));
192 EXPORT int _nextwc __PR((void));
193 LOCAL void writec __PR((int c));
194 LOCAL void _writes __PR((char *p));
195 LOCAL void writes __PR((char *p));
196 LOCAL void _writews __PR((wchar_t *p));
197 LOCAL void writews __PR((wchar_t *p));
198 LOCAL void prettyp __PR((int c, BUF * b));
199 LOCAL void bflush __PR((void));
200 LOCAL void pch __PR((int c, BUF * bp));
201 LOCAL void putch __PR((int c));
202 LOCAL void beep __PR((void));
203 LOCAL int chlen __PR((int c));
204 LOCAL int linelen __PR((wchar_t *s));
205 LOCAL int linediff __PR((wchar_t *a, wchar_t *e));
206 LOCAL void backspace __PR((int n));
207 EXPORT void space __PR((int n));
208 LOCAL void delete __PR((wchar_t *p));
209 LOCAL wchar_t *clearword __PR((wchar_t *lp, wchar_t *cp,
210 unsigned int *lenp));
211 LOCAL void clearline __PR((wchar_t *lp, wchar_t *cp));
212 LOCAL void ins_char __PR((wchar_t *cp, int c));
213 LOCAL wchar_t *ins_word __PR((wchar_t *cp, wchar_t *s));
214 LOCAL void del_char __PR((wchar_t *cp));
215 LOCAL void free_line __PR((HISTPTR p));
216 LOCAL HISTPTR mktmp __PR((void));
217 LOCAL HISTPTR hold_line __PR((HISTPTR p));
218 LOCAL void unhold_line __PR((HISTPTR p, HISTPTR op));
219 LOCAL HISTPTR remove_line __PR((HISTPTR p));
220 EXPORT void append_line __PR((char *linep, unsigned int len,
221 unsigned int pos));
222 EXPORT void append_wline __PR((wchar_t *linep, unsigned int len,
223 unsigned int pos));
224 LOCAL void append_hline __PR((wchar_t *linep, unsigned int len));
225 LOCAL void move_to_end __PR((HISTPTR p));
226 LOCAL void stripout __PR((void));
227 LOCAL HISTPTR match_input __PR((HISTPTR cur_line, HISTPTR tmp_line,
228 BOOL up));
229 EXPORT char *match_hist __PR((char *pattern));
230 LOCAL HISTPTR match __PR((HISTPTR cur_line, wchar_t *pattern,
231 BOOL up));
232 LOCAL int edit_line __PR((HISTPTR cur_line));
233 LOCAL void redisp __PR((wchar_t *lp, wchar_t *cp));
234 LOCAL wchar_t *insert __PR((wchar_t *cp, wchar_t *s,
235 unsigned int *lenp));
236 LOCAL wchar_t *undo_del __PR((wchar_t *lp, wchar_t *cp,
237 unsigned int *lenp));
238 LOCAL char *strwchr __PR((char *s, wchar_t c));
239 LOCAL wchar_t *xpwcs __PR((wchar_t *cp, int qoff));
240 LOCAL wchar_t *xp_tilde __PR((char *ns, int *delp));
241 LOCAL wchar_t *xp_files __PR((wchar_t *lp, wchar_t *cp, BOOL show,
242 int *multip, int *delp));
243 LOCAL wchar_t *exp_files __PR((wchar_t **lpp, wchar_t *cp,
244 unsigned int *lenp,
245 unsigned int *maxlenp,
246 int *multip));
247 LOCAL void show_files __PR((wchar_t *lp, wchar_t *cp));
248 LOCAL int get_request __PR((void));
249 LOCAL wchar_t *esc_process __PR((int xc, wchar_t *lp, wchar_t *cp,
250 unsigned int *lenp));
251 LOCAL wchar_t *sget_line __PR((void));
252 LOCAL wchar_t *iget_line __PR((void));
253 EXPORT char *make_line __PR((int (*f)(MYFILE *), MYFILE *arg));
254 LOCAL char *fread_line __PR((MYFILE *f));
255 EXPORT char *get_line __PR((int n, MYFILE *f));
256 EXPORT int put_history __PR((MYFILE *f, int flg,
257 int first, int last, char *subst));
258 EXPORT HISTPTR _search_history __PR((int flg,
259 int first, char *pat));
260 EXPORT int search_history __PR((int flg,
261 int first, char *pat));
262 LOCAL char *get_histfname __PR((int flag));
263 EXPORT void save_history __PR((int flg));
264 EXPORT void read_init_history __PR((void));
265 EXPORT void readhistory __PR((MYFILE *f));
266 LOCAL void term_init __PR((void));
267 LOCAL void tty_init __PR((void));
268 LOCAL void tty_term __PR((void));
269 #ifdef DO_DEBUG
270 LOCAL void cdbg __PR((char *fmt, ...)) __printflike__(1, 2);
271 #endif
272
273 extern pid_t mypgrp;
274 extern int delim;
275 extern int prflg;
276 extern int ttyflg;
277 extern int mapflag;
278 extern BOOL mp_init;
279 extern char *inithome;
280 extern BOOL ins_mode;
281 extern BOOL i_should_echo;
282
283 LOCAL MYFILE *infile = 0; /* FILE * to read frm */
284 LOCAL HISTPTR first_line = (HISTPTR) NULL; /* Oldest line */
285 LOCAL HISTPTR last_line = (HISTPTR) NULL; /* Newest line in h */
286 LOCAL HISTPTR rub_line = (HISTPTR) NULL;
287 LOCAL HISTPTR del_line = (HISTPTR) NULL;
288 LOCAL char *hfilename = NULL; /* Abs history path */
289 LOCAL char *line_pointer = NULL; /* Temp MB line ptr */
290 LOCAL wchar_t *wline_pointer = NULL; /* Temp WC line ptr */
291 LOCAL char *iprompt = NULL; /* Current promot */
292 LOCAL int histlen = 0; /* Max allowed h len */
293 LOCAL int no_lines = 0; /* Current hist len */
294 LOCAL BUF buf = {0}; /* BUF to optimize */
295 LOCAL char mapesc = '\0'; /* ESC char for map */
296 LOCAL unsigned hnumber = 0; /* For POSIX numbers */
297 #ifdef notneeded
298 LOCAL int eof;
299 #endif
300 EXPORT BOOL (*__ign_eof) __PR((void));
301
302
303
304 /* Begin wide string support routines -------> */
305 EXPORT wchar_t *makewstr __PR((wchar_t *s));
306 EXPORT wchar_t *
makewstr(s)307 makewstr(s)
308 register wchar_t *s;
309 {
310 wchar_t *tmp;
311 register wchar_t *s1;
312
313 if ((tmp = malloc((size_t)((wcslen(s)+1)) * sizeof (wchar_t))) ==
314 NULL) {
315 raisecond("makewstr", (long)NULL);
316 return (0);
317 }
318 for (s1 = tmp; (*s1++ = *s++) != '\0'; );
319 return (tmp);
320 }
321
322 LOCAL wchar_t *towcs __PR((wchar_t *wbuf, size_t bsize, char *s,
323 ssize_t len));
324 LOCAL wchar_t *
towcs(wbuf,bsize,s,len)325 towcs(wbuf, bsize, s, len)
326 wchar_t *wbuf;
327 size_t bsize;
328 char *s;
329 ssize_t len;
330 {
331 int i;
332 int mlen;
333 int chars;
334 char *sp;
335 wchar_t *wp;
336 wchar_t c;
337
338 if (len < 0)
339 len = strlen(s);
340
341 (void) mbtowc(NULL, NULL, 0);
342 for (sp = s, i = len, chars = 0; i > 0 && *sp != '\0'; ) {
343 mlen = mbtowc(&c, sp, i);
344 if (mlen <= 0) {
345 (void) mbtowc(NULL, NULL, 0);
346 mlen = 1;
347 }
348 i -= mlen;
349 sp += mlen;
350 chars++;
351 }
352 i -= 1;
353 chars++;
354
355 if (wbuf == NULL || chars > bsize) {
356 wbuf = malloc(chars * sizeof (wchar_t));
357 if (wbuf == NULL)
358 return (wbuf);
359 }
360
361 (void) mbtowc(NULL, NULL, 0);
362 for (wp = wbuf, sp = s, i = len; i > 0 && *sp != '\0'; wp++) {
363 mlen = mbtowc(&c, sp, i);
364 if (mlen <= 0) {
365 (void) mbtowc(NULL, NULL, 0);
366 mlen = 1;
367 c = *sp & 0xFF;
368 }
369 *wp = c;
370 sp += mlen;
371 i -= mlen;
372 }
373 *wp = 0;
374 return (wbuf);
375 }
376
377 LOCAL char *tombs __PR((char *cbuf, size_t bsize, wchar_t *ws,
378 ssize_t wlen));
379 LOCAL char *
tombs(cbuf,bsize,ws,wlen)380 tombs(cbuf, bsize, ws, wlen)
381 char *cbuf;
382 size_t bsize;
383 wchar_t *ws;
384 ssize_t wlen;
385 {
386 int len;
387 wchar_t *wcp;
388 char *cp;
389 char cstr[MB_LEN_MAX];
390
391 if (wlen < 0)
392 wlen = wcslen(ws);
393
394 for (len = 0, wcp = ws; *wcp; wcp++) {
395 int mlen;
396
397 mlen = wctomb(cstr, *wcp);
398 if (mlen <= 0)
399 mlen = 1;
400 len += mlen;
401 }
402 len++;
403
404 if (cbuf == NULL || len > bsize) {
405 cbuf = malloc(len);
406 if (cbuf == NULL)
407 return (cbuf);
408 }
409
410 for (wcp = ws, cp = cbuf; *wcp; wcp++) {
411 int mlen;
412
413 mlen = wctomb(cp, *wcp);
414 if (mlen <= 0) {
415 mlen = 1;
416 *cp = *wcp & 0xFF;
417 if (*cp == '\0')
418 *cp = '?';
419 }
420 cp += mlen;
421 }
422 *cp = '\0';
423 return (cbuf);
424 }
425 /* -------> End wide string support routines */
426
427
428 /*
429 * Return actual input FILE *
430 */
431 EXPORT MYFILE *
getinfile()432 getinfile()
433 {
434 return (infile);
435 }
436
437
438 /*
439 * Return the actual length of the history.
440 */
441 EXPORT int
get_histlen()442 get_histlen()
443 {
444 return (histlen);
445 }
446
447
448 /*
449 * Read new value and change the length of the current history to this value.
450 * chghistory() gets called when the environment is changed for HISTORY=
451 */
452 EXPORT void
chghistory(cp)453 chghistory(cp)
454 char *cp; /* A string with the new value of the history length */
455 {
456 int n;
457
458 #ifdef DEBUG
459 printf("chghistory ('%s')\r\n", cp);
460 #endif
461 if (!toint(gstd, cp, &n) || n < 0) {
462 berror("Bad value '%s' for %s.", cp, histname);
463 return;
464 }
465 changehistory(n);
466 }
467
468
469 /*
470 * Change the length of the current history.
471 */
472 LOCAL void
changehistory(n)473 changehistory(n)
474 register int n;
475 {
476 while (n < no_lines)
477 remove_line(first_line);
478 histlen = n;
479 }
480
481 EXPORT void
histrange(firstp,lastp,nextp)482 histrange(firstp, lastp, nextp)
483 unsigned *firstp;
484 unsigned *lastp;
485 unsigned *nextp;
486 {
487 /*
488 * Make it safe against a state where the history is not yet initialized
489 * and first_line or last_line are still NULL pointers.
490 */
491 if (firstp)
492 *firstp = first_line == 0 ? 0 : first_line->h_number;
493 if (lastp)
494 *lastp = last_line == 0 ? 0 : last_line->h_number;
495 if (nextp) /* XXX */
496 *nextp = (hnumber + 1) ? hnumber + 1 : hnumber + 2;
497 }
498
499 /*
500 * Init the history to the default size.
501 */
502 EXPORT void
init_input()503 init_input()
504 {
505 register char *p;
506
507 #ifdef DEBUGX
508 printf("init_input()\r\n");
509 #endif
510 /*
511 * First check for "HISTSIZE" as this is POSIX
512 */
513 if ((p = getcurenv(histsizename)) != NULL) {
514 /* EMPTY */;
515 } else if ((p = getcurenv(histname)) == NULL) {
516 /*
517 * If our historic name "HISTORY" is not present, use the
518 * minimum size required by POSIX.
519 */
520 p = "128";
521 ev_insert(concat(histname, eql, p, (char *)NULL));
522 }
523 chghistory(p);
524 rub_line = mktmp();
525 del_line = mktmp();
526 }
527
528
529 /*
530 * Non interruptable version of getc() from stdio lib.
531 */
532 EXPORT int
getnextc()533 getnextc()
534 {
535 int c;
536 int errs = 0;
537
538 again:
539 #ifdef USE_GETCH
540 c = getch(); /* DOS console input */
541 #else
542 c = getc(infile);
543 #endif
544 if (c < 0 && geterrno() == EINTR) {
545 clearerr(infile);
546 if (++errs < 10)
547 goto again;
548 }
549
550 #if defined(F_GETFL) && defined(O_NONBLOCK) && \
551 (defined(EAGAIN) || defined(EWOULDBLOCK))
552 #ifndef EWOULDBLOCK /* Not present on DJGPP */
553 #define EWOULDBLOCK EAGAIN
554 #endif
555 #ifndef EAGAIN /* Make it symmetric */
556 #define EAGAIN EWOULDBLOCK
557 #endif
558 if (c < 0 && (geterrno() == EAGAIN || geterrno() == EWOULDBLOCK)) {
559 int fl;
560
561 fl = fcntl(fdown(infile), F_GETFL, 0);
562 fl &= ~O_NONBLOCK;
563 fcntl(fdown(infile), F_SETFL, fl);
564 clearerr(infile);
565 if (++errs < 10)
566 goto again;
567 }
568 #endif
569 return (c);
570 }
571
572
573 /*
574 * Get next character from mapper.
575 * This allows us to implement cursor key mappings.
576 */
577 EXPORT int
nextc()578 nextc()
579 {
580 register int c;
581
582 if (!mapflag) {
583 if ((c = mapgetc()) == mapesc)
584 c = mapgetc();
585 else if (rmap(c))
586 c = gmap();
587 } else if ((c = gmap()) == 0) {
588 c = nextc();
589 }
590 return (c);
591 }
592
593 /*
594 * Get next wide character
595 * Caution: The Bourne Shell has nextwc() in word.c
596 */
597 EXPORT int
_nextwc()598 _nextwc()
599 {
600 register int cur_max = MB_CUR_MAX;
601 register int i;
602 int mlen;
603 wchar_t c = -1;
604 char cstr[MB_LEN_MAX];
605 char *cp;
606 int ic;
607
608 (void) mbtowc(NULL, NULL, 0);
609 for (i = 1, cp = cstr; i <= cur_max; i++) {
610 ic = nextc();
611 if (ic == EOF)
612 break;
613 *cp++ = (char)ic;
614 mlen = mbtowc(&c, cstr, i);
615 if (mlen >= 0)
616 break;
617 (void) mbtowc(NULL, NULL, 0);
618 }
619 #ifdef _NEXTWC_DEBUG
620 error("C %d %x\n", c, c);
621 #endif
622 return ((int)c);
623 }
624
625
626 /*
627 * Write a single (wide) character to our tty
628 */
629 LOCAL void
writec(c)630 writec(c)
631 int c;
632 {
633 prettyp(c, &buf);
634 }
635
636
637 /*
638 * Write a string
639 * Needed for internal string output like "<EOF>"
640 */
641 LOCAL void
_writes(p)642 _writes(p)
643 register char *p;
644 {
645 register BUF *rb = &buf;
646
647 while (*p)
648 prettyp(*p++, rb);
649 }
650
651 LOCAL void
writes(p)652 writes(p)
653 char *p;
654 {
655 _writes(p);
656 bflush();
657 }
658
659 /*
660 * Write a wide character string
661 */
662 LOCAL void
_writews(p)663 _writews(p)
664 register wchar_t *p;
665 {
666 register BUF *rb = &buf;
667
668 while (*p)
669 prettyp(*p++, rb);
670 }
671
672 LOCAL void
writews(p)673 writews(p)
674 wchar_t *p;
675 {
676 _writews(p);
677 bflush();
678 }
679
680
681 /*
682 * Write a single (wide) character in expanded (readable) form.
683 * Non printable characters are frefixed by '~' if they
684 * are beyond 0x7F and prefixed by '^' if they are a
685 * control character.
686 */
687 LOCAL void
prettyp(c,b)688 prettyp(c, b)
689 register int c;
690 register BUF *b;
691 {
692 if (iswprint(c) /* || c == '\n' */) {
693 pch(c, b);
694 return;
695 }
696 if (c > 0xFF) {
697 char s[16];
698
699 js_snprintf(s, sizeof (s), "'%10.10X", c);
700 writes(s);
701 return;
702 }
703 if (c & 0x80) {
704 pch('~', b);
705 prettyp(c & 0177, b);
706 } else {
707 pch('^', b);
708 pch(c ^ 0100, b);
709 }
710 }
711
712
713 /*
714 * Flush our internal line buffering module.
715 */
716 LOCAL void
bflush()717 bflush()
718 {
719 register BUF *bp = &buf;
720
721 if (bp->b_index <= 0)
722 return;
723 (void) filewrite(stdout, bp->b_buf, bp->b_index);
724 (void) fflush(stdout);
725 bp->b_index = 0;
726 }
727
728
729 /*
730 * Put a (wide) character into out line buffering module.
731 */
732 LOCAL void
pch(c,bp)733 pch(c, bp)
734 int c;
735 register BUF *bp;
736 {
737 char cstr[MB_LEN_MAX];
738 int len;
739
740 if (bp->b_index >= BUFSIZE)
741 bflush();
742
743 len = wctomb(cstr, (wchar_t)c);
744 if (len <= 0) {
745 if ((c & 0xFF) == '\0')
746 c = '?';
747
748 bp->b_buf[bp->b_index++] = (char)c;
749 } else {
750 char *cp = cstr;
751
752 while (--len >= 0) {
753 bp->b_buf[bp->b_index++] = *cp++;
754 }
755 if (bp->b_index >= BUFSIZE) /* If >= BUFSIZE */
756 bflush(); /* flush immediately */
757 }
758 }
759
760
761 /*
762 * Simple (wide) putchar() replacement that uses our line buffering.
763 */
764 LOCAL void
putch(c)765 putch(c)
766 int c;
767 {
768 pch(c, &buf);
769 }
770
771
772 /*
773 * Ring the bell.
774 */
775 LOCAL void
beep()776 beep()
777 {
778 char *p = getcurenv("BEEP");
779
780 if (p != NULL && streql(p, "off"))
781 return;
782 putch(BEEP);
783 bflush();
784 }
785
786
787 /*
788 * Compute the visible length of a (wide) character.
789 */
790 LOCAL int
chlen(c)791 chlen(c)
792 register int c;
793 {
794 if (iswprint(c) /* || c == '\n' */) {
795 #ifdef HAVE_WCWIDTH
796 return (wcwidth(c));
797 #else
798 return (1);
799 #endif
800 }
801 if (c > 0xFF) /* UNICODE */
802 return (11); /* "'%10.10X" */
803 if (c & 0x80)
804 return (2 + !iswprint(c&0177));
805 return (2);
806 }
807
808
809 /*
810 * Compute the visible length of a wide string.
811 */
812 LOCAL int
linelen(s)813 linelen(s)
814 register wchar_t *s;
815 {
816 register int len = 0;
817 while (*s)
818 len += chlen(*s++);
819 return (len);
820 }
821
822
823 /*
824 * Compute the visible difference of two characters in a string.
825 */
826 LOCAL int
linediff(a,e)827 linediff(a, e)
828 register wchar_t *a;
829 register wchar_t *e;
830 {
831 register int diff = 0;
832
833 while (*a && a < e)
834 diff += chlen(*a++);
835 return (diff);
836 }
837
838
839 /*
840 * Back space cursor by 'n'.
841 */
842 LOCAL void
backspace(n)843 backspace(n)
844 register int n;
845 {
846 register BUF *rb = &buf;
847
848 while (n--)
849 pch(BACKSPACE, rb);
850 bflush();
851 }
852
853
854 /*
855 * Forward space cursor by 'n'. Do this by writing spaces.
856 */
857 EXPORT void
space(n)858 space(n)
859 register int n;
860 {
861 register BUF *rb = &buf;
862
863 while (n--)
864 pch(BLANK, rb);
865 bflush();
866 }
867
868
869 /*
870 * loescht bis Ende der Zeile
871 */
872 LOCAL void
delete(p)873 delete(p)
874 register wchar_t *p;
875 {
876 register int len = 0;
877
878 while (*p)
879 len += chlen(*p++);
880 space(len);
881 backspace(len);
882 }
883
884 /*
885 * loescht ein Wort rueckwaerts wie im Terminaltreiber mit ^W
886 */
887 LOCAL wchar_t *
clearword(lp,cp,lenp)888 clearword(lp, cp, lenp)
889 register wchar_t *lp;
890 register wchar_t *cp;
891 unsigned *lenp;
892 {
893 register int dl = 0; /* Displayed len of word */
894 register int wl; /* Length of word in chars */
895 register wchar_t *p;
896 wchar_t *sp = cp;
897
898 if (cp == lp) { /* At begin of line */
899 beep();
900 return (cp);
901 }
902 cp--;
903 while (cp >= lp && *cp == BLANK) { /* Skip space behind word */
904 --cp, dl++;
905 }
906 while (cp >= lp && *cp != BLANK) /* Skip chars in word */
907 dl += chlen(*cp--);
908 backspace(dl); /* Backspace over del chars */
909 cp++;
910
911 wl = sp - cp;
912
913 for (p = cp; ; p++) {
914 if ((*p = *(p+wl)) == '\0')
915 break;
916 }
917 writews(cp); /* Write new end of line */
918 space(dl); /* Overwrite deleted space */
919 backspace(linelen(cp) + dl); /* Readjust cursor position */
920
921
922 *lenp -= wl;
923 return (cp);
924 }
925
926 /*
927 * loescht die ganze Zeile
928 */
929 LOCAL void
clearline(lp,cp)930 clearline(lp, cp)
931 wchar_t *lp;
932 wchar_t *cp;
933 {
934 backspace(linediff(lp, cp));
935 delete(lp);
936 }
937
938 /*
939 * Fuegt char c bei cp ein.
940 */
941 LOCAL void
ins_char(cp,c)942 ins_char(cp, c)
943 register wchar_t *cp;
944 int c;
945 {
946 register wchar_t *ep = cp;
947
948 writews(cp);
949 backspace(linelen(cp));
950 while (*ep++);
951 ep--;
952 do {
953 *(ep + 1) = *ep;
954 } while (ep-- > cp);
955 *cp = c;
956 }
957
958
959 /*
960 * Fuegt String s bei cp ein.
961 */
962 LOCAL wchar_t *
ins_word(cp,s)963 ins_word(cp, s)
964 register wchar_t *cp;
965 register wchar_t *s;
966 {
967 register wchar_t *ep = cp;
968 register int len;
969
970 len = wcslen(s);
971 writews(cp);
972 backspace(linelen(cp));
973 while (*ep++);
974 ep--;
975 do {
976 *(ep + len) = *ep;
977 } while (ep-- > cp);
978 while (*s)
979 *cp++ = *s++;
980 return (cp);
981 }
982
983
984 /*
985 * loescht Zeichen *cp und stellt dar
986 */
987 LOCAL void
del_char(cp)988 del_char(cp)
989 register wchar_t *cp;
990 {
991 register wchar_t *p;
992 register int dl = 0;
993
994 backspace(dl = chlen(*cp));
995 for (p = cp; *p; p++)
996 *p = *(p+1);
997 writews(cp);
998 space(dl);
999 backspace(linelen(cp) + dl);
1000 }
1001
1002 #ifdef OOO
1003 LOCAL char *
new_line(lp,old,new)1004 new_line(lp, old, new)
1005 char *lp;
1006 unsigned old;
1007 unsigned new;
1008 {
1009 register char *np;
1010
1011 /* realloc !!! */
1012 np = malloc(new * sizeof (*lp));
1013 if (np == NULL)
1014 return (np);
1015 movebytes(lp, np, (int)old); /* make_line hat kein NULL Byte ! */
1016 free(lp);
1017 return (np);
1018 }
1019 #else
1020 #define new_line(lp, old, new) realloc(lp, new * sizeof (*lp))
1021 #endif
1022
1023 /*
1024 * Free a history line Tnode.
1025 */
1026 LOCAL void
free_line(p)1027 free_line(p)
1028 register HISTPTR p;
1029 {
1030 p->h_flags = 0;
1031 free(p->h_line);
1032 free((char *)p);
1033 }
1034
1035 /*
1036 * Create a temporary history line template.
1037 */
1038 LOCAL HISTPTR
mktmp()1039 mktmp()
1040 {
1041 register HISTPTR tmp;
1042
1043 tmp = (HISTPTR)malloc(sizeof (_HISTPTR));
1044 if (tmp == NULL)
1045 return (tmp);
1046 tmp->h_prev = tmp->h_next = NULL;
1047 tmp->h_line = malloc(LINEQUANT * sizeof (*tmp->h_line));
1048 if (tmp->h_line == NULL) {
1049 free(tmp);
1050 return ((HISTPTR) NULL);
1051 }
1052 tmp->h_len = LINEQUANT;
1053 tmp->h_pos = 0;
1054 tmp->h_number = 0;
1055 tmp->h_flags = F_TMP; /* Mark it temporary */
1056 tmp->h_line[0] = '\0';
1057 return (tmp);
1058 }
1059
1060 /*
1061 * Create an editable copy of a history line.
1062 */
1063 LOCAL HISTPTR
hold_line(p)1064 hold_line(p)
1065 register HISTPTR p;
1066 {
1067 register HISTPTR tmp;
1068
1069 tmp = (HISTPTR)malloc(sizeof (_HISTPTR));
1070 if (tmp == NULL)
1071 return (tmp);
1072 tmp->h_prev = p->h_prev;
1073 tmp->h_next = p->h_next;
1074 tmp->h_line = malloc(p->h_len * sizeof (*tmp->h_line));
1075 if (tmp->h_line == NULL) {
1076 free(tmp);
1077 return ((HISTPTR) NULL);
1078 }
1079 tmp->h_len = p->h_len;
1080 tmp->h_pos = p->h_pos;
1081 tmp->h_number = 0;
1082 tmp->h_flags = F_TMP; /* Mark it temporary */
1083 movebytes(p->h_line, tmp->h_line, p->h_len * sizeof (*tmp->h_line));
1084 return (tmp);
1085 }
1086
1087 LOCAL void
unhold_line(p,op)1088 unhold_line(p, op)
1089 register HISTPTR p; /* copy of hold line */
1090 register HISTPTR op; /* original hold line */
1091 {
1092 if (p == (HISTPTR) NULL)
1093 return;
1094
1095 if (op) {
1096 int len = wcslen(op->h_line);
1097
1098 if (p->h_pos > len)
1099 op->h_pos = len;
1100 else
1101 op->h_pos = p->h_pos;
1102 }
1103 if ((p->h_flags & F_TMP) != 0)
1104 free_line(p);
1105 }
1106
1107 #ifdef DEBUG
1108 LOCAL void
trace_hist()1109 trace_hist()
1110 {
1111 register HISTPTR p;
1112
1113 for (p = first_line; p; p = p->h_next) {
1114 printf("{ %s } at %p, prev at %p, next at %p\r\n",
1115 p->h_line, p, p->h_prev, p->h_next);
1116 }
1117 }
1118 #endif
1119
1120
1121 /*
1122 * Remove a line from the history list.
1123 */
1124 LOCAL HISTPTR
remove_line(p)1125 remove_line(p)
1126 register HISTPTR p;
1127 {
1128 register HISTPTR lp;
1129 register HISTPTR np;
1130
1131 if (p == (HISTPTR) NULL)
1132 return ((HISTPTR) NULL);
1133 #ifdef DEBUG
1134 printf("removing line at %p, chars at %p\r\n", p, p->h_line);
1135 #endif
1136 lp = p->h_prev;
1137 np = p->h_next;
1138 if ((p->h_flags & F_TMP) == 0) {
1139 /*
1140 * Only free it if not claimed by a temporary pointer.
1141 */
1142 free(p->h_line);
1143 free((char *)p);
1144 }
1145 if (lp)
1146 lp->h_next = np;
1147 else
1148 first_line = np;
1149 if (np)
1150 np->h_prev = lp;
1151 else
1152 last_line = lp;
1153 no_lines--;
1154 #ifdef DEBUG
1155 printf("removed. first at %p, last at %p, no_lines = %d\r\n",
1156 first_line, last_line, no_lines);
1157 #endif
1158 if (lp == (HISTPTR) NULL)
1159 lp = first_line;
1160 return (lp);
1161 }
1162
1163
1164 /*
1165 * Append a multibyte line to the end of the history list.
1166 */
1167 EXPORT void
append_line(linep,len,pos)1168 append_line(linep, len, pos)
1169 char *linep;
1170 unsigned len;
1171 unsigned pos;
1172 {
1173 wchar_t wline[512];
1174 wchar_t *wp;
1175
1176 wp = towcs(wline, sizeof (wline) / sizeof (wline[0]), linep, len);
1177 if (wp == NULL)
1178 return;
1179
1180 len = 1 + wcslen(wp);
1181 append_wline(wp, len, pos);
1182
1183 if (wp != wline)
1184 free(wp);
1185 }
1186
1187 /*
1188 * Append a wide character line to the end of the history list.
1189 */
1190 EXPORT void
append_wline(linep,len,pos)1191 append_wline(linep, len, pos)
1192 wchar_t *linep;
1193 unsigned len;
1194 unsigned pos;
1195 {
1196 register HISTPTR p;
1197 register wchar_t *lp;
1198
1199 #ifdef DEBUG
1200 printf("appending line, histlen = %d.\r\n", histlen);
1201 #endif
1202 if (histlen == 0)
1203 return;
1204 if (no_lines == histlen)
1205 remove_line(first_line);
1206 p = (HISTPTR)malloc(sizeof (_HISTPTR));
1207 if (p == NULL)
1208 return;
1209 lp = malloc(len * sizeof (*lp));
1210 if (lp == NULL) { /* Do nothing if malloc() fails */
1211 free(p);
1212 return;
1213 }
1214 wcscpy(lp, linep);
1215 p->h_prev = last_line;
1216 p->h_line = lp;
1217 p->h_len = len;
1218 p->h_pos = pos;
1219 p->h_number = ++hnumber; /* XXX */
1220 if (p->h_number == 0)
1221 p->h_number = ++hnumber;
1222 p->h_flags = 0;
1223 p->h_next = (HISTPTR) NULL;
1224 if (last_line)
1225 last_line->h_next = p;
1226 else
1227 first_line = p;
1228 last_line = p;
1229 no_lines++;
1230 #ifdef DEBUG
1231 printf("appended line: first at %p, last at %p, no_lines = %d\r\n",
1232 first_line, last_line, no_lines);
1233 #endif
1234 }
1235
1236
1237 /*
1238 * Provisorisch Folgezeilen in History (fuer if then else ... History)
1239 *
1240 */
1241 LOCAL void
append_hline(linep,len)1242 append_hline(linep, len)
1243 wchar_t *linep;
1244 unsigned len;
1245 {
1246 register HISTPTR p = last_line;
1247 register wchar_t *lp;
1248 wchar_t anl[2];
1249
1250 len += p->h_len;
1251 lp = malloc(len * sizeof (*lp));
1252 if (lp == NULL) /* Do nothing if malloc() fails */
1253 return;
1254 #ifdef USE_ANSI_NL_SEPARATOR
1255 anl[0] = '\205';
1256 #else
1257 anl[0] = '\n';
1258 #endif
1259 anl[1] = '\0';
1260 wcscatl(lp, p->h_line, anl, linep, (wchar_t *)NULL);
1261 free(p->h_line);
1262 p->h_line = lp;
1263 p->h_len = len;
1264 /*
1265 * Keep cursor position of first line.
1266 */
1267 }
1268
1269
1270 /*
1271 * Move line p to the end of the history.
1272 */
1273 LOCAL void
move_to_end(p)1274 move_to_end(p)
1275 HISTPTR p;
1276 {
1277 register HISTPTR lp;
1278 register HISTPTR np;
1279
1280 if ((np = p->h_next) != NULL) {
1281 lp = p->h_prev;
1282 if (lp) {
1283 /* middle of history */
1284 lp->h_next = np;
1285 np->h_prev = lp;
1286 } else {
1287 /* first line of history */
1288 np->h_prev = (HISTPTR) NULL;
1289 first_line = np;
1290 }
1291 last_line->h_next = p;
1292 p->h_prev = last_line;
1293 p->h_next = (HISTPTR) NULL;
1294 p->h_number = ++hnumber; /* XXX */
1295 if (p->h_number == 0)
1296 p->h_number = ++hnumber;
1297 p->h_flags &= ~F_TMP;
1298 last_line = p;
1299 }
1300 #ifdef DEBUG
1301 printf("moved line: first at %p, last at %p\r\n",
1302 first_line, last_line);
1303 #endif
1304 }
1305
1306
1307 /*
1308 * Stripout entfernt alle Zeilen, die den gleichen Inhalt wie die lezte Zeile
1309 * haben. Stripout wird aufgerufen, nachdem die aktuell editierte Zeile die
1310 * lezte Zeile geworden ist.
1311 */
1312 LOCAL void
stripout()1313 stripout()
1314 {
1315 register HISTPTR p;
1316 register wchar_t *linep;
1317
1318 if (!last_line)
1319 return;
1320 linep = last_line->h_line;
1321 #ifdef DEBUG
1322 printf("stripping out '%s'.\r\n", linep);
1323 #endif
1324 for (p = first_line; p && p != last_line; p = p->h_next) {
1325 #ifdef DEBUG
1326 printf("stripout: '%s'.\r\n", p->h_line);
1327 #endif
1328 if (wcslen(p->h_line) == 0 || wcseql(linep, p->h_line)) {
1329 p = remove_line(p);
1330 if (p == (HISTPTR)NULL)
1331 break;
1332 }
1333 }
1334 }
1335
1336
1337 /*
1338 * Implement the bsh history search function.
1339 */
1340 LOCAL HISTPTR
match_input(cur_line,tmp_line,up)1341 match_input(cur_line, tmp_line, up)
1342 HISTPTR cur_line;
1343 HISTPTR tmp_line;
1344 BOOL up;
1345 {
1346 static HISTPTR match_line = (HISTPTR) NULL;
1347 HISTPTR hp;
1348 char *oldprompt;
1349 wchar_t *pattern;
1350
1351 if (!match_line)
1352 match_line = mktmp();
1353 if (!match_line) {
1354 (void) fprintf(stderr, "\r\n");
1355 (void) fflush(stderr);
1356 beep();
1357 hp = tmp_line;
1358 goto fail;
1359 }
1360 oldprompt = iprompt;
1361 iprompt = up ? "search up: " : "search down: ";
1362 (void) fprintf(stderr, "\r\n%s", iprompt);
1363 (void) fflush(stderr);
1364 for (;;)
1365 if (edit_line(match_line) == '\n')
1366 break;
1367 else
1368 beep();
1369 pattern = match_line->h_line;
1370 if ((hp = match(cur_line, pattern, up)) == (HISTPTR) NULL)
1371 hp = tmp_line;
1372 iprompt = oldprompt;
1373 fail:
1374 (void) fprintf(stderr, "%s", iprompt);
1375 (void) fflush(stderr);
1376 return (hp);
1377 }
1378
1379
1380 /*
1381 * Implement the !pattern search that is a simple csh feature.
1382 * match_hist() is called by the parser when !<pattern> is seen.
1383 */
1384 EXPORT char *
match_hist(pattern)1385 match_hist(pattern)
1386 char *pattern;
1387 {
1388 register HISTPTR hp;
1389 wchar_t wpattern[32];
1390 wchar_t *wp;
1391
1392 wpattern[0] = '\0';
1393 if (streql(pattern, "!")) {
1394 wpattern[0] = '*';
1395 wpattern[1] = '\0';
1396 wp = wpattern;
1397 } else {
1398 wp = towcs(wpattern, sizeof (wpattern) / sizeof (wpattern[0]),
1399 pattern, -1);
1400 if (wp == NULL)
1401 return (NULL);
1402 }
1403 remove_line(last_line);
1404 if ((hp = match(last_line, wp, TRUE)) == (HISTPTR) NULL) {
1405 if (wp != wpattern)
1406 free(wp);
1407 return (NULL);
1408 }
1409 if (wp != wpattern)
1410 free(wp);
1411 move_to_end(hp);
1412
1413 if (line_pointer)
1414 free(line_pointer);
1415 line_pointer = tombs(NULL, 0, hp->h_line, -1);
1416
1417 if (line_pointer)
1418 (void) fprintf(stderr, "%s\r\n", line_pointer);
1419 (void) fflush(stderr);
1420 return (line_pointer);
1421 }
1422
1423
1424 /*
1425 * Find a line that matches pattern in history.
1426 */
1427 LOCAL HISTPTR
match(cur_line,pattern,up)1428 match(cur_line, pattern, up)
1429 HISTPTR cur_line;
1430 register wchar_t *pattern;
1431 register BOOL up;
1432 {
1433 register int patlen;
1434 register int alt = 0;
1435 register int *aux;
1436 register int *state;
1437 register HISTPTR hp = (HISTPTR) NULL;
1438 register wchar_t *lp;
1439
1440 if (pattern) {
1441 patlen = wcslen(pattern);
1442 aux = (int *)malloc((size_t)patlen * sizeof (int));
1443 if (aux == NULL)
1444 return ((HISTPTR) NULL);
1445 state = (int *)malloc((size_t)(patlen+1) * sizeof (int));
1446 if (state == NULL) {
1447 free(aux);
1448 return ((HISTPTR) NULL);
1449 }
1450 if ((alt = patwcompile(pattern, patlen, aux)) != 0) {
1451 for (hp = cur_line; hp; ) {
1452 lp = hp->h_line;
1453 if (patwmatch(pattern, aux,
1454 lp, 0, wcslen(lp), alt, state))
1455 break;
1456 if (up)
1457 hp = hp->h_prev;
1458 else
1459 hp = hp->h_next;
1460 }
1461 }
1462 free((char *)aux);
1463 free((char *)state);
1464 }
1465 if (!hp) {
1466 if (!alt)
1467 berror("%s", ebadpattern);
1468 else
1469 berror("%s", enotfound);
1470 return ((HISTPTR) NULL);
1471 }
1472 return (hp);
1473 }
1474
1475
1476 /*
1477 * Implementation of basic editing functions.
1478 */
1479 LOCAL int
edit_line(cur_line)1480 edit_line(cur_line)
1481 HISTPTR cur_line;
1482 {
1483 register int c;
1484 register wchar_t *lp, *cp;
1485 wchar_t *lpp;
1486 unsigned llen, maxlen;
1487 int diff;
1488 int multi = 0;
1489
1490 get_tty_modes(infile);
1491 #if JOBCONTROL
1492 tty_setpgrp(fdown(infile), mypgrp);
1493 #endif
1494 #ifdef DEBUG
1495 printf("editline at %p.\r\n", cur_line);
1496 #endif
1497 maxlen = cur_line->h_len;
1498 cp = lp = cur_line->h_line;
1499 llen = wcslen(lp) + 1;
1500 writews(lp);
1501 cp = &lp[cur_line->h_pos];
1502 backspace(linelen(cp));
1503 ins_mode = !*cp;
1504 #ifdef notneeded
1505 eof = 0;
1506 #endif
1507 for (;;) {
1508 *cp ? set_insert_modes(infile) : set_append_modes(infile);
1509 c = _nextwc();
1510 switch (c) {
1511
1512 case '\r': /* eigentlich nicht noetig */
1513 case '\n':
1514 delim = '\n';
1515 putch('\r');
1516 putch('\n');
1517 bflush();
1518 cur_line->h_len = maxlen;
1519 cur_line->h_line = lp;
1520 cur_line->h_pos = cp - lp;
1521 reset_tty_modes();
1522 #ifdef DEBUG
1523 printf("ctlc: %d\r\n", ctlc);
1524 #endif
1525 return ('\n');
1526 case RETYPE:
1527 redisp(lp, cp);
1528 break;
1529 case UNDO:
1530 cp = undo_del(lp, cp, &llen);
1531 break;
1532 case SHOW:
1533 show_files(lp, cp);
1534 break;
1535 case '\t':
1536 if (multi) {
1537 show_files(lp, cp);
1538 break;
1539 }
1540 lpp = lp;
1541 cp = exp_files(&lpp, cp, &llen, &maxlen, &multi);
1542 if (lpp == NULL)
1543 return ('\0');
1544 lp = lpp;
1545 break;
1546 case BACKSPACE:
1547 multi = 0;
1548 if (cp > lp)
1549 backspace(chlen(*(--cp)));
1550 else
1551 beep();
1552 break;
1553 case FORWARD:
1554 multi = 0;
1555 if (*cp) {
1556 writec(*cp++);
1557 bflush();
1558 }
1559 else
1560 beep();
1561 break;
1562 case EOF:
1563 #ifdef notneeded
1564 if (++eof < 4)
1565 break;
1566 clearerr(infile);
1567 #endif
1568 if (delim == EOF)
1569 exitbsh(0);
1570 /* FALLTHROUGH */
1571 case CTRLD:
1572 multi = 0;
1573 if ((cp == lp && !wcslen(lp) &&
1574 !ev_eql(ignoreeofname, on) &&
1575 !(__ign_eof && (*__ign_eof)())) || c == EOF) {
1576 delim = EOF;
1577 clearline(lp, cp);
1578 reset_tty_modes();
1579 putch('\r');
1580 putch('\n');
1581 writes("<EOF>");
1582 putch('\r');
1583 putch('\n');
1584 bflush();
1585 return (EOF);
1586 }
1587 if (*cp) {
1588 writec(*cp);
1589 bflush();
1590 del_char(cp);
1591 --llen;
1592 }
1593 else
1594 beep();
1595
1596 break;
1597 case CTRLC:
1598 multi = 0;
1599 delim = CTRLC;
1600 #ifndef LIB_SHEDIT
1601 ctlc++; /* Mark for bsh */
1602 #endif
1603 *lp = 0;
1604 llen = 1; /* NULL Byte !!! */
1605 cp = lp;
1606 writes("^C");
1607 return (EOF);
1608 case CTRLU:
1609 multi = 0;
1610 clearline(lp, cp);
1611 *lp = 0;
1612 llen = 1; /* NULL Byte !!! */
1613 cp = lp;
1614 break;
1615 case CTRLW:
1616 multi = 0;
1617 cp = clearword(lp, cp, &llen);
1618 break;
1619 case ESC:
1620 multi = 0;
1621 c = get_request();
1622 if (c == RESTORE)
1623 clearline(lp, cp);
1624 if (c & ~BYTEMASK) {
1625 cur_line->h_line = lp;
1626 cur_line->h_len = maxlen;
1627 cur_line->h_pos = cp - lp;
1628 reset_tty_modes();
1629 return (c);
1630 }
1631 else
1632 cp = esc_process(c, lp, cp, &llen);
1633 break;
1634 case UPWARD:
1635 case DOWNWARD:
1636 clearline(lp, cp);
1637 cur_line->h_line = lp;
1638 cur_line->h_len = maxlen;
1639 cur_line->h_pos = cp - lp;
1640 return (c);
1641 case DELETE:
1642 multi = 0;
1643 if (cp > lp) {
1644 del_char(--cp);
1645 llen--;
1646 }
1647 else
1648 beep();
1649 break;
1650 case BEGINLINE:
1651 multi = 0;
1652 backspace(linediff(lp, cp));
1653 cp = lp;
1654 break;
1655 case ENDLINE:
1656 multi = 0;
1657 while (*cp)
1658 writec(*cp++);
1659 bflush();
1660 break;
1661 case QUOTECH:
1662 case CTRLV: { int oc = c;
1663
1664 set_insert_modes(infile);
1665 c = _nextwc();
1666
1667 if (oc == QUOTECH &&
1668 (towupper(c) < 0140) && c >= '@')
1669 c &= 037;
1670 }
1671 /* FALLTHROUGH */
1672 default:
1673 multi = 0;
1674 if (i_should_echo || !iswprint(c))
1675 writec(c);
1676 bflush();
1677 if (llen == maxlen) {
1678 maxlen += LINEQUANT;
1679 diff = cp - lp;
1680 lp = new_line(lp, llen, maxlen);
1681 if (lp == NULL)
1682 return ('\0');
1683 cp = lp + diff;
1684 }
1685 ins_char(cp, c);
1686 cp++;
1687 llen++;
1688 }
1689 }
1690 }
1691
1692
1693 /*
1694 * Redisplay current line.
1695 */
1696 LOCAL void
redisp(lp,cp)1697 redisp(lp, cp)
1698 wchar_t *lp;
1699 wchar_t *cp;
1700 {
1701 (void) fprintf(stderr, "\r\n%s", iprompt);
1702 (void) fflush(stderr);
1703 writews(lp);
1704 backspace(linelen(cp));
1705 }
1706
1707
1708 /*
1709 * Insert a string a current cursor position.
1710 */
1711 LOCAL wchar_t *
insert(cp,s,lenp)1712 insert(cp, s, lenp)
1713 register wchar_t *cp;
1714 register wchar_t *s;
1715 unsigned *lenp;
1716 {
1717 *lenp += wcslen(s);
1718 writews(s);
1719 cp = ins_word(cp, s);
1720 return (cp);
1721 }
1722
1723
1724 /*
1725 * Undo last delete(s).
1726 */
1727 LOCAL wchar_t *
undo_del(lp,cp,lenp)1728 undo_del(lp, cp, lenp)
1729 register wchar_t *lp;
1730 register wchar_t *cp;
1731 unsigned *lenp;
1732 {
1733 #ifdef NEW
1734 register int fdellen;
1735
1736 cp = insert(cp, "undo", lenp);
1737 cp = insert(cp, "del", lenp);
1738 fdellen = strlen("del");
1739 backspace(fdellen);
1740 cp -= fdellen;
1741 #endif
1742 return (cp);
1743 }
1744
1745 /*
1746 * Check whether a wide char code can be found in a narrow char string.
1747 */
1748 LOCAL char *
strwchr(s,c)1749 strwchr(s, c)
1750 char *s;
1751 wchar_t c;
1752 {
1753 while (*s) {
1754 if ((*s++ & 0xFF) == c)
1755 return (--s);
1756 }
1757 return (NULL);
1758 }
1759
1760 /*
1761 * The characters ' ' ... '&' are handled by the shell parser,
1762 * the characters '!' ... '$' are pattern matcher meta characters.
1763 * The complete pattern matcher meta characters are "!#%*?\\{}[]^$", but
1764 * the '%' has been checked before in the shell parser charracter set.
1765 * The string quoting charcters '"' and '\''.
1766 */
1767 LOCAL char xchars[] = " \t\"'<>%|;()&-!#*?\\{}[]^$"; /* Chars that need quoting */
1768
1769 /*
1770 * Expand a wide char string and return a malloc()ed copy.
1771 * Any character that needs quoting is prepended with a '\\'.
1772 */
1773 LOCAL wchar_t *
xpwcs(cp,qoff)1774 xpwcs(cp, qoff)
1775 wchar_t *cp;
1776 int qoff;
1777 {
1778 wchar_t *ret;
1779 wchar_t *p = &cp[qoff];
1780 int len = qoff;
1781
1782 /*
1783 * Count characters past "qoff" twice if they need quoting.
1784 */
1785 while (*p) {
1786 len++;
1787 if (strwchr(xchars, *p++))
1788 len++;
1789 }
1790 ret = malloc((len+1) * sizeof (wchar_t));
1791 if (ret == NULL)
1792 return (ret);
1793 /*
1794 * First copy over the pre quoting offset characters literarily.
1795 */
1796 for (p = ret; --qoff >= 0; ) {
1797 *p++ = *cp++;
1798 }
1799 /*
1800 * Now qoute all needed characters from the input string.
1801 */
1802 while (*cp) {
1803 if (strwchr(xchars, *cp))
1804 *p++ = '\\';
1805 *p++ = *cp++;
1806 }
1807 *p = '\0';
1808 return (ret);
1809 }
1810
1811 /*
1812 * Expand tilde.
1813 * delp returns the number of characters to delete left to the cursor.
1814 * Returns the allocated replacement as wide string.
1815 */
1816 LOCAL wchar_t *
xp_tilde(ns,delp)1817 xp_tilde(ns, delp)
1818 char *ns;
1819 int *delp;
1820 {
1821 struct passwd *pw;
1822 char *ep = NULL; /* Keep GCC happy ;-) */
1823 int dels = 0;
1824
1825 if (*ns == '\0') {
1826 dels = 1;
1827 ep = getcurenv("HOME");
1828 } else if (*ns == '+' && ns[1] == '\0') {
1829 dels = 2;
1830 ep = getcurenv("PWD");
1831 } else if (*ns == '-' && ns[1] == '\0') {
1832 dels = 2;
1833 ep = getcurenv("OLDPWD");
1834 }
1835 if (dels) {
1836 if (ep == NULL)
1837 return (0);
1838 if (delp)
1839 *delp = dels;
1840 return (towcs((wchar_t *)NULL, 0, ep, -1));
1841 }
1842 dels = strlen(ns) + 1;
1843
1844 pw = getpwnam(ns);
1845 endpwent();
1846 if (pw == NULL)
1847 return (0);
1848 if (delp)
1849 *delp = dels;
1850 return (towcs((wchar_t *)NULL, 0, pw->pw_dir, -1));
1851 }
1852
1853 /*
1854 * The characters ' ' ... '&' are handled by the shell parser as word separators
1855 */
1856 LOCAL char wschars[] = " \t<>%|;()&"; /* Chars that are word separators */
1857
1858 /*
1859 * Expand filenames (implement file name completion).
1860 * This is the basic function that either expands or lists filenames.
1861 * It gets called by exp_files() and by show_files().
1862 *
1863 * Returns pointer with string to insert
1864 */
1865 LOCAL wchar_t *
xp_files(lp,cp,show,multip,delp)1866 xp_files(lp, cp, show, multip, delp)
1867 register wchar_t *lp; /* Begin of current line */
1868 register wchar_t *cp; /* Current cursor position */
1869 BOOL show; /* Show list of multi-results */
1870 int *multip; /* Found mult results in non show mode */
1871 int *delp; /* Chars to delete before cursor */
1872 {
1873 Tnode *np;
1874 Tnode *l1;
1875 wchar_t *wp;
1876 wchar_t *wp2 = NULL;
1877 wchar_t *tp;
1878 int len;
1879 int xlen;
1880 int dir = 0;
1881 wchar_t *p1;
1882 wchar_t *p2 = NULL;
1883 char *ns;
1884 int multi = 0; /* No mutiple results found yet */
1885
1886 /*
1887 * Check whether to expand (complete) the filename.
1888 * This depends on the character that is currently under the cursor.
1889 */
1890 if (*cp != '\0' && !strwchr(wschars, *cp)) {
1891 #ifdef __do_beep_when_in_word__
1892 beep();
1893 #endif
1894 return (0);
1895 }
1896 if (show) {
1897 (void) fprintf(stderr, "\r\n");
1898 (void) fflush(stderr);
1899 }
1900 /*
1901 * Set "wp" to current cursor position and then step back into the text
1902 */
1903 wp = cp;
1904 if (*wp == '\0' || strwchr(wschars, *wp))
1905 wp--;
1906
1907
1908 /*
1909 * Step back to the beginning of the current word.
1910 * Do not stop on quoted delimiters.
1911 */
1912 again:
1913 while (wp > lp && !strwchr(wschars, *wp))
1914 wp--;
1915 if (wp > lp && strwchr(wschars, *wp) &&
1916 wp[-1] == '\\') {
1917 wp--;
1918 goto again;
1919 }
1920
1921
1922 /*
1923 * Advance again into current word.
1924 */
1925 if (strwchr(wschars, *wp))
1926 wp++;
1927
1928
1929 len = cp - wp;
1930 tp = malloc((len+2) * sizeof (wchar_t));
1931 if (tp == NULL)
1932 return (0);
1933 wcsncpy(tp, wp, len);
1934 tp[len] = '\0';
1935 if (tp[0] != '~') {
1936 for (wp = tp; *wp; wp++) {
1937 if (wp[0] == '\\' && wp[1] != '\0') {
1938 wchar_t *w1 = wp++;
1939 wchar_t *w2 = wp;
1940 while (*w2)
1941 *w1++ = *w2++;
1942 *w1 = '\0';
1943 len--;
1944 }
1945 }
1946 }
1947 ns = tombs(NULL, 0, tp, -1);
1948 free(tp);
1949 if (ns == NULL)
1950 return (0);
1951
1952 if (ns[0] == '~')
1953 return (xp_tilde(++ns, delp));
1954
1955 /*
1956 * Call path name expand routing from bsh
1957 *
1958 * XXX: If the characters to the left of the cursor contain pattern meta
1959 * XXX: characters that are not escaped, this will cause false matches.
1960 */
1961 np = bexpand(ns);
1962 free(ns);
1963 if (np == NULL) {
1964 #ifdef __do_beep_when_expand_to_null__
1965 beep();
1966 #endif
1967 return (0);
1968 }
1969
1970 /*
1971 * More than one result?
1972 */
1973 if (np->tn_right.tn_node)
1974 multi++;
1975 /* wp = first from list */
1976 wp = towcs((wchar_t *)NULL, 0, np->tn_left.tn_str, -1);
1977 if (wp == NULL)
1978 goto out;
1979 p2 = wp;
1980 wp = xpwcs(wp, len); /* Insert '\\' if needed */
1981 free(p2);
1982 p2 = NULL;
1983 if (wp == NULL)
1984 goto out;
1985 p2 = wcsrchr(wp, '/'); /* Find last slash */
1986 xlen = 0; /* If no slash start from str begin */
1987 if (p2) /* If we found a slash */
1988 xlen = p2 - wp + 1; /* start right to slash */
1989 p2 = NULL;
1990 for (l1 = np; l1 != (Tnode *)NULL; l1 = l1->tn_right.tn_node) {
1991 if (l1->tn_right.tn_node == NULL) {
1992 /*
1993 * Last in list for max. differences to
1994 * compare with first one.
1995 */
1996 p2 = towcs((wchar_t *)NULL, 0, l1->tn_left.tn_str, -1);
1997 }
1998 if (show) {
1999 wchar_t *p;
2000 p = towcs((wchar_t *)NULL, 0, l1->tn_left.tn_str, -1);
2001 if (p) {
2002 writews(&p[xlen]);
2003 writes(" ");
2004 free(p);
2005 }
2006 }
2007 }
2008 /*
2009 * For multiple results, find longest common match.
2010 */
2011 if (multi) {
2012 if (p2 == NULL) /* towcs() in for() loop returned NULL */
2013 goto out;
2014 wp2 = xpwcs(p2, len);
2015 free(p2);
2016 p2 = NULL;
2017 if (wp2 == NULL)
2018 goto out;
2019 for (p1 = wp, p2 = wp2; *p1; ) {
2020 if (*p1++ != *p2++) {
2021 p1--;
2022 break;
2023 }
2024 }
2025 xlen = p1 - wp;
2026 if (*p1 == '\0' && *p2 == '\0') {
2027 p1 = NULL;
2028 }
2029 } else {
2030 xlen = wcslen(wp);
2031 p1 = NULL;
2032 if (p2)
2033 free(p2);
2034 }
2035 p2 = NULL;
2036
2037 if (!show) {
2038 if (p1 == NULL)
2039 dir = is_dir(np->tn_left.tn_str);
2040 if ((xlen - len) > 0 || dir) {
2041 p2 = malloc((xlen-len+2) * sizeof (wchar_t));
2042 } else {
2043 /*
2044 * Nothing to add.
2045 */
2046 p2 = NULL;
2047 #ifdef __do_beep_when_nothing_to_add__
2048 beep();
2049 #endif
2050 if (multip)
2051 *multip = multi;
2052 }
2053 }
2054 /*
2055 * xlen-len may be negative in case that there was a false match
2056 * that results in directory entries that do not start with
2057 * the last word in the line. This happens when there are unescaped
2058 * pattern meta characters in the last word on the line.
2059 */
2060 if (!show && p2 && (xlen-len) >= 0) {
2061 wcsncpy(p2, &wp[len], xlen-len);
2062 if (p1) {
2063 if ((xlen - len) == 0)
2064 beep();
2065 p2[xlen-len] = '\0';
2066 } else {
2067 p2[xlen-len] = dir ? '/' : ' ';
2068 p2[xlen-len+1] = '\0';
2069 }
2070 } else {
2071 p2 = NULL;
2072 }
2073 out:
2074 freetree(np);
2075 if (wp)
2076 free(wp);
2077 if (wp2)
2078 free(wp2);
2079 return (p2);
2080 }
2081
2082
2083 /*
2084 * Expand filenames (implement file name completion).
2085 * Insert expansion result into current line if applicable.
2086 */
2087 LOCAL wchar_t *
exp_files(lpp,cp,lenp,maxlenp,multip)2088 exp_files(lpp, cp, lenp, maxlenp, multip)
2089 register wchar_t **lpp;
2090 register wchar_t *cp;
2091 unsigned *lenp;
2092 unsigned *maxlenp;
2093 int *multip;
2094 {
2095 wchar_t *p;
2096 int diff;
2097 int del = 0;
2098
2099 p = xp_files(*lpp, cp, FALSE, multip, &del);
2100 if (p) {
2101 diff = wcslen(p);
2102 if (*lenp + diff >= *maxlenp) {
2103 diff = ((diff + LINEQUANT)/LINEQUANT) * LINEQUANT;
2104 *maxlenp += diff;
2105 diff = cp - *lpp;
2106 *lpp = new_line(*lpp, *lenp, *maxlenp);
2107 if (*lpp == NULL) {
2108 free(p);
2109 return ((wchar_t *)NULL);
2110 }
2111 cp = *lpp + diff;
2112 }
2113 while (--del >= 0 && cp > *lpp) {
2114 del_char(--cp);
2115 (*lenp)--;
2116 }
2117 cp = insert(cp, p, lenp);
2118 free(p);
2119 } else {
2120 beep();
2121 }
2122 return (cp);
2123 }
2124
2125
2126 /*
2127 * Show filename list (implement file name completion).
2128 */
2129 LOCAL void
show_files(lp,cp)2130 show_files(lp, cp)
2131 register wchar_t *lp;
2132 register wchar_t *cp;
2133 {
2134 wchar_t *p;
2135
2136 p = xp_files(lp, cp, TRUE, NULL, NULL);
2137 if (p)
2138 free(p);
2139 redisp(lp, cp);
2140 }
2141
2142
2143 /*
2144 * Get an ESC request
2145 */
2146 LOCAL int
get_request()2147 get_request()
2148 {
2149 register int c;
2150
2151 set_insert_modes(infile);
2152 c = _nextwc();
2153 if (c == UPWARD || c == DOWNWARD)
2154 return (c | SEARCH);
2155 if (c == '\n' || c == '\r')
2156 return (RESTORE);
2157 return (c);
2158 }
2159
2160
2161 /*
2162 * Implementation of functions that follow an ESC in an ESC sequence.
2163 */
2164 LOCAL wchar_t *
esc_process(xc,lp,cp,lenp)2165 esc_process(xc, lp, cp, lenp)
2166 register int xc;
2167 register wchar_t *lp;
2168 register wchar_t *cp;
2169 unsigned *lenp;
2170 {
2171 register wchar_t *pp;
2172 register int dl = 0;
2173
2174 switch (xc) {
2175
2176 case BACKSPACE:
2177 if (cp == lp) {
2178 beep();
2179 return (cp);
2180 }
2181 cp--;
2182 while (cp >= lp && *cp == BLANK) {
2183 --cp, dl++;
2184 }
2185 while (cp >= lp && *cp != BLANK)
2186 dl += chlen(*cp--);
2187 backspace(dl);
2188 return (++cp);
2189 case FORWARD:
2190 if (!*cp)
2191 beep();
2192 else {
2193 while (*cp && *cp != BLANK)
2194 writec(*cp++);
2195 while (*cp == BLANK)
2196 writec(*cp++);
2197 bflush();
2198 }
2199 return (cp);
2200 case DELETE:
2201 case CTRLD:
2202 pp = cp;
2203 if (xc == CTRLD) {
2204 if (!*cp) {
2205 beep();
2206 return (cp);
2207 }
2208 while (*pp && *pp != BLANK)
2209 dl += chlen(*pp++);
2210 while (*pp == BLANK)
2211 dl += chlen(*pp++);
2212 } else {
2213 if (cp == lp) {
2214 beep();
2215 return (cp);
2216 }
2217 while (--cp > lp && *cp == BLANK);
2218 while (cp > lp && *(cp-1) != BLANK)
2219 --cp;
2220 dl = linediff(cp, pp);
2221 backspace(dl);
2222 }
2223 /*
2224 * strcpy() doesn't handle overlapped buffers
2225 */
2226 { wchar_t *p2 = cp; wchar_t *p1 = pp;
2227 while ((*p2++ = *p1++) != '\0')
2228 ;
2229 }
2230 writews(cp);
2231 space(dl);
2232 backspace(linelen(cp) + dl);
2233 (*lenp) -= (pp - cp);
2234 return (cp);
2235 default:
2236 beep();
2237 return (cp);
2238 }
2239 }
2240
2241
2242 /*
2243 * Interactive line editor function.
2244 * Allows only a limited subset if the editing functions.
2245 */
2246 LOCAL wchar_t *
sget_line()2247 sget_line()
2248 {
2249 wchar_t *lp;
2250 BOOL edit = TRUE;
2251 int cmd;
2252 register HISTPTR tmp_line;
2253
2254 tmp_line = mktmp();
2255 if (tmp_line == NULL)
2256 return (NULL);
2257 while (edit) {
2258 cmd = edit_line(tmp_line);
2259 switch (cmd) {
2260
2261 case UPWARD:
2262 case DOWNWARD:
2263 case SEARCHUP:
2264 case SEARCHDOWN:
2265 case RESTORE:
2266 beep();
2267 break;
2268 case '\0':
2269 case EOF:
2270 return (NULL);
2271 case '\r':
2272 case '\n':
2273 edit = FALSE;
2274 }
2275 }
2276 lp = tmp_line->h_line;
2277 append_hline(lp, tmp_line->h_len);
2278 free((char *)tmp_line);
2279 return (lp);
2280 }
2281
2282
2283 /*
2284 * Interactive line editor function.
2285 * Allows all editing functions.
2286 */
2287 LOCAL wchar_t *
iget_line()2288 iget_line()
2289 {
2290 register wchar_t *lp;
2291 wchar_t *np;
2292 BOOL edit = TRUE;
2293 register int cmd = '\0';
2294 register HISTPTR cur_line;
2295 register HISTPTR tmp_line; /* empty tmp */
2296 register HISTPTR save_line;
2297 register HISTPTR etmp_line = (HISTPTR) NULL; /* tmp copy */
2298 register HISTPTR orig_line = (HISTPTR) NULL; /* tmp orig */
2299
2300 save_line = cur_line = tmp_line = mktmp();
2301 if (cur_line == NULL)
2302 return (NULL);
2303
2304 lp = cur_line->h_line;
2305 cur_line->h_prev = last_line;
2306 cur_line->h_next = first_line;
2307 #ifdef DEBUG
2308 printf("History: first at %p, last at %p\r\n", first_line, last_line);
2309 #endif
2310 while (edit) {
2311 if (!(cmd & ~BYTEMASK))
2312 save_line = cur_line;
2313 cmd = edit_line(cur_line);
2314 #ifdef DEBUG
2315 printf("Edited line at %p, line at %p '%s'.\r\n", cur_line,
2316 cur_line->h_line, cur_line->h_line);
2317 #endif
2318 switch (cmd) {
2319
2320 case UPWARD:
2321 case DOWNWARD:
2322 case SEARCHUP:
2323 case SEARCHDOWN:
2324 #ifdef DEBUG
2325 printf("Changing line at %p.\r\n", cur_line);
2326 #endif
2327 if ((cmd & BYTEMASK) == UPWARD)
2328 cur_line = cur_line->h_prev;
2329 else
2330 cur_line = cur_line->h_next;
2331 if (etmp_line) {
2332 if (etmp_line == save_line)
2333 save_line = orig_line;
2334 unhold_line(etmp_line, orig_line);
2335 etmp_line = NULL;
2336 }
2337 #ifdef DEBUG
2338 printf("New line at %p.\r\n", cur_line);
2339 #endif
2340 if (cur_line == (HISTPTR) NULL)
2341 cur_line = tmp_line;
2342 if ((cmd & ~BYTEMASK) == SEARCH) {
2343 cur_line = match_input(cur_line,
2344 tmp_line, cmd == SEARCHUP);
2345 }
2346 orig_line = cur_line;
2347 etmp_line = cur_line = hold_line(cur_line);
2348 if (etmp_line == NULL)
2349 cur_line = orig_line;
2350 break;
2351 case RESTORE:
2352 if (etmp_line) {
2353 if (etmp_line == save_line)
2354 save_line = orig_line;
2355 unhold_line(etmp_line, orig_line);
2356 etmp_line = NULL;
2357 }
2358 cur_line = save_line;
2359 break;
2360 case '\0':
2361 case EOF:
2362 return (NULL);
2363 case '\r':
2364 case '\n':
2365 #ifdef DEBUG
2366 printf("End of line.\r\n");
2367 #endif
2368 edit = FALSE;
2369 }
2370 }
2371 lp = cur_line->h_line;
2372 np = makewstr(lp);
2373 if (cur_line == tmp_line || cur_line == etmp_line) {
2374 #ifdef DEBUG
2375 printf("tmp_line to append....\r\n");
2376 #endif
2377 if (*lp)
2378 append_wline(lp, cur_line->h_len, cur_line->h_pos);
2379 } else {
2380 #ifdef DEBUG
2381 printf("previous line to move....\r\n");
2382 #endif
2383 if (*lp)
2384 move_to_end(cur_line);
2385 }
2386
2387 stripout();
2388 if ((tmp_line->h_flags & F_TMP) != 0) {
2389 free_line(tmp_line);
2390 }
2391 if (etmp_line && (etmp_line->h_flags & F_TMP) != 0) {
2392 free_line(etmp_line);
2393 }
2394 #ifdef DEBUG
2395 printf("History: first at %p, last at %p\r\n", first_line, last_line);
2396 #endif
2397 return (np);
2398 }
2399
2400
2401 /*
2402 * Read a line using func pointer and return result in allocated string.
2403 */
2404 /* VARARGS1 */
2405 EXPORT char *
2406 make_line(f, arg)
2407 register int (*f) __PR((MYFILE *));
2408 register MYFILE *arg;
2409 {
2410 register unsigned maxl;
2411 register unsigned llen;
2412 char *lp;
2413 register char *p;
2414 register int c;
2415
2416 #ifdef DEBUG
2417 printf(" make_line\r\n");
2418 #endif
2419 lp = p = malloc((maxl = LINEQUANT) * sizeof (*lp));
2420 if (lp == NULL)
2421 return (lp);
2422 llen = 0;
2423 for (;;) {
2424 if ((c = (*f)(arg)) == EOF || c == '\n' || c == '\205') {
2425 if (c == EOF)
2426 delim = EOF;
2427 else
2428 delim = '\n'; /* XXX ??? \205 ??? */
2429 *p = 0;
2430 return (lp);
2431 }
2432 *p++ = (char)c;
2433 if (++llen == maxl) {
2434 maxl += LINEQUANT;
2435 lp = new_line(lp, llen, maxl);
2436 if (lp == NULL)
2437 return (lp);
2438 p = lp + llen;
2439 }
2440 }
2441 }
2442
2443
2444 /*
2445 * Read a line from a file in a way compatible to [is]get_line().
2446 */
2447 LOCAL char *
fread_line(f)2448 fread_line(f)
2449 MYFILE *f;
2450 {
2451 extern int fgetc __PR((MYFILE *));
2452
2453 return (make_line(fgetc, f));
2454 }
2455
2456
2457 /*
2458 * External interface to the line editor.
2459 * Use fread_line() if input is from a file.
2460 * Use iget_line() if input is from tty and promt counter is 0.
2461 * Use sget_line() if input is from tty and promt counter is > 0.
2462 */
2463 EXPORT char *
get_line(n,f)2464 get_line(n, f)
2465 int n; /* Prompt index */
2466 MYFILE *f; /* FILE * to read from */
2467 {
2468 if (line_pointer) {
2469 free(line_pointer);
2470 line_pointer = NULL;
2471 }
2472 if (wline_pointer) {
2473 free(wline_pointer);
2474 wline_pointer = NULL;
2475 }
2476 if (ttyflg && mp_init) {
2477 map_init();
2478 term_init();
2479 }
2480 if (prflg) {
2481 iprompt = prompts[n?1:0];
2482 (void) fprintf(stderr, "%s", iprompt);
2483 (void) fflush(stderr);
2484 }
2485 if (!ttyflg) {
2486 #ifdef DEBUG
2487 printf(" get_line: fread_line(%p).\r\n", f);
2488 #endif
2489 line_pointer = fread_line(f);
2490 if (line_pointer == NULL)
2491 return (nullstr);
2492 return (line_pointer);
2493 } else {
2494 infile = f;
2495 tty_init();
2496 if (n)
2497 wline_pointer = sget_line(); /* Editor w/o history */
2498 else
2499 wline_pointer = iget_line(); /* Editor w. history */
2500 tty_term();
2501 }
2502
2503 if (wline_pointer == NULL)
2504 return (nullstr);
2505
2506 line_pointer = tombs(NULL, 0, wline_pointer, -1);
2507 return (line_pointer?line_pointer:nullstr);
2508 }
2509
2510 /*
2511 * Write current content of the history to an open file.
2512 * If we like to be compatible to what was used by "#v on" on UNOS,
2513 * add {} brackets if we are writing to stdout.
2514 */
2515 EXPORT int
put_history(f,flg,first,last,subst)2516 put_history(f, flg, first, last, subst)
2517 register MYFILE *f;
2518 register int flg;
2519 int first;
2520 int last;
2521 char *subst;
2522 {
2523 register HISTPTR p;
2524 register HISTPTR pe = last_line;
2525 size_t oldlen = 0; /* make silly GCC happy */
2526 char *eqp = NULL; /* make silly GCC happy */
2527
2528 if (f == NULL)
2529 f = gstd[1];
2530
2531 if (subst) {
2532 eqp = strchr(subst, '=');
2533 oldlen = eqp++ - subst;
2534 }
2535
2536 if (first < 0) {
2537 register int i;
2538
2539 for (i = 0, p = last_line; p; p = p->h_prev) {
2540 if (--i == first)
2541 break;
2542 }
2543 if (p == NULL)
2544 p = first_line;
2545 } else if (first > 0) {
2546 for (p = last_line; p; p = p->h_prev) {
2547 if (p->h_number == first)
2548 break;
2549 }
2550 if (p == NULL)
2551 return (-1);
2552 } else {
2553 p = first_line;
2554 }
2555 if (last) {
2556 for (pe = last_line; pe; pe = pe->h_prev) {
2557 if (pe->h_number == last)
2558 break;
2559 }
2560 if (pe == NULL)
2561 pe = last_line;
2562 }
2563 if (flg & HI_REVERSE) {
2564 HISTPTR pt;
2565 pt = p;
2566 p = pe;
2567 pe = pt;
2568 }
2569 for (; p; p = (flg & HI_REVERSE) ? p->h_prev : p->h_next) {
2570 if (ctlc && (flg & HI_INTR))
2571 break;
2572 if (f == stdout) { /* Used to print history */
2573 register wchar_t *cp;
2574 register wchar_t wc;
2575 char s[16];
2576 #ifdef JOS_VERBOSE /* Make output similar to JOS #v on */
2577 _writes("{ ");
2578 #endif
2579
2580 if ((flg & HI_NONUM) == 0) {
2581 js_snprintf(s, sizeof (s), "%d", p->h_number);
2582 _writes(s);
2583 }
2584 if (flg & HI_TAB)
2585 putch('\t');
2586 for (cp = p->h_line; (wc = *cp++) != '\0'; ) {
2587 (flg & HI_PRETTYP) ?
2588 prettyp(wc, &buf) :
2589 putch(wc);
2590 if (wc == '\n')
2591 putch('\t');
2592 }
2593
2594 #ifdef JOS_VERBOSE /* Make output similar to JOS #v on */
2595 _writes(" }");
2596 #endif
2597 putch('\n');
2598 } else { /* This is the save_history() variant */
2599 char line[512];
2600 char *lp;
2601 char *lp2;
2602 #ifndef USE_ANSI_NL_SEPARATOR
2603 char *cp;
2604 #endif
2605
2606 lp2 = lp = tombs(line, sizeof (line), p->h_line, -1);
2607 if (lp == NULL)
2608 continue;
2609
2610 if (subst && strncmp(lp, subst, oldlen) == 0) {
2611 lp2 += oldlen;
2612 fprintf(f, "%s", eqp);
2613 }
2614 #ifndef USE_ANSI_NL_SEPARATOR
2615 if (flg & HI_ANSI_NL) {
2616 for (cp = lp2; *cp; cp++) {
2617 if (*cp == '\n')
2618 *cp = '\205';
2619 }
2620 }
2621 #endif
2622 /*
2623 * XXX could be fprintf(f, "%ls\n", p->h_line);
2624 */
2625 fprintf(f, "%s\n", lp2);
2626 if (lp != line)
2627 free(lp);
2628 }
2629 if (p == pe)
2630 break;
2631 }
2632 if (f == stdout)
2633 bflush();
2634
2635 return (0);
2636 }
2637
2638 EXPORT HISTPTR
_search_history(flg,first,pat)2639 _search_history(flg, first, pat)
2640 register int flg;
2641 int first;
2642 char *pat;
2643 {
2644 register HISTPTR p;
2645 wchar_t wline[512];
2646 wchar_t *wp = NULL;
2647 size_t wlen = 0; /* make silly GCC happy */
2648
2649 if (pat) {
2650 wp = towcs(wline, sizeof (wline) / sizeof (wline[0]), pat, -1);
2651 if (wp == NULL)
2652 return ((HISTPTR)NULL);
2653 wlen = wcslen(wp);
2654 }
2655 if (first < 0) {
2656 register int i;
2657
2658 for (i = 0, p = last_line; p; p = p->h_prev) {
2659 if (--i == first)
2660 break;
2661 }
2662 if (p == NULL)
2663 p = first_line;
2664 } else if (first > 0) {
2665 for (p = last_line; p; p = p->h_prev) {
2666 if (p->h_number == first)
2667 break;
2668 }
2669 if (p == NULL)
2670 return (p);
2671 } else {
2672 p = first_line;
2673 }
2674 if (pat) {
2675 for (; p; p = p->h_next) {
2676 if (ctlc && (flg & HI_INTR)) {
2677 p = NULL;
2678 break;
2679 }
2680 if (wcsncmp(wp, p->h_line, wlen) == 0)
2681 break;
2682 }
2683 if (wp != wline)
2684 free(wp);
2685 }
2686 return (p);
2687 }
2688
2689 EXPORT int
search_history(flg,first,pat)2690 search_history(flg, first, pat)
2691 register int flg;
2692 int first;
2693 char *pat;
2694 {
2695 register HISTPTR p = _search_history(flg, first, pat);
2696
2697 if (p == NULL)
2698 return (-1);
2699 return (p->h_number);
2700 }
2701
2702 EXPORT int
remove_history(flg,first,pat)2703 remove_history(flg, first, pat)
2704 register int flg;
2705 int first;
2706 char *pat;
2707 {
2708 register HISTPTR p = _search_history(flg, first, pat);
2709
2710 if (p == NULL)
2711 return (-1);
2712
2713 remove_line(p);
2714 return (0);
2715 }
2716
2717 #define HF_READ 0
2718 #define HF_SAVE 1
2719
2720 LOCAL char *
get_histfname(flag)2721 get_histfname(flag)
2722 int flag;
2723 {
2724 if (hfilename)
2725 free(hfilename);
2726 /*
2727 * First check for "HISTFILE" as this is required by POSIX.
2728 */
2729 if ((hfilename = getcurenv(histfilename)) != NULL) {
2730 hfilename = concat(hfilename, (char *)NULL);
2731 } else {
2732 /*
2733 * Our historic name is "$HOME/.history", use it whenever
2734 * "HISTFILE" is not present.
2735 */
2736 hfilename = concat(inithome, slash, historyname, (char *)NULL);
2737 if (flag == HF_SAVE && !ev_eql(savehistname, on))
2738 return (NULL);
2739 }
2740 return (hfilename);
2741 }
2742
2743 /*
2744 * Save the history by writing to ~/.history
2745 */
2746 EXPORT void
save_history(flg)2747 save_history(flg)
2748 int flg;
2749 {
2750 MYFILE *f;
2751 char *hfname;
2752
2753 if (no_lines == 0) /* don't damage history File */
2754 return;
2755 hfname = get_histfname(HF_SAVE);
2756 if (hfname == NULL)
2757 return;
2758 f = fileopen(hfname, for_wct);
2759 if (f) {
2760 put_history(f, flg | HI_ANSI_NL, 0, 0, NULL);
2761 fclose(f);
2762 }
2763 }
2764
2765
2766 /*
2767 * Init the history by reading from ~/.history
2768 */
2769 EXPORT void
read_init_history()2770 read_init_history()
2771 {
2772 MYFILE *f;
2773 char *hfname;
2774
2775 hfname = get_histfname(HF_READ);
2776 if (hfname == NULL)
2777 return;
2778 f = fileopen(hfname, for_read);
2779 if (f) {
2780 readhistory(f);
2781 fclose(f);
2782 }
2783 }
2784
2785 /*
2786 * Read in the history from a file.
2787 * Used to init history and for source -h
2788 */
2789 EXPORT void
readhistory(f)2790 readhistory(f)
2791 register MYFILE *f;
2792 {
2793 #define BUF_SIZE 8192 /* XXX Dymanic resize ??? */
2794 char rbuf[BUF_SIZE+1]; /* + space for null byte */
2795 register char *s = rbuf; /* Start of current line */
2796 register char *ep; /* End of current line */
2797 register int len;
2798 register int amt;
2799
2800 amt = BUF_SIZE;
2801 rbuf[amt] = '\0'; /* Final null byte after rbuf */
2802 while ((amt = fileread(f, s, amt)) > 0) {
2803 amt += s - rbuf; /* Continue to work on whole rest */
2804 s = rbuf;
2805
2806 again:
2807 ep = strchr(s, '\n');
2808 if (ep == NULL && s > rbuf && amt >= BUF_SIZE) {
2809 /*
2810 * If no '\n' could be found, we need to check whether
2811 * we are in the middle of a line. If the buffer was
2812 * not full, we are at EOF already.
2813 */
2814 amt = amt - (s - rbuf); /* Compute unprocessed amt */
2815 movebytes(s, rbuf, amt); /* Move to the start of buf */
2816 s = &rbuf[amt]; /* Point past old content */
2817 amt = BUF_SIZE - amt; /* Compute remaining space */
2818 continue; /* Read again to fill buf */
2819 }
2820 if (ep) /* Current line ends in '\n' */
2821 *ep = '\0'; /* so clear it */
2822
2823 /*
2824 * Skip bash timestamps
2825 */
2826 if (s[0] == '#' && s[1] == '+') {
2827 register char *p;
2828
2829 for (p = &s[2]; *p != '\0'; p++)
2830 if (!_isdigit((unsigned char)*p))
2831 break;
2832 if (*p == '\0')
2833 goto endline;
2834 }
2835
2836 #ifdef USE_ANSI_NL_SEPARATOR
2837 len = strlen(s);
2838 #else
2839 {
2840 char *cp;
2841 for (cp = s, len = 0; *cp; cp++, len++) {
2842 if (*cp == '\205')
2843 *cp = '\n';
2844 }
2845 }
2846 #endif
2847 #ifdef DEBUG
2848 fprintf(stderr, "appending: %d bytes: %s\r\n", len, s);
2849 #endif
2850 append_line(s, (unsigned)len+1, len);
2851
2852 endline:
2853 if (ep) { /* Found '\n', check rest */
2854 s = &ep[1];
2855 if ((s - rbuf) >= amt && amt < BUF_SIZE) /* EOF */
2856 break;
2857 goto again;
2858 } else {
2859 if (amt < BUF_SIZE) /* EOF */
2860 break;
2861 s = rbuf;
2862 amt = BUF_SIZE;
2863 }
2864 }
2865 }
2866
2867 #include <schily/termcap.h>
2868 LOCAL char *KE; /* Keypad end transmit mode "ke" */
2869 LOCAL char *KS; /* Keypad start transmit mode "ks" */
2870 LOCAL char *VE; /* Visual end sequence "ve" */
2871 LOCAL char *VS; /* Visual start sequence "vs" */
2872
2873 LOCAL char **tstrs[] = {
2874 &KE, &KS, &VE, &VS,
2875 };
2876
2877 LOCAL void
term_init()2878 term_init()
2879 {
2880 register char *np = "keksvevs";
2881 register char ***sp = tstrs;
2882
2883 if (KE) free(KE);
2884 if (KS) free(KS);
2885 if (VE) free(VE);
2886 if (VS) free(VS);
2887 do {
2888 *(*sp++) = tgetstr(np, NULL);
2889 np += 2;
2890 } while (*np);
2891 }
2892
2893 #define f_putch ((int (*)__PR((int)))putch)
2894
2895 LOCAL void
tty_init()2896 tty_init()
2897 {
2898 tputs(VS, 0, f_putch); /* make cursor visible */
2899 tputs(KS, 0, f_putch); /* start keypad transmit mode */
2900 }
2901
2902 LOCAL void
tty_term()2903 tty_term()
2904 {
2905 tputs(VE, 0, f_putch);
2906 tputs(KE, 0, f_putch);
2907 bflush();
2908 }
2909
2910
2911 #ifdef DO_DEBUG
2912 #include <schily/varargs.h>
2913 /*
2914 * Do formatted debugging output to the console
2915 */
2916 /* PRINTFLIKE1 */
2917 #ifdef PROTOTYPES
2918 LOCAL void
cdbg(char * fmt,...)2919 cdbg(char *fmt, ...)
2920 #else
2921 LOCAL void
2922 cdbg(fmt, va_alist)
2923 char *fmt;
2924 va_dcl
2925 #endif
2926 {
2927 char lbuf[1024];
2928 va_list args;
2929 static int f;
2930 int len;
2931
2932 #ifdef PROTOTYPES
2933 va_start(args, fmt);
2934 #else
2935 va_start(args);
2936 #endif
2937 #ifdef HAVE_VSNPRINTF
2938 len = vsnprintf(lbuf, sizeof (lbuf), fmt, args);
2939 #else
2940 len = snprintf(lbuf, sizeof (lbuf), "%r", fmt, args);
2941 #endif
2942 va_end(args);
2943
2944 if (f == 0) {
2945 char *cname;
2946 if ((cname = getcurenv("BSH_DBGTERM")) == NULL)
2947 cname = "/dev/console";
2948
2949 f = open(cname, O_WRONLY);
2950 if (f == 0)
2951 return;
2952 #ifdef F_SETFD
2953 fcntl(f, F_SETFD, FD_CLOEXEC); /* Set close on exec */
2954 #endif
2955 }
2956 write(f, lbuf, len);
2957 }
2958 #endif /* DO_DEBUG */
2959
2960 #endif /* INTERACTIVE */
2961