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/**/
36void
37doinsert(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    if (insmode)
51	spaceinline(m * len);
52    else
53#ifdef MULTIBYTE_SUPPORT
54    {
55	int pos = zlecs, diff, i;
56
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; i++) {
72	    if (!IS_COMBINING(zstr[i]))
73		count++;
74	}
75	/*
76	 * Ensure we replace a complete combining character
77	 * for each character we overwrite.
78	 */
79	for (i = count; pos < zlell && i--; ) {
80	    INCPOS(pos);
81	}
82	/*
83	 * Calculate how many raw line places we need.
84	 * pos - zlecs is the raw line distance we're replacing,
85	 * m * len the number we're inserting.
86	 */
87	diff = pos - zlecs - m * len;
88	if (diff < 0) {
89	    spaceinline(-diff);
90	} else if (diff > 0) {
91	    /*
92	     * We use shiftchars() here because we don't
93	     * want combining char alignment fixed up: we
94	     * are going to write over any that remain.
95	     */
96	    shiftchars(zlecs, diff);
97	}
98    }
99#else
100    if (zlecs + m * len > zlell)
101	spaceinline(zlecs + m * len - zlell);
102#endif
103    while (m--)
104	for (s = zstr, count = len; count; s++, count--)
105	    zleline[zlecs++] = *s;
106    if (neg)
107	zlecs += zmult * len;
108    /* if we ended up on a combining character, skip over it */
109    CCRIGHT();
110}
111
112/**/
113mod_export int
114selfinsert(UNUSED(char **args))
115{
116    ZLE_CHAR_T tmp;
117
118#ifdef MULTIBYTE_SUPPORT
119    if (!lastchar_wide_valid)
120	if (getrestchar(lastchar) == WEOF)
121	    return 1;
122#endif
123    tmp = LASTFULLCHAR;
124    doinsert(&tmp, 1);
125    return 0;
126}
127
128/**/
129mod_export void
130fixunmeta(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/**/
148mod_export int
149selfinsertunmeta(char **args)
150{
151    fixunmeta();
152    return selfinsert(args);
153}
154
155/**/
156int
157deletechar(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/**/
179int
180backwarddeletechar(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/**/
194int
195killwholeline(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/**/
214int
215killbuffer(UNUSED(char **args))
216{
217    zlecs = 0;
218    forekill(zlell, CUT_RAW);
219    clearlist = 1;
220    return 0;
221}
222
223/**/
224int
225backwardkillline(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
254static void
255transpose_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/**/
273int
274gosmacstransposechars(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/**/
312int
313transposechars(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/**/
368int
369poundinsert(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/**/
400int
401acceptline(UNUSED(char **args))
402{
403    done = 1;
404    return 0;
405}
406
407/**/
408int
409acceptandhold(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/**/
418int
419killline(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/**/
443int
444killregion(UNUSED(char **args))
445{
446    if (mark > zlell)
447	mark = zlell;
448    if (mark > zlecs)
449	forekill(mark - zlecs, CUT_RAW);
450    else
451	backkill(zlecs - mark, CUT_FRONT|CUT_RAW);
452    return 0;
453}
454
455/**/
456int
457copyregionaskill(char **args)
458{
459    if (*args) {
460        int len;
461        ZLE_STRING_T line = stringaszleline(*args, 0, &len, NULL, NULL);
462	cuttext(line, len, CUT_REPLACE);
463	free(line);
464    } else {
465	if (mark > zlell)
466	    mark = zlell;
467	if (mark > zlecs)
468	    cut(zlecs, mark - zlecs, 0);
469	else
470	    cut(mark, zlecs - mark, CUT_FRONT);
471    }
472    return 0;
473}
474
475/*
476 * kct: index into kill ring, or -1 for original cutbuffer of yank.
477 * yankb, yanke: mark the start and end of last yank in editing buffer.
478 */
479static int kct, yankb, yanke;
480/* The original cutbuffer, either cutbuf or one of the vi buffers. */
481static Cutbuffer kctbuf;
482
483/**/
484int
485yank(UNUSED(char **args))
486{
487    int n = zmult;
488
489    if (n < 0)
490	return 1;
491    if (zmod.flags & MOD_VIBUF)
492	kctbuf = &vibuf[zmod.vibuf];
493    else
494	kctbuf = &cutbuf;
495    if (!kctbuf->buf)
496	return 1;
497    mark = zlecs;
498    yankb = zlecs;
499    while (n--) {
500	kct = -1;
501	spaceinline(kctbuf->len);
502	ZS_memcpy(zleline + zlecs, kctbuf->buf, kctbuf->len);
503	zlecs += kctbuf->len;
504	yanke = zlecs;
505    }
506    return 0;
507}
508
509/**/
510int
511yankpop(UNUSED(char **args))
512{
513    int cc, kctstart = kct;
514    Cutbuffer buf;
515
516    if (!(lastcmd & ZLE_YANK) || !kring || !kctbuf) {
517	kctbuf = NULL;
518	return 1;
519    }
520    do {
521	/*
522	 * This is supposed to make the yankpop loop
523	 *   original buffer -> kill ring in order -> original buffer -> ...
524	 * where the original buffer is -1 and the remainder are
525	 * indices into the kill ring, remember that we need to start
526	 * that at kringnum rather than zero.
527	 */
528	if (kct == -1)
529	    kct = kringnum;
530	else {
531	    int kctnew = (kct + kringsize - 1) % kringsize;
532	    if (kctnew == kringnum)
533		kct = -1;
534	    else
535		kct = kctnew;
536	}
537	if (kct == -1)
538	    buf = kctbuf;	/* Use original cutbuffer */
539	else
540	    buf = kring+kct;	/* Use somewhere in the kill ring */
541	/* Careful to prevent infinite looping */
542	if (kct == kctstart)
543	    return 1;
544	/*
545	 * Skip unset buffers instead of stopping as we used to do.
546	 * Also skip zero-length buffers.
547	 * There are two reasons for this:
548	 * 1. We now map the array $killring directly into the
549	 *    killring, instead of via some extra size-checking logic.
550	 *    When $killring has been set, a buffer will always have
551	 *    at least a zero-length string in it.
552	 * 2. The old logic was inconsistent; when the kill ring
553	 *    was full, we could loop round and round it, otherwise
554	 *    we just stopped when we hit the first empty buffer.
555	 */
556    } while (!buf->buf || *buf->buf == ZWC('\0'));
557
558    zlecs = yankb;
559    foredel(yanke - yankb, CUT_RAW);
560    cc = buf->len;
561    spaceinline(cc);
562    ZS_memcpy(zleline + zlecs, buf->buf, cc);
563    zlecs += cc;
564    yanke = zlecs;
565    return 0;
566}
567
568/**/
569int
570overwritemode(UNUSED(char **args))
571{
572    insmode ^= 1;
573    return 0;
574}
575
576/**/
577int
578whatcursorposition(UNUSED(char **args))
579{
580    char msg[100];
581    char *s = msg, *mbstr;
582    int bol = findbol(), len;
583    ZLE_CHAR_T c = zleline[zlecs];
584
585    if (zlecs == zlell)
586	strucpy(&s, "EOF");
587    else {
588	strucpy(&s, "Char: ");
589	switch (c) {
590	case ZWC(' '):
591	    strucpy(&s, "SPC");
592	    break;
593	case ZWC('\t'):
594	    strucpy(&s, "TAB");
595	    break;
596	case ZWC('\n'):
597	    strucpy(&s, "LFD");
598	    break;
599	default:
600	    /*
601	     * convert a single character, remembering it may
602	     * turn into a multibyte string or be metafied.
603	     */
604	    mbstr = zlelineasstring(zleline+zlecs, 1, 0, &len, NULL, 1);
605	    strcpy(s, mbstr);
606	    s += len;
607	}
608	sprintf(s, " (0%o, %u, 0x%x)", (unsigned int)c,
609		(unsigned int)c, (unsigned int)c);
610	s += strlen(s);
611    }
612    sprintf(s, "  point %d of %d(%d%%)  column %d", zlecs+1, zlell+1,
613	    zlell ? 100 * zlecs / zlell : 0, zlecs - bol);
614    showmsg(msg);
615    return 0;
616}
617
618/**/
619int
620undefinedkey(UNUSED(char **args))
621{
622    return 1;
623}
624
625/**/
626int
627quotedinsert(char **args)
628{
629#ifndef HAS_TIO
630    struct sgttyb sob;
631
632    sob = shttyinfo.sgttyb;
633    sob.sg_flags = (sob.sg_flags | RAW) & ~ECHO;
634    ioctl(SHTTY, TIOCSETN, &sob);
635#endif
636    getfullchar(0);
637#ifndef HAS_TIO
638    zsetterm();
639#endif
640    if (LASTFULLCHAR == ZLEEOF)
641	return 1;
642    else
643	return selfinsert(args);
644}
645
646static int
647parsedigit(int inkey)
648{
649#ifdef MULTIBYTE_SUPPORT
650    /*
651     * It's too dangerous to allow metafied input.  See
652     * universalargument for comments on (possibly suboptimal) handling
653     * of digits.  We are assuming ASCII is a subset of the multibyte
654     * encoding.
655     */
656#else
657    /* allow metafied as well as ordinary digits */
658    inkey &= 0x7f;
659#endif
660
661    /* remember inkey is not a wide character */
662    if (zmod.base > 10) {
663	if (inkey >= 'a' && inkey < 'a' + zmod.base - 10)
664	    return inkey - 'a' + 10;
665	if (inkey >= 'A' && inkey < 'A' + zmod.base - 10)
666	    return inkey - 'A' + 10;
667	if (idigit(inkey))
668	    return inkey - '0';
669	return -1;
670    }
671    if (inkey >= '0' && inkey < '0' + zmod.base)
672	return inkey - '0';
673    return -1;
674}
675
676/**/
677int
678digitargument(UNUSED(char **args))
679{
680    int sign = (zmult < 0) ? -1 : 1;
681    int newdigit = parsedigit(lastchar);
682
683    if (newdigit < 0)
684	return 1;
685
686    if (!(zmod.flags & MOD_TMULT))
687	zmod.tmult = 0;
688    if (zmod.flags & MOD_NEG) {
689	/* If we just had a negative argument, this is the digit, *
690	 * rather than the -1 assumed by negargument()            */
691	zmod.tmult = sign * newdigit;
692	zmod.flags &= ~MOD_NEG;
693    } else
694	zmod.tmult = zmod.tmult * zmod.base + sign * newdigit;
695    zmod.flags |= MOD_TMULT;
696    prefixflag = 1;
697    return 0;
698}
699
700/**/
701int
702negargument(UNUSED(char **args))
703{
704    if (zmod.flags & MOD_TMULT)
705	return 1;
706    zmod.tmult = -1;
707    zmod.flags |= MOD_TMULT|MOD_NEG;
708    prefixflag = 1;
709    return 0;
710}
711
712/**/
713int
714universalargument(char **args)
715{
716    int digcnt = 0, pref = 0, minus = 1, gotk;
717    if (*args) {
718	zmod.mult = atoi(*args);
719	zmod.flags |= MOD_MULT;
720	return 0;
721    }
722    /*
723     * TODO: this is quite tricky to do when trying to maintain
724     * compatibility between the old input system and Unicode.
725     * We don't know what follows the digits, so if we try to
726     * read wide characters we may fail (e.g. we may come across an old
727     * \M-style binding).
728     *
729     * If we assume individual bytes are either explicitly ASCII or
730     * not (a la UTF-8), we get away with it; we can back up individual
731     * bytes and everything will work.  We may want to relax this
732     * assumption later.  ("Much later" - (C) Steven Singer,
733     * CSR BlueCore firmware, ca. 2000.)
734     *
735     * Hence for now this remains byte-by-byte.
736     */
737    while ((gotk = getbyte(0L, NULL)) != EOF) {
738	if (gotk == '-' && !digcnt) {
739	    minus = -1;
740	    digcnt++;
741	} else {
742	    int newdigit = parsedigit(gotk);
743
744	    if (newdigit >= 0) {
745		pref = pref * zmod.base + newdigit;
746		digcnt++;
747	    } else {
748		ungetbyte(gotk);
749		break;
750	    }
751	}
752    }
753    if (digcnt)
754	zmod.tmult = minus * (pref ? pref : 1);
755    else
756	zmod.tmult *= 4;
757    zmod.flags |= MOD_TMULT;
758    prefixflag = 1;
759    return 0;
760}
761
762/* Set the base for a digit argument. */
763
764/**/
765int
766argumentbase(char **args)
767{
768    int multbase;
769
770    if (*args)
771	multbase = (int)zstrtol(*args, NULL, 0);
772    else
773	multbase = zmod.mult;
774
775    if (multbase < 2 || multbase > ('9' - '0' + 1) + ('z' - 'a' + 1))
776	return 1;
777
778    zmod.base = multbase;
779
780    /* reset modifier, apart from base... */
781    zmod.flags = 0;
782    zmod.mult = 1;
783    zmod.tmult = 1;
784    zmod.vibuf = 0;
785
786    /* ...but indicate we are still operating on a prefix argument. */
787    prefixflag = 1;
788
789    return 0;
790}
791
792/**/
793int
794copyprevword(UNUSED(char **args))
795{
796    int len, t0 = zlecs, t1;
797
798    if (zmult > 0) {
799	int count = zmult;
800
801	for (;;) {
802	    t1 = t0;
803
804	    while (t0) {
805		int prev = t0;
806		DECPOS(prev);
807		if (ZC_iword(zleline[prev]))
808		    break;
809		t0 = prev;
810	    }
811	    while (t0) {
812		int prev = t0;
813		DECPOS(prev);
814		if (!ZC_iword(zleline[prev]))
815		    break;
816		t0 = prev;
817	    }
818
819	    if (!--count)
820		break;
821	    if (t0 == 0)
822		return 1;
823	}
824    }
825    else
826	return 1;
827    len = t1 - t0;
828    spaceinline(len);
829    ZS_memcpy(zleline + zlecs, zleline + t0, len);
830    zlecs += len;
831    return 0;
832}
833
834/**/
835int
836copyprevshellword(UNUSED(char **args))
837{
838    LinkList l;
839    LinkNode n;
840    int i;
841    char *p = NULL;
842
843    if (zmult <= 0)
844	return 1;
845
846    if ((l = bufferwords(NULL, NULL, &i, 0))) {
847	i -= (zmult-1);
848	if (i < 0)
849	    return 1;
850        for (n = firstnode(l); n; incnode(n))
851            if (!i--) {
852                p = getdata(n);
853                break;
854            }
855    }
856
857    if (p) {
858	int len;
859	ZLE_STRING_T lineadd = stringaszleline(p, 0, &len, NULL, NULL);
860
861	spaceinline(len);
862	ZS_memcpy(zleline + zlecs, lineadd, len);
863	zlecs += len;
864
865	free(lineadd);
866    }
867    return 0;
868}
869
870/**/
871int
872sendbreak(UNUSED(char **args))
873{
874    errflag = 1;
875    return 1;
876}
877
878/**/
879int
880quoteregion(UNUSED(char **args))
881{
882    ZLE_STRING_T str;
883    size_t len;
884
885    if (mark > zlell)
886	mark = zlell;
887    if (mark < zlecs) {
888	int tmp = mark;
889	mark = zlecs;
890	zlecs = tmp;
891    }
892    str = (ZLE_STRING_T)hcalloc((len = mark - zlecs) * ZLE_CHAR_SIZE);
893    ZS_memcpy(str, zleline + zlecs, len);
894    foredel(len, CUT_RAW);
895    str = makequote(str, &len);
896    spaceinline(len);
897    ZS_memcpy(zleline + zlecs, str, len);
898    mark = zlecs;
899    zlecs += len;
900    return 0;
901}
902
903/**/
904int
905quoteline(UNUSED(char **args))
906{
907    ZLE_STRING_T str;
908    size_t len = zlell;
909
910    str = makequote(zleline, &len);
911    sizeline(len);
912    ZS_memcpy(zleline, str, len);
913    zlecs = zlell = len;
914    return 0;
915}
916
917/**/
918static ZLE_STRING_T
919makequote(ZLE_STRING_T str, size_t *len)
920{
921    int qtct = 0;
922    ZLE_STRING_T l, ol;
923    ZLE_STRING_T end = str + *len;
924
925    for (l = str; l < end; l++)
926	if (*l == ZWC('\''))
927	    qtct++;
928    *len += 2 + qtct*3;
929    l = ol = (ZLE_STRING_T)zhalloc(*len * ZLE_CHAR_SIZE);
930    *l++ = ZWC('\'');
931    for (; str < end; str++)
932	if (*str == ZWC('\'')) {
933	    *l++ = ZWC('\'');
934	    *l++ = ZWC('\\');
935	    *l++ = ZWC('\'');
936	    *l++ = ZWC('\'');
937	} else
938	    *l++ = *str;
939    *l++ = ZWC('\'');
940    return ol;
941}
942
943/*
944 * cmdstr is the buffer used for execute-named-command converted
945 * to a metafied multibyte string.
946 */
947static char *namedcmdstr;
948static LinkList namedcmdll;
949static int namedcmdambig;
950
951/**/
952static void
953scancompcmd(HashNode hn, UNUSED(int flags))
954{
955    int l;
956    Thingy t = (Thingy) hn;
957
958    if(strpfx(namedcmdstr, t->nam)) {
959	addlinknode(namedcmdll, t->nam);
960	l = pfxlen(peekfirst(namedcmdll), t->nam);
961	if (l < namedcmdambig)
962	    namedcmdambig = l;
963    }
964
965}
966
967#define NAMLEN 60
968
969/*
970 * Local keymap used when reading a command name for the
971 * execute-named-command and where-is widgets.
972 */
973
974/**/
975Keymap command_keymap;
976
977/**/
978Thingy
979executenamedcommand(char *prmt)
980{
981    Thingy cmd, retval = NULL;
982    int l, len, feep = 0, listed = 0, curlist = 0;
983    int ols = (listshown && validlist), olll = lastlistlen;
984    char *cmdbuf, *ptr;
985    char *okeymap = ztrdup(curkeymapname);
986
987    clearlist = 1;
988    /* prmt may be constant */
989    prmt = ztrdup(prmt);
990    l = strlen(prmt);
991    cmdbuf = (char *)zhalloc(l + NAMLEN + 2
992#ifdef MULTIBYTE_SUPPORT
993			     + 2 * MB_CUR_MAX
994#endif
995			     );
996    strcpy(cmdbuf, prmt);
997    zsfree(prmt);
998    statusline = cmdbuf;
999    selectlocalmap(command_keymap);
1000    selectkeymap("main", 1);
1001    ptr = cmdbuf += l;
1002    len = 0;
1003    for (;;) {
1004	*ptr = '_';
1005	ptr[1] = '\0';
1006	zrefresh();
1007	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
1008	    statusline = NULL;
1009	    selectkeymap(okeymap, 1);
1010	    zsfree(okeymap);
1011	    if ((listshown = ols)) {
1012		showinglist = -2;
1013		lastlistlen = olll;
1014	    } else if (listed)
1015		clearlist = listshown = 1;
1016
1017	    retval = NULL;
1018	    goto done;
1019	}
1020	if(cmd == Th(z_clearscreen)) {
1021	    clearscreen(zlenoargs);
1022	    if (curlist) {
1023		int zmultsav = zmult;
1024
1025		zmult = 1;
1026		listlist(namedcmdll);
1027		showinglist = 0;
1028		zmult = zmultsav;
1029	    }
1030	} else if(cmd == Th(z_redisplay)) {
1031	    redisplay(zlenoargs);
1032	    if (curlist) {
1033		int zmultsav = zmult;
1034
1035		zmult = 1;
1036		listlist(namedcmdll);
1037		showinglist = 0;
1038		zmult = zmultsav;
1039	    }
1040	} else if(cmd == Th(z_viquotedinsert)) {
1041	    *ptr = '^';
1042	    zrefresh();
1043	    getfullchar(0);
1044	    if(LASTFULLCHAR == ZLEEOF || !LASTFULLCHAR || len >= NAMLEN)
1045		feep = 1;
1046	    else {
1047		int ret = zlecharasstring(LASTFULLCHAR, ptr);
1048		len += ret;
1049		ptr += ret;
1050		curlist = 0;
1051	    }
1052	} else if(cmd == Th(z_quotedinsert)) {
1053	    if(getfullchar(0) == ZLEEOF ||
1054	       !LASTFULLCHAR || len == NAMLEN)
1055		feep = 1;
1056	    else {
1057		int ret = zlecharasstring(LASTFULLCHAR, ptr);
1058		len += ret;
1059		ptr += ret;
1060		curlist = 0;
1061	    }
1062	} else if(cmd == Th(z_backwarddeletechar) ||
1063		  cmd == Th(z_vibackwarddeletechar)) {
1064	    if (len) {
1065		ptr = backwardmetafiedchar(cmdbuf, ptr, NULL);
1066		len = ptr - cmdbuf;
1067		curlist = 0;
1068	    }
1069	} else if(cmd == Th(z_killregion) || cmd == Th(z_backwardkillword) ||
1070		  cmd == Th(z_vibackwardkillword)) {
1071	    if (len)
1072		curlist = 0;
1073	    while (len) {
1074		convchar_t cc;
1075		ptr = backwardmetafiedchar(cmdbuf, ptr, &cc);
1076		len = ptr - cmdbuf;
1077		if (cc == ZWC('-'))
1078		    break;
1079	    }
1080	} else if(cmd == Th(z_killwholeline) || cmd == Th(z_vikillline) ||
1081	    	cmd == Th(z_backwardkillline)) {
1082	    len = 0;
1083	    ptr = cmdbuf;
1084	    if (listed)
1085		clearlist = listshown = 1;
1086	    curlist = 0;
1087	} else {
1088	    if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) {
1089		Thingy r;
1090		unambiguous:
1091		*ptr = 0;
1092		r = rthingy(cmdbuf);
1093		if (!(r->flags & DISABLED)) {
1094		    unrefthingy(r);
1095		    statusline = NULL;
1096		    selectkeymap(okeymap, 1);
1097		    zsfree(okeymap);
1098		    if ((listshown = ols)) {
1099			showinglist = -2;
1100			lastlistlen = olll;
1101		    } else if (listed)
1102			clearlist = listshown = 1;
1103
1104		    retval = r;
1105		    goto done;
1106		}
1107		unrefthingy(r);
1108	    }
1109	    if(cmd == Th(z_selfinsertunmeta)) {
1110		fixunmeta();
1111		cmd = Th(z_selfinsert);
1112	    }
1113	    if (cmd == Th(z_listchoices) || cmd == Th(z_deletecharorlist) ||
1114		cmd == Th(z_expandorcomplete) || cmd == Th(z_completeword) ||
1115		cmd == Th(z_expandorcompleteprefix) || cmd == Th(z_vicmdmode) ||
1116		cmd == Th(z_acceptline) || lastchar == ' ' || lastchar == '\t') {
1117		namedcmdambig = 100;
1118
1119		namedcmdll = newlinklist();
1120
1121		*ptr = '\0';
1122		namedcmdstr = cmdbuf;
1123		scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
1124		namedcmdstr = NULL;
1125
1126		if (empty(namedcmdll)) {
1127		    feep = 1;
1128		    if (listed)
1129			clearlist = listshown = 1;
1130		    curlist = 0;
1131		} else if (cmd == Th(z_listchoices) ||
1132		    cmd == Th(z_deletecharorlist)) {
1133		    int zmultsav = zmult;
1134		    *ptr = '_';
1135		    ptr[1] = '\0';
1136		    zmult = 1;
1137		    listlist(namedcmdll);
1138		    listed = curlist = 1;
1139		    showinglist = 0;
1140		    zmult = zmultsav;
1141		} else if (!nextnode(firstnode(namedcmdll))) {
1142		    strcpy(ptr = cmdbuf, peekfirst(namedcmdll));
1143		    len = strlen(ptr);
1144		    ptr += len;
1145		    if (cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode))
1146			goto unambiguous;
1147		} else {
1148		    strcpy(cmdbuf, peekfirst(namedcmdll));
1149		    ptr = cmdbuf + namedcmdambig;
1150		    *ptr = '_';
1151		    ptr[1] = '\0';
1152		    if (isset(AUTOLIST) &&
1153			!(isset(LISTAMBIGUOUS) && namedcmdambig > len)) {
1154			int zmultsav = zmult;
1155			if (isset(LISTBEEP))
1156			    feep = 1;
1157			zmult = 1;
1158			listlist(namedcmdll);
1159			listed = curlist = 1;
1160			showinglist = 0;
1161			zmult = zmultsav;
1162		    }
1163		    len = namedcmdambig;
1164		}
1165	    } else {
1166		if (len == NAMLEN || cmd != Th(z_selfinsert))
1167		    feep = 1;
1168		else {
1169#ifdef MULTIBYTE_SUPPORT
1170		    if (!lastchar_wide_valid)
1171			getrestchar(lastchar);
1172		    if (lastchar_wide == WEOF)
1173			feep = 1;
1174		    else
1175#endif
1176		    if (ZC_icntrl(LASTFULLCHAR))
1177			feep = 1;
1178		    else {
1179			int ret = zlecharasstring(LASTFULLCHAR, ptr);
1180			len += ret;
1181			ptr += ret;
1182			if (listed) {
1183			    clearlist = listshown = 1;
1184			    listed = 0;
1185			} else
1186			    curlist = 0;
1187		    }
1188		}
1189	    }
1190	}
1191	if (feep)
1192	    handlefeep(zlenoargs);
1193	feep = 0;
1194    }
1195
1196 done:
1197    selectlocalmap(NULL);
1198    return retval;
1199}
1200
1201/*****************/
1202/* Suffix system */
1203/*****************/
1204
1205/*
1206 * The completion system sometimes tentatively adds a suffix to a word,
1207 * which can be removed depending on what is inserted next.  These
1208 * functions provide the capability to handle a removable suffix.
1209 *
1210 * Any removable suffix consists of characters immediately before the
1211 * cursor.  Whether it is removed depends on the next editing action.
1212 * There can be more than one suffix simultaneously present, with
1213 * different actions deleting different numbers of characters.
1214 *
1215 * If the next editing action changes the buffer other than by inserting
1216 * characters, normally the suffix should be removed so as to leave a
1217 * meaningful complete word.  The behaviour should be the same if the
1218 * next character inserted is a word separator.  If the next character
1219 * reasonably belongs where it is typed, or if the next editing action
1220 * is a deletion, the suffix should not be removed.  Other reasons for
1221 * suffix removal may have other behaviour.
1222 *
1223 * In order to maintain a consistent state, after a suffix has been added
1224 * the table *must* be zeroed, one way or another, before the buffer is
1225 * changed.  If the suffix is not being removed, call fixsuffix() to
1226 * indicate that it is being permanently fixed.
1227 */
1228
1229struct suffixset;
1230
1231/* An element of a suffix specification */
1232struct suffixset {
1233    struct suffixset *next;	/* Next in the list */
1234    int tp;			/* The SUFTYP_* from enum suffixtype */
1235    int flags;			/* Some of SUFFLAGS_* */
1236    ZLE_STRING_T chars;		/* Set of characters to match (or not) */
1237    int lenstr;			/* Length of chars */
1238    int lensuf;			/* Length of suffix */
1239};
1240
1241/* The list of suffix structures */
1242struct suffixset *suffixlist;
1243
1244/* Shell function to call to remove the suffix. */
1245
1246/**/
1247static char *suffixfunc;
1248
1249/* Length associated with the suffix function */
1250static int suffixfunclen;
1251
1252/* Length associated with uninsertable characters */
1253/**/
1254mod_export int
1255suffixnoinslen;
1256
1257/**/
1258mod_export void
1259addsuffix(int tp, int flags, ZLE_STRING_T chars, int lenstr, int lensuf)
1260{
1261    struct suffixset *newsuf = zalloc(sizeof(struct suffixset));
1262    newsuf->next = suffixlist;
1263    suffixlist = newsuf;
1264
1265    newsuf->tp = tp;
1266    newsuf->flags = flags;
1267    if (lenstr) {
1268	newsuf->chars = zalloc(lenstr*sizeof(ZLE_CHAR_T));
1269	ZS_memcpy(newsuf->chars, chars, lenstr);
1270    } else
1271	newsuf->chars = NULL;
1272    newsuf->lenstr = lenstr;
1273    newsuf->lensuf = lensuf;
1274}
1275
1276
1277/* Same as addsuffix, but from metafied string */
1278
1279/**/
1280mod_export void
1281addsuffixstring(int tp, int flags, char *chars, int lensuf)
1282{
1283    int slen, alloclen;
1284    ZLE_STRING_T suffixstr;
1285
1286    /* string needs to be writable... I've been regretting this for years.. */
1287    chars = ztrdup(chars);
1288    suffixstr = stringaszleline(chars, 0, &slen, &alloclen, NULL);
1289    addsuffix(tp, flags, suffixstr, slen, lensuf);
1290    zfree(suffixstr, alloclen);
1291    zsfree(chars);
1292}
1293
1294/* Set up suffix: the last n characters are a suffix that should be *
1295 * removed in the usual word end conditions.                        */
1296
1297/**/
1298mod_export void
1299makesuffix(int n)
1300{
1301    char *suffixchars;
1302
1303    if (!(suffixchars = getsparam("ZLE_REMOVE_SUFFIX_CHARS")))
1304	suffixchars = " \t\n;&|";
1305
1306    addsuffixstring(SUFTYP_POSSTR, 0, suffixchars, n);
1307
1308    /* Do this second so it takes precedence */
1309    if ((suffixchars = getsparam("ZLE_SPACE_SUFFIX_CHARS")) && *suffixchars)
1310	addsuffixstring(SUFTYP_POSSTR, SUFFLAGS_SPACE, suffixchars, n);
1311
1312    suffixnoinslen = n;
1313}
1314
1315/* Set up suffix for parameter names: the last n characters are a suffix *
1316 * that should be removed if the next character is one of the ones that  *
1317 * needs to go immediately after the parameter name.  br indicates that  *
1318 * the name is in braces (${PATH} instead of $PATH), so the extra        *
1319 * characters that can only be used in braces are included.              */
1320
1321/**/
1322mod_export void
1323makeparamsuffix(int br, int n)
1324{
1325    ZLE_STRING_T charstr = ZWS(":[#%?-+=");
1326    int lenstr = 0;
1327
1328    if (br || unset(KSHARRAYS)) {
1329	lenstr = 2;
1330	if (br)
1331	    lenstr += 6;
1332    }
1333    if (lenstr)
1334	addsuffix(SUFTYP_POSSTR, 0, charstr, lenstr, n);
1335}
1336
1337/* Set up suffix given a string containing the characters on which to   *
1338 * remove the suffix. */
1339
1340/**/
1341mod_export void
1342makesuffixstr(char *f, char *s, int n)
1343{
1344    if (f) {
1345	zsfree(suffixfunc);
1346	suffixfunc = ztrdup(f);
1347	suffixfunclen = n;
1348    } else if (s) {
1349	int inv, i, z = 0;
1350	ZLE_STRING_T ws, lasts, wptr;
1351
1352	if (*s == '^' || *s == '!') {
1353	    inv = 1;
1354	    s++;
1355	} else
1356	    inv = 0;
1357	s = getkeystring(s, &i, GETKEYS_SUFFIX, &z);
1358	s = metafy(s, i, META_USEHEAP);
1359	ws = stringaszleline(s, 0, &i, NULL, NULL);
1360
1361	if (z)
1362	    suffixnoinslen = inv ? 0 : n;
1363	else if (inv) {
1364	    /*
1365	     * negative match, \- wasn't present, so it *should*
1366	     * have this suffix length
1367	     */
1368	    suffixnoinslen = n;
1369	}
1370
1371	lasts = wptr = ws;
1372	while (i) {
1373	    if (i >= 3 && wptr[1] == ZWC('-')) {
1374		ZLE_CHAR_T str[2];
1375
1376		if (wptr > lasts)
1377		    addsuffix(inv ? SUFTYP_NEGSTR : SUFTYP_POSSTR, 0,
1378			      lasts, wptr - lasts, n);
1379		str[0] = *wptr;
1380		str[1] = wptr[2];
1381		addsuffix(inv ? SUFTYP_NEGRNG : SUFTYP_POSRNG, 0,
1382			  str, 2, n);
1383
1384		wptr += 3;
1385		i -= 3;
1386		lasts = wptr;
1387	    } else {
1388		wptr++;
1389		i--;
1390	    }
1391	}
1392	if (wptr > lasts)
1393	    addsuffix(inv ? SUFTYP_NEGSTR : SUFTYP_POSSTR, 0,
1394		      lasts, wptr - lasts, n);
1395	free(ws);
1396    } else
1397	makesuffix(n);
1398}
1399
1400/* Remove suffix, if there is one, when inserting character c. */
1401
1402/**/
1403mod_export void
1404iremovesuffix(ZLE_INT_T c, int keep)
1405{
1406    if (suffixfunc) {
1407	Shfunc shfunc = getshfunc(suffixfunc);
1408
1409	if (shfunc) {
1410	    LinkList args = newlinklist();
1411	    char buf[20];
1412	    int osc = sfcontext;
1413	    int wasmeta = (zlemetaline != 0);
1414
1415	    if (wasmeta) {
1416		/*
1417		 * The suffix removal function runs as a normal
1418		 * ZLE function, not a completion function, so
1419		 * the line should be unmetafied if this was
1420		 * called from completion.  (It may not be since
1421		 * we may decide to remove the suffix later.)
1422		 */
1423		unmetafy_line();
1424	    }
1425
1426	    sprintf(buf, "%d", suffixfunclen);
1427	    addlinknode(args, suffixfunc);
1428	    addlinknode(args, buf);
1429
1430	    startparamscope();
1431	    makezleparams(0);
1432	    sfcontext = SFC_COMPLETE;
1433	    doshfunc(shfunc, args, 1);
1434	    sfcontext = osc;
1435	    endparamscope();
1436
1437	    if (wasmeta)
1438		metafy_line();
1439	}
1440	zsfree(suffixfunc);
1441	suffixfunc = NULL;
1442    } else {
1443	int sl = 0, flags = 0;
1444	struct suffixset *ss;
1445
1446	if (c == NO_INSERT_CHAR) {
1447	    sl = suffixnoinslen;
1448	} else {
1449	    ZLE_CHAR_T ch = c;
1450	    /*
1451	     * Search for a match for ch in the suffix list.
1452	     * We stop if we encounter a match in a positive or negative
1453	     * list, using the suffix length specified or zero respectively.
1454	     * If we reached the end and passed through a negative
1455	     * list, we use the suffix length for that, else zero.
1456	     * This would break if it were possible to have negative
1457	     * sets with different suffix length:  that's not supposed
1458	     * to happen.
1459	     */
1460	    int negsuflen = 0, found = 0;
1461
1462	    for (ss = suffixlist; ss; ss = ss->next) {
1463		switch (ss->tp) {
1464		case SUFTYP_POSSTR:
1465		    if (ZS_memchr(ss->chars, ch, ss->lenstr)) {
1466			sl = ss->lensuf;
1467			found = 1;
1468		    }
1469		    break;
1470
1471		case SUFTYP_NEGSTR:
1472		    if (ZS_memchr(ss->chars, ch, ss->lenstr)) {
1473			sl = 0;
1474			found = 1;
1475		    } else {
1476			negsuflen = ss->lensuf;
1477		    }
1478		    break;
1479
1480		case SUFTYP_POSRNG:
1481		    if (ss->chars[0] <= ch && ch <= ss->chars[1]) {
1482			sl = ss->lensuf;
1483			found = 1;
1484		    }
1485		    break;
1486
1487		case SUFTYP_NEGRNG:
1488		    if (ss->chars[0] <= ch && ch <= ss->chars[1]) {
1489			sl = 0;
1490			found = 1;
1491		    } else {
1492			negsuflen = ss->lensuf;
1493		    }
1494		    break;
1495		}
1496		if (found) {
1497		    flags = ss->flags;
1498		    break;
1499		}
1500	    }
1501
1502	    if (!found)
1503		sl = negsuflen;
1504	}
1505	if (sl) {
1506	    /* must be shifting wide character lengths */
1507	    backdel(sl, CUT_RAW);
1508	    if (flags & SUFFLAGS_SPACE)
1509	    {
1510		/* Add a space and advance over it */
1511		spaceinline(1);
1512		if (zlemetaline) {
1513		    zlemetaline[zlemetacs++] = ' ';
1514		} else {
1515		    zleline[zlecs++] = ZWC(' ');
1516		}
1517	    }
1518	    if (!keep)
1519		invalidatelist();
1520	}
1521    }
1522    fixsuffix();
1523}
1524
1525/* Fix the suffix in place, if there is one, making it non-removable. */
1526
1527/**/
1528mod_export void
1529fixsuffix(void)
1530{
1531    while (suffixlist) {
1532	struct suffixset *next = suffixlist->next;
1533
1534	if (suffixlist->lenstr)
1535	    zfree(suffixlist->chars, suffixlist->lenstr * sizeof(ZLE_CHAR_T));
1536	zfree(suffixlist, sizeof(struct suffixset));
1537
1538	suffixlist = next;
1539    }
1540
1541    suffixfunclen = suffixnoinslen = 0;
1542}
1543