1 /*
2  * zle_misc.c - miscellaneous editor routines
3  *
4  * This file is part of zsh, the Z shell.
5  *
6  * Copyright (c) 1992-1997 Paul Falstad
7  * All rights reserved.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and to distribute modified versions of this software for any
12  * purpose, provided that the above copyright notice and the following
13  * two paragraphs appear in all copies of this software.
14  *
15  * In no event shall Paul Falstad or the Zsh Development Group be liable
16  * to any party for direct, indirect, special, incidental, or consequential
17  * damages arising out of the use of this software and its documentation,
18  * even if Paul Falstad and the Zsh Development Group have been advised of
19  * the possibility of such damage.
20  *
21  * Paul Falstad and the Zsh Development Group specifically disclaim any
22  * warranties, including, but not limited to, the implied warranties of
23  * merchantability and fitness for a particular purpose.  The software
24  * provided hereunder is on an "as is" basis, and Paul Falstad and the
25  * Zsh Development Group have no obligation to provide maintenance,
26  * support, updates, enhancements, or modifications.
27  *
28  */
29 
30 #include "zle.mdh"
31 #include "zle_misc.pro"
32 
33 /* insert a zle string, with repetition and suffix removal */
34 
35 /**/
36 void
doinsert(ZLE_STRING_T zstr,int len)37 doinsert(ZLE_STRING_T zstr, int len)
38 {
39     ZLE_STRING_T s;
40     ZLE_CHAR_T c1 = *zstr;	     /* first character */
41     int neg = zmult < 0;             /* insert *after* the cursor? */
42     int m = neg ? -zmult : zmult;    /* number of copies to insert */
43     int count;
44 
45     UNMETACHECK();
46 
47     iremovesuffix(c1, 0);
48     invalidatelist();
49 
50     /* In overwrite mode, don't replace newlines. */
51     if (insmode || zleline[zlecs] == ZWC('\n'))
52 	spaceinline(m * len);
53     else
54     {
55 	int pos = zlecs, diff, i;
56 #ifdef MULTIBYTE_SUPPORT
57 	/*
58 	 * Calculate the number of character positions we are
59 	 * going to be using.  The algorithm is that
60 	 * anything that shows up as a logical single character
61 	 * (i.e. even if control, or double width, or with combining
62 	 * characters) is treated as 1 for the purpose of replacing
63 	 * what's there already.
64 	 *
65 	 * This can cause inserting of a combining character in
66 	 * places where it should overwrite, such as the start
67 	 * of a line.  However, combining characters aren't
68 	 * useful there anyway and this doesn't cause any
69 	 * particular harm.
70 	 */
71 	for (i = 0, count = 0; i < len * m; i++) {
72 	    if (!IS_COMBINING(zstr[i]))
73 		count++;
74 	}
75 #else
76 	count = len * m;
77 #endif
78 	/*
79 	 * Ensure we replace a complete combining characterfor each
80 	 * character we overwrite. Switch to inserting at first newline.
81 	 */
82 	for (i = count; pos < zlell && zleline[pos] != ZWC('\n') && i--; ) {
83 	    INCPOS(pos);
84 	}
85 	/*
86 	 * Calculate how many raw line places we need.
87 	 * pos - zlecs is the raw line distance we're replacing,
88 	 * m * len the number we're inserting.
89 	 */
90 	diff = pos - zlecs - m * len;
91 	if (diff < 0) {
92 	    spaceinline(-diff);
93 	} else if (diff > 0) {
94 	    /*
95 	     * We use shiftchars() here because we don't
96 	     * want combining char alignment fixed up: we
97 	     * are going to write over any that remain.
98 	     */
99 	    shiftchars(zlecs, diff);
100 	}
101     }
102     while (m--)
103 	for (s = zstr, count = len; count; s++, count--)
104 	    zleline[zlecs++] = *s;
105     if (neg)
106 	zlecs += zmult * len;
107     /* if we ended up on a combining character, skip over it */
108     CCRIGHT();
109 }
110 
111 /**/
112 mod_export int
selfinsert(UNUSED (char ** args))113 selfinsert(UNUSED(char **args))
114 {
115     ZLE_CHAR_T tmp;
116 
117 #ifdef MULTIBYTE_SUPPORT
118     /* may be redundant with getkeymapcmd(), but other widgets call here too */
119     if (!lastchar_wide_valid)
120 	if (getrestchar(lastchar, NULL, NULL) == WEOF)
121 	    return 1;
122 #endif
123     tmp = LASTFULLCHAR;
124     doinsert(&tmp, 1);
125     return 0;
126 }
127 
128 /**/
129 mod_export void
fixunmeta(void)130 fixunmeta(void)
131 {
132     lastchar &= 0x7f;
133     if (lastchar == '\r')
134 	lastchar = '\n';
135 #ifdef MULTIBYTE_SUPPORT
136     /*
137      * TODO: can we do this better?
138      * We need a wide character to insert.
139      * selfinsertunmeta is intrinsically problematic
140      * with multibyte input.
141      */
142     lastchar_wide = (ZLE_INT_T)lastchar;
143     lastchar_wide_valid = 1;
144 #endif
145 }
146 
147 /**/
148 mod_export int
selfinsertunmeta(char ** args)149 selfinsertunmeta(char **args)
150 {
151     fixunmeta();
152     return selfinsert(args);
153 }
154 
155 /**/
156 int
deletechar(char ** args)157 deletechar(char **args)
158 {
159     int n;
160     if (zmult < 0) {
161 	int ret;
162 	zmult = -zmult;
163 	ret = backwarddeletechar(args);
164 	zmult = -zmult;
165 	return ret;
166     }
167 
168     n = zmult;
169     while (n--) {
170 	if (zlecs == zlell)
171 	    return 1;
172 	INCCS();
173     }
174     backdel(zmult, 0);
175     return 0;
176 }
177 
178 /**/
179 int
backwarddeletechar(char ** args)180 backwarddeletechar(char **args)
181 {
182     if (zmult < 0) {
183 	int ret;
184 	zmult = -zmult;
185 	ret = deletechar(args);
186 	zmult = -zmult;
187 	return ret;
188     }
189     backdel(zmult > zlecs ? zlecs : zmult, 0);
190     return 0;
191 }
192 
193 /**/
194 int
killwholeline(UNUSED (char ** args))195 killwholeline(UNUSED(char **args))
196 {
197     int i, fg, n = zmult;
198 
199     if (n < 0)
200 	return 1;
201     while (n--) {
202 	if ((fg = (zlecs && zlecs == zlell)))
203 	    zlecs--;
204 	while (zlecs && zleline[zlecs - 1] != '\n')
205 	    zlecs--;
206 	for (i = zlecs; i != zlell && zleline[i] != '\n'; i++);
207 	forekill(i - zlecs + (i != zlell), fg ? (CUT_FRONT|CUT_RAW) : CUT_RAW);
208     }
209     clearlist = 1;
210     return 0;
211 }
212 
213 /**/
214 int
killbuffer(UNUSED (char ** args))215 killbuffer(UNUSED(char **args))
216 {
217     zlecs = 0;
218     forekill(zlell, CUT_RAW);
219     clearlist = 1;
220     return 0;
221 }
222 
223 /**/
224 int
backwardkillline(char ** args)225 backwardkillline(char **args)
226 {
227     int i = 0, n = zmult;
228 
229     if (n < 0) {
230 	int ret;
231 	zmult = -n;
232 	ret = killline(args);
233 	zmult = n;
234 	return ret;
235     }
236     while (n--) {
237 	if (zlecs && zleline[zlecs - 1] == '\n')
238 	    zlecs--, i++;
239 	else
240 	    while (zlecs && zleline[zlecs - 1] != '\n')
241 		zlecs--, i++;
242     }
243     forekill(i, CUT_FRONT|CUT_RAW);
244     clearlist = 1;
245     return 0;
246 }
247 
248 #ifdef MULTIBYTE_SUPPORT
249 /*
250  * Transpose the chunk of the line from start to middle with
251  * that from middle to end.
252  */
253 
254 static void
transpose_swap(int start,int middle,int end)255 transpose_swap(int start, int middle, int end)
256 {
257     int len1, len2;
258     ZLE_STRING_T first;
259 
260     len1 = middle - start;
261     len2 = end - middle;
262 
263     first = (ZLE_STRING_T)zalloc(len1 * ZLE_CHAR_SIZE);
264     ZS_memcpy(first, zleline + start, len1);
265     /* Move may be overlapping... */
266     ZS_memmove(zleline + start, zleline + middle, len2);
267     ZS_memcpy(zleline + start + len2, first, len1);
268     zfree(first, len1 * ZLE_CHAR_SIZE);
269 }
270 #endif
271 
272 /**/
273 int
gosmacstransposechars(UNUSED (char ** args))274 gosmacstransposechars(UNUSED(char **args))
275 {
276     if (zlecs < 2 || zleline[zlecs - 1] == '\n' || zleline[zlecs - 2] == '\n') {
277 	int twice = (zlecs == 0 || zleline[zlecs - 1] == '\n');
278 
279 	if (zlecs == zlell || zleline[zlecs] == '\n')
280 	    return 1;
281 
282 	INCCS();
283 	if (twice) {
284 	    if (zlecs == zlell || zleline[zlecs] == '\n')
285 		return 1;
286 	    INCCS();
287 	}
288     }
289 #ifdef MULTIBYTE_SUPPORT
290     {
291 	int start, middle;
292 
293 	middle = zlecs;
294 	DECPOS(middle);
295 
296 	start = middle;
297 	DECPOS(start);
298 
299 	transpose_swap(start, middle, zlecs);
300     }
301 #else
302     {
303 	ZLE_CHAR_T cc = zleline[zlecs - 2];
304 	zleline[zlecs - 2] = zleline[zlecs - 1];
305 	zleline[zlecs - 1] = cc;
306     }
307 #endif
308     return 0;
309 }
310 
311 /**/
312 int
transposechars(UNUSED (char ** args))313 transposechars(UNUSED(char **args))
314 {
315     int ct;
316     int n = zmult;
317     int neg = n < 0;
318 
319     if (neg)
320 	n = -n;
321     while (n--) {
322 	if (!(ct = zlecs) || zleline[zlecs - 1] == '\n') {
323 	    if (zlell == zlecs || zleline[zlecs] == '\n')
324 		return 1;
325 	    if (!neg)
326 		INCCS();
327 	    INCPOS(ct);
328 	}
329 	if (neg) {
330 	    if (zlecs && zleline[zlecs - 1] != '\n') {
331 		DECCS();
332 		if (ct > 1 && zleline[ct - 2] != '\n') {
333 		    DECPOS(ct);
334 		}
335 	    }
336 	} else {
337 	    if (zlecs != zlell && zleline[zlecs] != '\n')
338 		INCCS();
339 	}
340 	if (ct == zlell || zleline[ct] == '\n') {
341 	    DECPOS(ct);
342 	}
343 	if (ct < 1 || zleline[ct - 1] == '\n')
344 	    return 1;
345 #ifdef MULTIBYTE_SUPPORT
346 	{
347 	    /*
348 	     * We should keep any accents etc. on their original characters.
349 	     */
350 	    int start = ct, end = ct;
351 	    DECPOS(start);
352 	    INCPOS(end);
353 
354 	    transpose_swap(start, ct, end);
355 	}
356 #else
357 	{
358 	    ZLE_CHAR_T cc = zleline[ct - 1];
359 	    zleline[ct - 1] = zleline[ct];
360 	    zleline[ct] = cc;
361 	}
362 #endif
363     }
364     return 0;
365 }
366 
367 /**/
368 int
poundinsert(UNUSED (char ** args))369 poundinsert(UNUSED(char **args))
370 {
371     zlecs = 0;
372     vifirstnonblank(zlenoargs);
373     if (zleline[zlecs] != '#') {
374 	spaceinline(1);
375 	zleline[zlecs] = '#';
376 	zlecs = findeol();
377 	while(zlecs != zlell) {
378 	    zlecs++;
379 	    vifirstnonblank(zlenoargs);
380 	    spaceinline(1);
381 	    zleline[zlecs] = '#';
382 	    zlecs = findeol();
383 	}
384     } else {
385 	foredel(1, 0);
386 	zlecs = findeol();
387 	while(zlecs != zlell) {
388 	    zlecs++;
389 	    vifirstnonblank(zlenoargs);
390 	    if(zleline[zlecs] == '#')
391 		foredel(1, 0);
392 	    zlecs = findeol();
393 	}
394     }
395     done = 1;
396     return 0;
397 }
398 
399 /**/
400 int
acceptline(UNUSED (char ** args))401 acceptline(UNUSED(char **args))
402 {
403     done = 1;
404     return 0;
405 }
406 
407 /**/
408 int
acceptandhold(UNUSED (char ** args))409 acceptandhold(UNUSED(char **args))
410 {
411     zpushnode(bufstack, zlelineasstring(zleline, zlell, 0, NULL, NULL, 0));
412     stackcs = zlecs;
413     done = 1;
414     return 0;
415 }
416 
417 /**/
418 int
killline(char ** args)419 killline(char **args)
420 {
421     int i = 0, n = zmult;
422 
423     if (n < 0) {
424 	int ret;
425 	zmult = -n;
426 	ret = backwardkillline(args);
427 	zmult = n;
428 	return ret;
429     }
430     while (n--) {
431 	if (zleline[zlecs] == ZWC('\n'))
432 	    zlecs++, i++;
433 	else
434 	    while (zlecs != zlell && zleline[zlecs] != ZWC('\n'))
435 		zlecs++, i++;
436     }
437     backkill(i, CUT_RAW);
438     clearlist = 1;
439     return 0;
440 }
441 
442 /**/
443 void
regionlines(int * start,int * end)444 regionlines(int *start, int *end)
445 {
446     int origcs = zlecs;
447 
448     UNMETACHECK();
449     if (zlecs < mark) {
450 	*start = findbol();
451         zlecs = (mark > zlell) ? zlell : mark;
452 	*end = findeol();
453     } else {
454 	*end = findeol();
455         zlecs = mark;
456 	*start = findbol();
457     }
458     zlecs = origcs;
459 }
460 
461 /**/
462 int
killregion(UNUSED (char ** args))463 killregion(UNUSED(char **args))
464 {
465     if (mark > zlell)
466 	mark = zlell;
467     if (region_active == 2) {
468 	int a, b;
469 	regionlines(&a, &b);
470 	zlecs = a;
471 	region_active = 0;
472 	cut(zlecs, b - zlecs, CUT_RAW);
473 	shiftchars(zlecs, b - zlecs);
474 	if (zlell) {
475 	    if (zlecs == zlell)
476 		DECCS();
477 	    foredel(1, 0);
478 	    vifirstnonblank(zlenoargs);
479 	}
480     } else if (mark > zlecs) {
481 	if (invicmdmode())
482 	    INCPOS(mark);
483 	forekill(mark - zlecs, CUT_RAW);
484     } else {
485 	if (invicmdmode())
486 	    INCCS();
487 	backkill(zlecs - mark, CUT_FRONT|CUT_RAW);
488     }
489     return 0;
490 }
491 
492 /**/
493 int
copyregionaskill(char ** args)494 copyregionaskill(char **args)
495 {
496     int start, end;
497     if (*args) {
498         int len;
499         ZLE_STRING_T line = stringaszleline(*args, 0, &len, NULL, NULL);
500 	cuttext(line, len, CUT_REPLACE);
501 	free(line);
502     } else {
503 	if (mark > zlell)
504 	    mark = zlell;
505 	if (mark > zlecs) {
506 	    start = zlecs;
507 	    end = mark;
508 	} else {
509 	    start = mark;
510 	    end = zlecs;
511 	}
512 	if (invicmdmode())
513 	    INCPOS(end);
514 	cut(start, end - start, mark > zlecs ? 0 : CUT_FRONT);
515     }
516     return 0;
517 }
518 
519 /*
520  * kct: index into kill ring, or -1 for original cutbuffer of yank.
521  * yankcs marks the cursor position preceding the last yank
522  */
523 static int kct, yankcs;
524 
525 /**/
526 int yankb, yanke; /* mark the start and end of last yank in editing buffer. */
527 
528 /* The original cutbuffer, either cutbuf or one of the vi buffers. */
529 static Cutbuffer kctbuf;
530 
531 /**/
532 int
yank(UNUSED (char ** args))533 yank(UNUSED(char **args))
534 {
535     int n = zmult;
536 
537     if (n < 0)
538 	return 1;
539     if (zmod.flags & MOD_VIBUF)
540 	kctbuf = &vibuf[zmod.vibuf];
541     else
542 	kctbuf = &cutbuf;
543     if (!kctbuf->buf)
544 	return 1;
545     yankb = yankcs = mark = zlecs;
546     while (n--) {
547 	kct = -1;
548 	spaceinline(kctbuf->len);
549 	ZS_memcpy(zleline + zlecs, kctbuf->buf, kctbuf->len);
550 	zlecs += kctbuf->len;
551 	yanke = zlecs;
552     }
553     return 0;
554 }
555 
556 /* position: 0 is before, 1 after, 2 split the line */
pastebuf(Cutbuffer buf,int mult,int position)557 static void pastebuf(Cutbuffer buf, int mult, int position)
558 {
559     int cc;
560     if (buf->flags & CUTBUFFER_LINE) {
561 	if (position == 2) {
562 	    if (!zlecs)
563 		position = 0;
564 	    else if (zlecs == zlell)
565 		position = 1;
566 	}
567 	if (position == 2) {
568 	    yankb = zlecs;
569 	    spaceinline(buf->len + 2);
570 	    zleline[zlecs++] = ZWC('\n');
571 	    ZS_memcpy(zleline + zlecs, buf->buf, buf->len);
572 	    zlecs += buf->len;
573 	    zleline[zlecs] = ZWC('\n');
574 	    yanke = zlecs + 1;
575 	} else if (position != 0) {
576 	    yankb = zlecs = findeol();
577 	    spaceinline(buf->len + 1);
578 	    zleline[zlecs++] = ZWC('\n');
579 	    yanke = zlecs + buf->len;
580 	    ZS_memcpy(zleline + zlecs, buf->buf, buf->len);
581 	} else {
582 	    yankb = zlecs = findbol();
583 	    spaceinline(buf->len + 1);
584 	    ZS_memcpy(zleline + zlecs, buf->buf, buf->len);
585 	    yanke = zlecs + buf->len + 1;
586 	    zleline[zlecs + buf->len] = ZWC('\n');
587 	}
588 	vifirstnonblank(zlenoargs);
589     } else {
590 	if (position == 1 && zlecs != findeol())
591 	    INCCS();
592 	yankb = zlecs;
593 	cc = buf->len;
594 	while (mult--) {
595 	    spaceinline(cc);
596 	    ZS_memcpy(zleline + zlecs, buf->buf, cc);
597 	    zlecs += cc;
598 	}
599 	yanke = zlecs;
600 	if (zlecs && invicmdmode())
601 	    DECCS();
602     }
603 }
604 
605 /**/
606 int
viputbefore(UNUSED (char ** args))607 viputbefore(UNUSED(char **args))
608 {
609     int n = zmult;
610 
611     startvichange(-1);
612     if (n < 0)
613 	return 1;
614     if (zmod.flags & MOD_NULL)
615 	return 0;
616     if (zmod.flags & MOD_VIBUF)
617 	kctbuf = &vibuf[zmod.vibuf];
618     else
619 	kctbuf = &cutbuf;
620     if (!kctbuf->buf)
621 	return 1;
622     kct = -1;
623     yankcs = zlecs;
624     pastebuf(kctbuf, n, 0);
625     return 0;
626 }
627 
628 /**/
629 int
viputafter(UNUSED (char ** args))630 viputafter(UNUSED(char **args))
631 {
632     int n = zmult;
633 
634     startvichange(-1);
635     if (n < 0)
636 	return 1;
637     if (zmod.flags & MOD_NULL)
638 	return 0;
639     if (zmod.flags & MOD_VIBUF)
640 	kctbuf = &vibuf[zmod.vibuf];
641     else
642 	kctbuf = &cutbuf;
643     if (!kctbuf->buf)
644 	return 1;
645     kct = -1;
646     yankcs = zlecs;
647     pastebuf(kctbuf, n, 1);
648     return 0;
649 }
650 
651 /**/
652 int
putreplaceselection(UNUSED (char ** args))653 putreplaceselection(UNUSED(char **args))
654 {
655     int n = zmult;
656     struct cutbuffer prevbuf;
657     Cutbuffer putbuf;
658     int clear = 0;
659     int pos = 2;
660 
661     startvichange(-1);
662     if (n < 0 || zmod.flags & MOD_NULL)
663 	return 1;
664     putbuf = (zmod.flags & MOD_VIBUF) ? &vibuf[zmod.vibuf] : &cutbuf;
665     if (!putbuf->buf)
666 	return 1;
667     memcpy(&prevbuf, putbuf, sizeof(prevbuf));
668 
669     /* if "9 was specified, prevent killregion from freeing it */
670     if (zmod.vibuf == 35) {
671 	putbuf->buf = 0;
672 	clear = 1;
673     }
674 
675     zmod.flags = 0; /* flags apply to paste not kill */
676     if (region_active == 2 && prevbuf.flags & CUTBUFFER_LINE) {
677 	int a, b;
678 	regionlines(&a, &b);
679 	pos = (b == zlell);
680     }
681     killregion(zlenoargs);
682 
683     pastebuf(&prevbuf, n, pos);
684     if (clear)
685 	free(prevbuf.buf);
686     return 0;
687 }
688 
689 /**/
690 int
yankpop(UNUSED (char ** args))691 yankpop(UNUSED(char **args))
692 {
693     int kctstart = kct;
694     Cutbuffer buf;
695 
696     if (!(lastcmd & ZLE_YANK) || !kring || !kctbuf) {
697 	kctbuf = NULL;
698 	return 1;
699     }
700     do {
701 	/*
702 	 * This is supposed to make the yankpop loop
703 	 *   original buffer -> kill ring in order -> original buffer -> ...
704 	 * where the original buffer is -1 and the remainder are
705 	 * indices into the kill ring, remember that we need to start
706 	 * that at kringnum rather than zero.
707 	 */
708 	if (kct == -1)
709 	    kct = kringnum;
710 	else {
711 	    int kctnew = (kct + kringsize - 1) % kringsize;
712 	    if (kctnew == kringnum)
713 		kct = -1;
714 	    else
715 		kct = kctnew;
716 	}
717 	if (kct == -1)
718 	    buf = kctbuf;	/* Use original cutbuffer */
719 	else
720 	    buf = kring+kct;	/* Use somewhere in the kill ring */
721 	/* Careful to prevent infinite looping */
722 	if (kct == kctstart)
723 	    return 1;
724 	/*
725 	 * Skip unset buffers instead of stopping as we used to do.
726 	 * Also skip zero-length buffers.
727 	 * There are two reasons for this:
728 	 * 1. We now map the array $killring directly into the
729 	 *    killring, instead of via some extra size-checking logic.
730 	 *    When $killring has been set, a buffer will always have
731 	 *    at least a zero-length string in it.
732 	 * 2. The old logic was inconsistent; when the kill ring
733 	 *    was full, we could loop round and round it, otherwise
734 	 *    we just stopped when we hit the first empty buffer.
735 	 */
736     } while (!buf->buf || *buf->buf == ZWC('\0'));
737 
738     zlecs = yankb;
739     foredel(yanke - yankb, CUT_RAW);
740     zlecs = yankcs;
741     pastebuf(buf, 1, !!(lastcmd & ZLE_YANKAFTER));
742     return 0;
743 }
744 
745 /**/
746 mod_export char *
bracketedstring(void)747 bracketedstring(void)
748 {
749     static const char endesc[] = "\033[201~";
750     int endpos = 0;
751     size_t psize = 64;
752     char *pbuf = zalloc(psize);
753     size_t current = 0;
754     int next, timeout;
755 
756     while (endesc[endpos]) {
757 	if (current + 1 >= psize)
758 	    pbuf = zrealloc(pbuf, psize *= 2);
759 	if ((next = getbyte(1L, &timeout, 1)) == EOF)
760 	    break;
761 	if (!endpos || next != endesc[endpos++])
762 	    endpos = (next == *endesc);
763 	if (imeta(next)) {
764 	    pbuf[current++] = Meta;
765 	    pbuf[current++] = next ^ 32;
766 	} else if (next == '\r')
767 	    pbuf[current++] = '\n';
768 	else
769 	    pbuf[current++] = next;
770     }
771     pbuf[current-endpos] = '\0';
772     return pbuf;
773 }
774 
775 /**/
776 int
bracketedpaste(char ** args)777 bracketedpaste(char **args)
778 {
779     char *pbuf = bracketedstring();
780 
781     if (*args) {
782 	setsparam(*args, pbuf);
783     } else {
784 	int n;
785 	ZLE_STRING_T wpaste;
786 	wpaste = stringaszleline((zmult == 1) ? pbuf :
787 	    quotestring(pbuf, QT_SINGLE_OPTIONAL), 0, &n, NULL, NULL);
788 	cuttext(wpaste, n, CUT_REPLACE);
789 	if (!(zmod.flags & MOD_VIBUF)) {
790 	    kct = -1;
791 	    kctbuf = &cutbuf;
792 	    zmult = 1;
793 	    if (region_active)
794 		killregion(zlenoargs);
795 	    yankcs = yankb = zlecs;
796 	    doinsert(wpaste, n);
797 	    yanke = zlecs;
798 	}
799 	free(pbuf); free(wpaste);
800     }
801     return 0;
802 }
803 
804 /**/
805 int
overwritemode(UNUSED (char ** args))806 overwritemode(UNUSED(char **args))
807 {
808     insmode ^= 1;
809     return 0;
810 }
811 
812 /**/
813 int
whatcursorposition(UNUSED (char ** args))814 whatcursorposition(UNUSED(char **args))
815 {
816     char msg[100];
817     char *s = msg, *mbstr;
818     int bol = findbol(), len;
819     ZLE_CHAR_T c = zleline[zlecs];
820 
821     if (zlecs == zlell)
822 	strucpy(&s, "EOF");
823     else {
824 	strucpy(&s, "Char: ");
825 	switch (c) {
826 	case ZWC(' '):
827 	    strucpy(&s, "SPC");
828 	    break;
829 	case ZWC('\t'):
830 	    strucpy(&s, "TAB");
831 	    break;
832 	case ZWC('\n'):
833 	    strucpy(&s, "LFD");
834 	    break;
835 	default:
836 	    /*
837 	     * convert a single character, remembering it may
838 	     * turn into a multibyte string or be metafied.
839 	     */
840 	    mbstr = zlelineasstring(zleline+zlecs, 1, 0, &len, NULL, 1);
841 	    strcpy(s, mbstr);
842 	    s += len;
843 	}
844 	sprintf(s, " (0%o, %u, 0x%x)", (unsigned int)c,
845 		(unsigned int)c, (unsigned int)c);
846 	s += strlen(s);
847     }
848     sprintf(s, "  point %d of %d(%d%%)  column %d", zlecs+1, zlell+1,
849 	    zlell ? 100 * zlecs / zlell : 0, zlecs - bol);
850     showmsg(msg);
851     return 0;
852 }
853 
854 /**/
855 int
undefinedkey(UNUSED (char ** args))856 undefinedkey(UNUSED(char **args))
857 {
858     return 1;
859 }
860 
861 /**/
862 int
quotedinsert(char ** args)863 quotedinsert(char **args)
864 {
865 #ifndef HAS_TIO
866     struct sgttyb sob;
867 
868     sob = shttyinfo.sgttyb;
869     sob.sg_flags = (sob.sg_flags | RAW) & ~ECHO;
870     ioctl(SHTTY, TIOCSETN, &sob);
871 #endif
872     getfullchar(0);
873 #ifndef HAS_TIO
874     zsetterm();
875 #endif
876     if (LASTFULLCHAR == ZLEEOF)
877 	return 1;
878     else
879 	return selfinsert(args);
880 }
881 
882 static int
parsedigit(int inkey)883 parsedigit(int inkey)
884 {
885 #ifdef MULTIBYTE_SUPPORT
886     /*
887      * It's too dangerous to allow metafied input.  See
888      * universalargument for comments on (possibly suboptimal) handling
889      * of digits.  We are assuming ASCII is a subset of the multibyte
890      * encoding.
891      */
892 #else
893     /* allow metafied as well as ordinary digits */
894     inkey &= 0x7f;
895 #endif
896 
897     /* remember inkey is not a wide character */
898     if (zmod.base > 10) {
899 	if (inkey >= 'a' && inkey < 'a' + zmod.base - 10)
900 	    return inkey - 'a' + 10;
901 	if (inkey >= 'A' && inkey < 'A' + zmod.base - 10)
902 	    return inkey - 'A' + 10;
903 	if (idigit(inkey))
904 	    return inkey - '0';
905 	return -1;
906     }
907     if (inkey >= '0' && inkey < '0' + zmod.base)
908 	return inkey - '0';
909     return -1;
910 }
911 
912 /**/
913 int
digitargument(UNUSED (char ** args))914 digitargument(UNUSED(char **args))
915 {
916     int sign = (zmult < 0) ? -1 : 1;
917     int newdigit = parsedigit(lastchar);
918 
919     if (newdigit < 0)
920 	return 1;
921 
922     if (!(zmod.flags & MOD_TMULT))
923 	zmod.tmult = 0;
924     if (zmod.flags & MOD_NEG) {
925 	/* If we just had a negative argument, this is the digit, *
926 	 * rather than the -1 assumed by negargument()            */
927 	zmod.tmult = sign * newdigit;
928 	zmod.flags &= ~MOD_NEG;
929     } else
930 	zmod.tmult = zmod.tmult * zmod.base + sign * newdigit;
931     zmod.flags |= MOD_TMULT;
932     prefixflag = 1;
933     return 0;
934 }
935 
936 /**/
937 int
negargument(UNUSED (char ** args))938 negargument(UNUSED(char **args))
939 {
940     if (zmod.flags & MOD_TMULT)
941 	return 1;
942     zmod.tmult = -1;
943     zmod.flags |= MOD_TMULT|MOD_NEG;
944     prefixflag = 1;
945     return 0;
946 }
947 
948 /**/
949 int
universalargument(char ** args)950 universalargument(char **args)
951 {
952     int digcnt = 0, pref = 0, minus = 1, gotk;
953     if (*args) {
954 	zmod.mult = atoi(*args);
955 	zmod.flags |= MOD_MULT;
956 	return 0;
957     }
958     /*
959      * TODO: this is quite tricky to do when trying to maintain
960      * compatibility between the old input system and Unicode.
961      * We don't know what follows the digits, so if we try to
962      * read wide characters we may fail (e.g. we may come across an old
963      * \M-style binding).
964      *
965      * If we assume individual bytes are either explicitly ASCII or
966      * not (a la UTF-8), we get away with it; we can back up individual
967      * bytes and everything will work.  We may want to relax this
968      * assumption later.  ("Much later" - (C) Steven Singer,
969      * CSR BlueCore firmware, ca. 2000.)
970      *
971      * Hence for now this remains byte-by-byte.
972      */
973     while ((gotk = getbyte(0L, NULL, 1)) != EOF) {
974 	if (gotk == '-' && !digcnt) {
975 	    minus = -1;
976 	    digcnt++;
977 	} else {
978 	    int newdigit = parsedigit(gotk);
979 
980 	    if (newdigit >= 0) {
981 		pref = pref * zmod.base + newdigit;
982 		digcnt++;
983 	    } else {
984 		ungetbyte(gotk);
985 		break;
986 	    }
987 	}
988     }
989     if (digcnt)
990 	zmod.tmult = minus * (pref ? pref : 1);
991     else
992 	zmod.tmult *= 4;
993     zmod.flags |= MOD_TMULT;
994     prefixflag = 1;
995     return 0;
996 }
997 
998 /* Set the base for a digit argument. */
999 
1000 /**/
1001 int
argumentbase(char ** args)1002 argumentbase(char **args)
1003 {
1004     int multbase;
1005 
1006     if (*args)
1007 	multbase = (int)zstrtol(*args, NULL, 0);
1008     else
1009 	multbase = zmod.mult;
1010 
1011     if (multbase < 2 || multbase > ('9' - '0' + 1) + ('z' - 'a' + 1))
1012 	return 1;
1013 
1014     zmod.base = multbase;
1015 
1016     /* reset modifier, apart from base... */
1017     zmod.flags = 0;
1018     zmod.mult = 1;
1019     zmod.tmult = 1;
1020     zmod.vibuf = 0;
1021 
1022     /* ...but indicate we are still operating on a prefix argument. */
1023     prefixflag = 1;
1024 
1025     return 0;
1026 }
1027 
1028 /**/
1029 int
copyprevword(UNUSED (char ** args))1030 copyprevword(UNUSED(char **args))
1031 {
1032     int len, t0 = zlecs, t1;
1033 
1034     if (zmult > 0) {
1035 	int count = zmult;
1036 
1037 	for (;;) {
1038 	    t1 = t0;
1039 
1040 	    while (t0) {
1041 		int prev = t0;
1042 		DECPOS(prev);
1043 		if (ZC_iword(zleline[prev]))
1044 		    break;
1045 		t0 = prev;
1046 	    }
1047 	    while (t0) {
1048 		int prev = t0;
1049 		DECPOS(prev);
1050 		if (!ZC_iword(zleline[prev]))
1051 		    break;
1052 		t0 = prev;
1053 	    }
1054 
1055 	    if (!--count)
1056 		break;
1057 	    if (t0 == 0)
1058 		return 1;
1059 	}
1060     }
1061     else
1062 	return 1;
1063     len = t1 - t0;
1064     spaceinline(len);
1065     ZS_memcpy(zleline + zlecs, zleline + t0, len);
1066     zlecs += len;
1067     return 0;
1068 }
1069 
1070 /**/
1071 int
copyprevshellword(UNUSED (char ** args))1072 copyprevshellword(UNUSED(char **args))
1073 {
1074     LinkList l;
1075     LinkNode n;
1076     int i;
1077     char *p = NULL;
1078 
1079     if (zmult <= 0)
1080 	return 1;
1081 
1082     if ((l = bufferwords(NULL, NULL, &i, LEXFLAGS_ZLE))) {
1083 	i -= (zmult-1);
1084 	if (i < 0)
1085 	    return 1;
1086         for (n = firstnode(l); n; incnode(n))
1087             if (!i--) {
1088                 p = getdata(n);
1089                 break;
1090             }
1091     }
1092 
1093     if (p) {
1094 	int len;
1095 	ZLE_STRING_T lineadd = stringaszleline(p, 0, &len, NULL, NULL);
1096 
1097 	spaceinline(len);
1098 	ZS_memcpy(zleline + zlecs, lineadd, len);
1099 	zlecs += len;
1100 
1101 	free(lineadd);
1102     }
1103     return 0;
1104 }
1105 
1106 /**/
1107 int
sendbreak(UNUSED (char ** args))1108 sendbreak(UNUSED(char **args))
1109 {
1110     errflag |= ERRFLAG_ERROR|ERRFLAG_INT;
1111     return 1;
1112 }
1113 
1114 /**/
1115 int
quoteregion(UNUSED (char ** args))1116 quoteregion(UNUSED(char **args))
1117 {
1118     ZLE_STRING_T str;
1119     size_t len;
1120     int extra = invicmdmode();
1121 
1122     if (mark > zlell)
1123 	mark = zlell;
1124     if (region_active == 2) {
1125 	int a, b;
1126 	regionlines(&a, &b);
1127 	zlecs = a;
1128 	mark = b;
1129 	extra = 0;
1130     } else if (mark < zlecs) {
1131 	int tmp = mark;
1132 	mark = zlecs;
1133 	zlecs = tmp;
1134     }
1135     if (extra)
1136 	INCPOS(mark);
1137     str = (ZLE_STRING_T)hcalloc((len = mark - zlecs) *
1138 	ZLE_CHAR_SIZE);
1139     ZS_memcpy(str, zleline + zlecs, len);
1140     foredel(len, CUT_RAW);
1141     str = makequote(str, &len);
1142     spaceinline(len);
1143     ZS_memcpy(zleline + zlecs, str, len);
1144     mark = zlecs;
1145     zlecs += len;
1146     return 0;
1147 }
1148 
1149 /**/
1150 int
quoteline(UNUSED (char ** args))1151 quoteline(UNUSED(char **args))
1152 {
1153     ZLE_STRING_T str;
1154     size_t len = zlell;
1155 
1156     str = makequote(zleline, &len);
1157     sizeline(len);
1158     ZS_memcpy(zleline, str, len);
1159     zlecs = zlell = len;
1160     return 0;
1161 }
1162 
1163 /**/
1164 static ZLE_STRING_T
makequote(ZLE_STRING_T str,size_t * len)1165 makequote(ZLE_STRING_T str, size_t *len)
1166 {
1167     int qtct = 0;
1168     ZLE_STRING_T l, ol;
1169     ZLE_STRING_T end = str + *len;
1170 
1171     for (l = str; l < end; l++)
1172 	if (*l == ZWC('\''))
1173 	    qtct++;
1174     *len += 2 + qtct*3;
1175     l = ol = (ZLE_STRING_T)zhalloc(*len * ZLE_CHAR_SIZE);
1176     *l++ = ZWC('\'');
1177     for (; str < end; str++)
1178 	if (*str == ZWC('\'')) {
1179 	    *l++ = ZWC('\'');
1180 	    *l++ = ZWC('\\');
1181 	    *l++ = ZWC('\'');
1182 	    *l++ = ZWC('\'');
1183 	} else
1184 	    *l++ = *str;
1185     *l++ = ZWC('\'');
1186     return ol;
1187 }
1188 
1189 /*
1190  * cmdstr is the buffer used for execute-named-command converted
1191  * to a metafied multibyte string.
1192  */
1193 static char *namedcmdstr;
1194 static LinkList namedcmdll;
1195 static int namedcmdambig;
1196 
1197 /**/
1198 static void
scancompcmd(HashNode hn,UNUSED (int flags))1199 scancompcmd(HashNode hn, UNUSED(int flags))
1200 {
1201     int l;
1202     Thingy t = (Thingy) hn;
1203 
1204     if(strpfx(namedcmdstr, t->nam)) {
1205 	addlinknode(namedcmdll, t->nam);
1206 	l = pfxlen(peekfirst(namedcmdll), t->nam);
1207 	if (l < namedcmdambig)
1208 	    namedcmdambig = l;
1209     }
1210 
1211 }
1212 
1213 #define NAMLEN 60
1214 
1215 /*
1216  * Local keymap used when reading a command name for the
1217  * execute-named-command and where-is widgets.
1218  */
1219 
1220 /**/
1221 Keymap command_keymap;
1222 
1223 /**/
1224 Thingy
executenamedcommand(char * prmt)1225 executenamedcommand(char *prmt)
1226 {
1227     Thingy cmd, retval = NULL;
1228     int l, len, feep = 0, listed = 0, curlist = 0;
1229     int ols = (listshown && validlist), olll = lastlistlen;
1230     char *cmdbuf, *ptr;
1231     char *okeymap = ztrdup(curkeymapname);
1232 
1233     clearlist = 1;
1234     /* prmt may be constant */
1235     prmt = ztrdup(prmt);
1236     l = strlen(prmt);
1237     cmdbuf = (char *)zhalloc(l + NAMLEN + 2
1238 #ifdef MULTIBYTE_SUPPORT
1239 			     + 2 * MB_CUR_MAX
1240 #endif
1241 			     );
1242     strcpy(cmdbuf, prmt);
1243     zsfree(prmt);
1244     statusline = cmdbuf;
1245     selectlocalmap(command_keymap);
1246     selectkeymap("main", 1);
1247     ptr = cmdbuf += l;
1248     len = 0;
1249     for (;;) {
1250 	*ptr = '_';
1251 	ptr[1] = '\0';
1252 	zrefresh();
1253 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
1254 	    statusline = NULL;
1255 	    selectkeymap(okeymap, 1);
1256 	    zsfree(okeymap);
1257 	    if ((listshown = ols)) {
1258 		showinglist = -2;
1259 		lastlistlen = olll;
1260 	    } else if (listed)
1261 		clearlist = listshown = 1;
1262 
1263 	    retval = NULL;
1264 	    goto done;
1265 	}
1266 	if(cmd == Th(z_clearscreen)) {
1267 	    clearscreen(zlenoargs);
1268 	    if (curlist) {
1269 		int zmultsav = zmult;
1270 
1271 		zmult = 1;
1272 		listlist(namedcmdll);
1273 		showinglist = 0;
1274 		zmult = zmultsav;
1275 	    }
1276 	} else if(cmd == Th(z_redisplay)) {
1277 	    redisplay(zlenoargs);
1278 	    if (curlist) {
1279 		int zmultsav = zmult;
1280 
1281 		zmult = 1;
1282 		listlist(namedcmdll);
1283 		showinglist = 0;
1284 		zmult = zmultsav;
1285 	    }
1286 	} else if(cmd == Th(z_viquotedinsert)) {
1287 	    *ptr = '^';
1288 	    zrefresh();
1289 	    getfullchar(0);
1290 	    if(LASTFULLCHAR == ZLEEOF || !LASTFULLCHAR || len >= NAMLEN)
1291 		feep = 1;
1292 	    else {
1293 		int ret = zlecharasstring(LASTFULLCHAR, ptr);
1294 		len += ret;
1295 		ptr += ret;
1296 		curlist = 0;
1297 	    }
1298 	} else if(cmd == Th(z_quotedinsert)) {
1299 	    if(getfullchar(0) == ZLEEOF ||
1300 	       !LASTFULLCHAR || len == NAMLEN)
1301 		feep = 1;
1302 	    else {
1303 		int ret = zlecharasstring(LASTFULLCHAR, ptr);
1304 		len += ret;
1305 		ptr += ret;
1306 		curlist = 0;
1307 	    }
1308 	} else if(cmd == Th(z_backwarddeletechar) ||
1309 		  cmd == Th(z_vibackwarddeletechar)) {
1310 	    if (len) {
1311 		ptr = backwardmetafiedchar(cmdbuf, ptr, NULL);
1312 		len = ptr - cmdbuf;
1313 		curlist = 0;
1314 	    }
1315 	} else if(cmd == Th(z_killregion) || cmd == Th(z_backwardkillword) ||
1316 		  cmd == Th(z_vibackwardkillword)) {
1317 	    if (len)
1318 		curlist = 0;
1319 	    while (len) {
1320 		convchar_t cc;
1321 		ptr = backwardmetafiedchar(cmdbuf, ptr, &cc);
1322 		len = ptr - cmdbuf;
1323 		if (cc == ZWC('-'))
1324 		    break;
1325 	    }
1326 	} else if(cmd == Th(z_killwholeline) || cmd == Th(z_vikillline) ||
1327 	    	cmd == Th(z_backwardkillline)) {
1328 	    len = 0;
1329 	    ptr = cmdbuf;
1330 	    if (listed)
1331 		clearlist = listshown = 1;
1332 	    curlist = 0;
1333 	} else if (cmd == Th(z_bracketedpaste)) {
1334 	    char *insert = bracketedstring();
1335 	    size_t inslen = strlen(insert);
1336 	    if (len + inslen > NAMLEN)
1337 		feep = 1;
1338 	    else {
1339 		strcpy(ptr, insert);
1340 		len += inslen;
1341 		ptr += inslen;
1342 		if (listed) {
1343 		    clearlist = listshown = 1;
1344 		    listed = 0;
1345 		} else
1346 		    curlist = 0;
1347 	    }
1348 	    free(insert);
1349 	} else {
1350 	    if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) {
1351 		Thingy r;
1352 		unambiguous:
1353 		*ptr = 0;
1354 		r = rthingy(cmdbuf);
1355 		if (!(r->flags & DISABLED)) {
1356 		    unrefthingy(r);
1357 		    statusline = NULL;
1358 		    selectkeymap(okeymap, 1);
1359 		    zsfree(okeymap);
1360 		    if ((listshown = ols)) {
1361 			showinglist = -2;
1362 			lastlistlen = olll;
1363 		    } else if (listed)
1364 			clearlist = listshown = 1;
1365 
1366 		    retval = r;
1367 		    goto done;
1368 		}
1369 		unrefthingy(r);
1370 	    }
1371 	    if(cmd == Th(z_selfinsertunmeta)) {
1372 		fixunmeta();
1373 		cmd = Th(z_selfinsert);
1374 	    }
1375 	    if (cmd == Th(z_listchoices) || cmd == Th(z_deletecharorlist) ||
1376 		cmd == Th(z_expandorcomplete) || cmd == Th(z_completeword) ||
1377 		cmd == Th(z_expandorcompleteprefix) || cmd == Th(z_vicmdmode) ||
1378 		cmd == Th(z_acceptline) || lastchar == ' ' || lastchar == '\t') {
1379 		namedcmdambig = 100;
1380 
1381 		namedcmdll = newlinklist();
1382 
1383 		*ptr = '\0';
1384 		namedcmdstr = cmdbuf;
1385 		scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
1386 		namedcmdstr = NULL;
1387 
1388 		if (empty(namedcmdll)) {
1389 		    feep = 1;
1390 		    if (listed)
1391 			clearlist = listshown = 1;
1392 		    curlist = 0;
1393 		} else if (cmd == Th(z_listchoices) ||
1394 		    cmd == Th(z_deletecharorlist)) {
1395 		    int zmultsav = zmult;
1396 		    *ptr = '_';
1397 		    ptr[1] = '\0';
1398 		    zmult = 1;
1399 		    listlist(namedcmdll);
1400 		    listed = curlist = 1;
1401 		    showinglist = 0;
1402 		    zmult = zmultsav;
1403 		} else if (!nextnode(firstnode(namedcmdll))) {
1404 		    strcpy(ptr = cmdbuf, peekfirst(namedcmdll));
1405 		    len = strlen(ptr);
1406 		    ptr += len;
1407 		    if (cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode))
1408 			goto unambiguous;
1409 		} else {
1410 		    strcpy(cmdbuf, peekfirst(namedcmdll));
1411 		    ptr = cmdbuf + namedcmdambig;
1412 		    *ptr = '_';
1413 		    ptr[1] = '\0';
1414 		    if (isset(AUTOLIST) &&
1415 			!(isset(LISTAMBIGUOUS) && namedcmdambig > len)) {
1416 			int zmultsav = zmult;
1417 			if (isset(LISTBEEP))
1418 			    feep = 1;
1419 			zmult = 1;
1420 			listlist(namedcmdll);
1421 			listed = curlist = 1;
1422 			showinglist = 0;
1423 			zmult = zmultsav;
1424 		    }
1425 		    len = namedcmdambig;
1426 		}
1427 	    } else {
1428 		if (len == NAMLEN || cmd != Th(z_selfinsert))
1429 		    feep = 1;
1430 		else {
1431 #ifdef MULTIBYTE_SUPPORT
1432 		    if (!lastchar_wide_valid)
1433 			getrestchar(lastchar, NULL, NULL);
1434 		    if (lastchar_wide == WEOF)
1435 			feep = 1;
1436 		    else
1437 #endif
1438 		    if (ZC_icntrl(LASTFULLCHAR))
1439 			feep = 1;
1440 		    else {
1441 			int ret = zlecharasstring(LASTFULLCHAR, ptr);
1442 			len += ret;
1443 			ptr += ret;
1444 			if (listed) {
1445 			    clearlist = listshown = 1;
1446 			    listed = 0;
1447 			} else
1448 			    curlist = 0;
1449 		    }
1450 		}
1451 	    }
1452 	}
1453 	if (feep)
1454 	    handlefeep(zlenoargs);
1455 	feep = 0;
1456     }
1457 
1458  done:
1459     selectlocalmap(NULL);
1460     return retval;
1461 }
1462 
1463 /*****************/
1464 /* Suffix system */
1465 /*****************/
1466 
1467 /*
1468  * The completion system sometimes tentatively adds a suffix to a word,
1469  * which can be removed depending on what is inserted next.  These
1470  * functions provide the capability to handle a removable suffix.
1471  *
1472  * Any removable suffix consists of characters immediately before the
1473  * cursor.  Whether it is removed depends on the next editing action.
1474  * There can be more than one suffix simultaneously present, with
1475  * different actions deleting different numbers of characters.
1476  *
1477  * If the next editing action changes the buffer other than by inserting
1478  * characters, normally the suffix should be removed so as to leave a
1479  * meaningful complete word.  The behaviour should be the same if the
1480  * next character inserted is a word separator.  If the next character
1481  * reasonably belongs where it is typed, or if the next editing action
1482  * is a deletion, the suffix should not be removed.  Other reasons for
1483  * suffix removal may have other behaviour.
1484  *
1485  * In order to maintain a consistent state, after a suffix has been added
1486  * the table *must* be zeroed, one way or another, before the buffer is
1487  * changed.  If the suffix is not being removed, call fixsuffix() to
1488  * indicate that it is being permanently fixed.
1489  */
1490 
1491 struct suffixset;
1492 
1493 /* An element of a suffix specification */
1494 struct suffixset {
1495     struct suffixset *next;	/* Next in the list */
1496     int tp;			/* The SUFTYP_* from enum suffixtype */
1497     int flags;			/* Some of SUFFLAGS_* */
1498     ZLE_STRING_T chars;		/* Set of characters to match (or not) */
1499     int lenstr;			/* Length of chars */
1500     int lensuf;			/* Length of suffix */
1501 };
1502 
1503 /* The list of suffix structures */
1504 static struct suffixset *suffixlist;
1505 
1506 /* Shell function to call to remove the suffix. */
1507 
1508 /**/
1509 static char *suffixfunc;
1510 
1511 /* Length associated with the suffix function */
1512 static int suffixfunclen;
1513 
1514 /* Whether to remove suffix on uninsertable characters */
1515 /**/
1516 int suffixnoinsrem;
1517 
1518 /* Length of the currently active, auto-removable suffix. */
1519 /**/
1520 mod_export int
1521 suffixlen;
1522 
1523 /**/
1524 mod_export void
addsuffix(int tp,int flags,ZLE_STRING_T chars,int lenstr,int lensuf)1525 addsuffix(int tp, int flags, ZLE_STRING_T chars, int lenstr, int lensuf)
1526 {
1527     struct suffixset *newsuf = zalloc(sizeof(struct suffixset));
1528     newsuf->next = suffixlist;
1529     suffixlist = newsuf;
1530 
1531     newsuf->tp = tp;
1532     newsuf->flags = flags;
1533     if (lenstr) {
1534 	newsuf->chars = zalloc(lenstr*sizeof(ZLE_CHAR_T));
1535 	ZS_memcpy(newsuf->chars, chars, lenstr);
1536     } else
1537 	newsuf->chars = NULL;
1538     newsuf->lenstr = lenstr;
1539     newsuf->lensuf = lensuf;
1540 }
1541 
1542 
1543 /* Same as addsuffix, but from metafied string */
1544 
1545 /**/
1546 mod_export void
addsuffixstring(int tp,int flags,char * chars,int lensuf)1547 addsuffixstring(int tp, int flags, char *chars, int lensuf)
1548 {
1549     int slen, alloclen;
1550     ZLE_STRING_T suffixstr;
1551 
1552     /* string needs to be writable... I've been regretting this for years.. */
1553     chars = ztrdup(chars);
1554     suffixstr = stringaszleline(chars, 0, &slen, &alloclen, NULL);
1555     addsuffix(tp, flags, suffixstr, slen, lensuf);
1556     zfree(suffixstr, alloclen);
1557     zsfree(chars);
1558 }
1559 
1560 /* Set up suffix: the last n characters are a suffix that should be *
1561  * removed in the usual word end conditions.                        */
1562 
1563 /**/
1564 mod_export void
makesuffix(int n)1565 makesuffix(int n)
1566 {
1567     char *suffixchars;
1568 
1569     if (!(suffixchars = getsparam_u("ZLE_REMOVE_SUFFIX_CHARS")))
1570 	suffixchars = " \t\n;&|";
1571 
1572     addsuffixstring(SUFTYP_POSSTR, 0, suffixchars, n);
1573 
1574     /* Do this second so it takes precedence */
1575     if ((suffixchars = getsparam_u("ZLE_SPACE_SUFFIX_CHARS")) && *suffixchars)
1576 	addsuffixstring(SUFTYP_POSSTR, SUFFLAGS_SPACE, suffixchars, n);
1577 
1578     suffixlen = n;
1579     suffixnoinsrem = 1;
1580 }
1581 
1582 /* Set up suffix for parameter names: the last n characters are a suffix *
1583  * that should be removed if the next character is one of the ones that  *
1584  * needs to go immediately after the parameter name.  br indicates that  *
1585  * the name is in braces (${PATH} instead of $PATH), so the extra        *
1586  * characters that can only be used in braces are included.              */
1587 
1588 /**/
1589 mod_export void
makeparamsuffix(int br,int n)1590 makeparamsuffix(int br, int n)
1591 {
1592     ZLE_STRING_T charstr = ZWS(":[#%?-+=");
1593     int lenstr = 0;
1594 
1595     if (br || unset(KSHARRAYS)) {
1596 	lenstr = 2;
1597 	if (br)
1598 	    lenstr += 6;
1599     }
1600     if (lenstr)
1601 	addsuffix(SUFTYP_POSSTR, 0, charstr, lenstr, n);
1602 }
1603 
1604 /* Set up suffix given a string containing the characters on which to   *
1605  * remove the suffix. */
1606 
1607 /**/
1608 mod_export void
makesuffixstr(char * f,char * s,int n)1609 makesuffixstr(char *f, char *s, int n)
1610 {
1611     if (f) {
1612 	zsfree(suffixfunc);
1613 	suffixfunc = ztrdup(f);
1614 	suffixfunclen = n;
1615     } else if (s) {
1616 	int inv, i, z = 0;
1617 	ZLE_STRING_T ws, lasts, wptr;
1618 
1619 	if (*s == '^' || *s == '!') {
1620 	    inv = 1;
1621 	    s++;
1622 	} else
1623 	    inv = 0;
1624 	s = getkeystring(s, &i, GETKEYS_SUFFIX, &z);
1625 	s = metafy(s, i, META_USEHEAP);
1626 	ws = stringaszleline(s, 0, &i, NULL, NULL);
1627 
1628 	/* Remove suffix on uninsertable characters if  \- was given *
1629 	 * and the character class wasn't negated -- or vice versa.  */
1630 	suffixnoinsrem = z ^ inv;
1631 	suffixlen = n;
1632 
1633 	lasts = wptr = ws;
1634 	while (i) {
1635 	    if (i >= 3 && wptr[1] == ZWC('-')) {
1636 		ZLE_CHAR_T str[2];
1637 
1638 		if (wptr > lasts)
1639 		    addsuffix(inv ? SUFTYP_NEGSTR : SUFTYP_POSSTR, 0,
1640 			      lasts, wptr - lasts, n);
1641 		str[0] = *wptr;
1642 		str[1] = wptr[2];
1643 		addsuffix(inv ? SUFTYP_NEGRNG : SUFTYP_POSRNG, 0,
1644 			  str, 2, n);
1645 
1646 		wptr += 3;
1647 		i -= 3;
1648 		lasts = wptr;
1649 	    } else {
1650 		wptr++;
1651 		i--;
1652 	    }
1653 	}
1654 	if (wptr > lasts)
1655 	    addsuffix(inv ? SUFTYP_NEGSTR : SUFTYP_POSSTR, 0,
1656 		      lasts, wptr - lasts, n);
1657 	free(ws);
1658     } else
1659 	makesuffix(n);
1660 }
1661 
1662 /* Remove suffix, if there is one, when inserting character c. */
1663 
1664 /**/
1665 mod_export void
iremovesuffix(ZLE_INT_T c,int keep)1666 iremovesuffix(ZLE_INT_T c, int keep)
1667 {
1668     if (suffixfunc) {
1669 	Shfunc shfunc = getshfunc(suffixfunc);
1670 
1671 	if (shfunc) {
1672 	    LinkList args = newlinklist();
1673 	    char buf[20];
1674 	    int osc = sfcontext;
1675 	    int wasmeta = (zlemetaline != 0);
1676 
1677 	    if (wasmeta) {
1678 		/*
1679 		 * The suffix removal function runs as a normal
1680 		 * ZLE function, not a completion function, so
1681 		 * the line should be unmetafied if this was
1682 		 * called from completion.  (It may not be since
1683 		 * we may decide to remove the suffix later.)
1684 		 */
1685 		unmetafy_line();
1686 	    }
1687 
1688 	    sprintf(buf, "%d", suffixfunclen);
1689 	    addlinknode(args, suffixfunc);
1690 	    addlinknode(args, buf);
1691 
1692 	    startparamscope();
1693 	    makezleparams(0);
1694 	    sfcontext = SFC_COMPLETE;
1695 	    doshfunc(shfunc, args, 1);
1696 	    sfcontext = osc;
1697 	    endparamscope();
1698 
1699 	    if (wasmeta)
1700 		metafy_line();
1701 	}
1702 	zsfree(suffixfunc);
1703 	suffixfunc = NULL;
1704     } else {
1705 	int sl = 0, flags = 0;
1706 	struct suffixset *ss;
1707 
1708 	if (c == NO_INSERT_CHAR) {
1709 	    sl = suffixnoinsrem ? suffixlen : 0;
1710 	} else {
1711 	    ZLE_CHAR_T ch = c;
1712 	    /*
1713 	     * Search for a match for ch in the suffix list.
1714 	     * We stop if we encounter a match in a positive or negative
1715 	     * list, using the suffix length specified or zero respectively.
1716 	     * If we reached the end and passed through a negative
1717 	     * list, we use the suffix length for that, else zero.
1718 	     * This would break if it were possible to have negative
1719 	     * sets with different suffix length:  that's not supposed
1720 	     * to happen.
1721 	     */
1722 	    int negsuflen = 0, found = 0;
1723 
1724 	    for (ss = suffixlist; ss; ss = ss->next) {
1725 		switch (ss->tp) {
1726 		case SUFTYP_POSSTR:
1727 		    if (ZS_memchr(ss->chars, ch, ss->lenstr)) {
1728 			sl = ss->lensuf;
1729 			found = 1;
1730 		    }
1731 		    break;
1732 
1733 		case SUFTYP_NEGSTR:
1734 		    if (ZS_memchr(ss->chars, ch, ss->lenstr)) {
1735 			sl = 0;
1736 			found = 1;
1737 		    } else {
1738 			negsuflen = ss->lensuf;
1739 		    }
1740 		    break;
1741 
1742 		case SUFTYP_POSRNG:
1743 		    if (ss->chars[0] <= ch && ch <= ss->chars[1]) {
1744 			sl = ss->lensuf;
1745 			found = 1;
1746 		    }
1747 		    break;
1748 
1749 		case SUFTYP_NEGRNG:
1750 		    if (ss->chars[0] <= ch && ch <= ss->chars[1]) {
1751 			sl = 0;
1752 			found = 1;
1753 		    } else {
1754 			negsuflen = ss->lensuf;
1755 		    }
1756 		    break;
1757 		}
1758 		if (found) {
1759 		    flags = ss->flags;
1760 		    break;
1761 		}
1762 	    }
1763 
1764 	    if (!found)
1765 		sl = negsuflen;
1766 	}
1767 	if (sl) {
1768 	    /* must be shifting wide character lengths */
1769 	    backdel(sl, CUT_RAW);
1770 	    if (flags & SUFFLAGS_SPACE)
1771 	    {
1772 		/* Add a space and advance over it */
1773 		spaceinline(1);
1774 		if (zlemetaline) {
1775 		    zlemetaline[zlemetacs++] = ' ';
1776 		} else {
1777 		    zleline[zlecs++] = ZWC(' ');
1778 		}
1779 	    }
1780 	    if (!keep)
1781 		invalidatelist();
1782 	}
1783     }
1784     fixsuffix();
1785 }
1786 
1787 /* Fix the suffix in place, if there is one, making it non-removable. */
1788 
1789 /**/
1790 mod_export void
fixsuffix(void)1791 fixsuffix(void)
1792 {
1793     while (suffixlist) {
1794 	struct suffixset *next = suffixlist->next;
1795 
1796 	if (suffixlist->lenstr)
1797 	    zfree(suffixlist->chars, suffixlist->lenstr * sizeof(ZLE_CHAR_T));
1798 	zfree(suffixlist, sizeof(struct suffixset));
1799 
1800 	suffixlist = next;
1801     }
1802 
1803     suffixfunclen = suffixnoinsrem = suffixlen = 0;
1804 }
1805