1/*
2 * zle_params.c - ZLE special parameters
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
32#include "zle_params.pro"
33
34/*
35 * ZLE SPECIAL PARAMETERS:
36 *
37 * These special parameters are created, with a local scope, when
38 * running user-defined widget functions.  Reading and writing them
39 * reads and writes bits of ZLE state.  The parameters are:
40 *
41 * BUFFER   (scalar)   entire buffer contents
42 * CURSOR   (integer)  cursor position; 0 <= $CURSOR <= $#BUFFER
43 * LBUFFER  (scalar)   portion of buffer to the left of the cursor
44 * RBUFFER  (scalar)   portion of buffer to the right of the cursor
45 */
46
47static const struct gsu_scalar buffer_gsu =
48{ get_buffer, set_buffer, zleunsetfn };
49static const struct gsu_scalar context_gsu =
50{ get_context, nullstrsetfn, zleunsetfn };
51static const struct gsu_scalar cutbuffer_gsu =
52{ get_cutbuffer, set_cutbuffer, unset_cutbuffer };
53static const struct gsu_scalar keymap_gsu =
54{ get_keymap, nullstrsetfn, zleunsetfn };
55static const struct gsu_scalar keys_gsu =
56{ get_keys, nullstrsetfn, zleunsetfn };
57static const struct gsu_scalar lastabortedsearch_gsu =
58{ get_lasearch, nullstrsetfn, zleunsetfn };
59static const struct gsu_scalar lastsearch_gsu =
60{ get_lsearch, nullstrsetfn, zleunsetfn };
61static const struct gsu_scalar lastwidget_gsu =
62{ get_lwidget, nullstrsetfn, zleunsetfn };
63static const struct gsu_scalar lbuffer_gsu =
64{ get_lbuffer, set_lbuffer, zleunsetfn };
65static const struct gsu_scalar postdisplay_gsu =
66{ get_postdisplay, set_postdisplay, zleunsetfn };
67static const struct gsu_scalar prebuffer_gsu =
68{ get_prebuffer, nullstrsetfn, zleunsetfn };
69static const struct gsu_scalar predisplay_gsu =
70{ get_predisplay, set_predisplay, zleunsetfn };
71static const struct gsu_scalar rbuffer_gsu =
72{ get_rbuffer, set_rbuffer, zleunsetfn };
73static const struct gsu_scalar widget_gsu =
74{ get_widget, nullstrsetfn, zleunsetfn };
75static const struct gsu_scalar widgetfunc_gsu =
76{ get_widgetfunc, nullstrsetfn, zleunsetfn };
77static const struct gsu_scalar widgetstyle_gsu =
78{ get_widgetstyle, nullstrsetfn, zleunsetfn };
79static const struct gsu_scalar zle_state_gsu =
80{ get_zle_state, nullstrsetfn, zleunsetfn };
81
82static const struct gsu_integer bufferlines_gsu =
83{ get_bufferlines, NULL, zleunsetfn };
84static const struct gsu_integer cursor_gsu =
85{ get_cursor, set_cursor, zleunsetfn };
86static const struct gsu_integer histno_gsu =
87{ get_histno, set_histno, zleunsetfn };
88static const struct gsu_integer mark_gsu =
89{ get_mark, set_mark, zleunsetfn };
90static const struct gsu_integer numeric_gsu =
91{ get_numeric, set_numeric, unset_numeric };
92static const struct gsu_integer pending_gsu =
93{ get_pending, NULL, zleunsetfn };
94static const struct gsu_integer region_active_gsu =
95{ get_region_active, set_region_active, zleunsetfn };
96static const struct gsu_integer undo_change_no_gsu =
97{ get_undo_current_change, NULL, zleunsetfn };
98
99static const struct gsu_array killring_gsu =
100{ get_killring, set_killring, unset_killring };
101/* implementation is in zle_refresh.c */
102static const struct gsu_array region_highlight_gsu =
103{ get_region_highlight, set_region_highlight, unset_region_highlight };
104
105#define GSU(X) ( (GsuScalar)(void*)(&(X)) )
106static struct zleparam {
107    char *name;
108    int type;
109    GsuScalar gsu;
110    void *data;
111} zleparams[] = {
112    { "BUFFER",  PM_SCALAR,  GSU(buffer_gsu), NULL },
113    { "BUFFERLINES", PM_INTEGER | PM_READONLY, GSU(bufferlines_gsu),
114        NULL },
115    { "CONTEXT", PM_SCALAR | PM_READONLY, GSU(context_gsu),
116	NULL },
117    { "CURSOR",  PM_INTEGER, GSU(cursor_gsu),
118	NULL },
119    { "CUTBUFFER", PM_SCALAR, GSU(cutbuffer_gsu), NULL },
120    { "HISTNO", PM_INTEGER, GSU(histno_gsu), NULL },
121    { "KEYMAP", PM_SCALAR | PM_READONLY, GSU(keymap_gsu), NULL },
122    { "KEYS", PM_SCALAR | PM_READONLY, GSU(keys_gsu), NULL },
123    { "killring", PM_ARRAY, GSU(killring_gsu), NULL },
124    { "LASTABORTEDSEARCH", PM_SCALAR | PM_READONLY, GSU(lastabortedsearch_gsu),
125      NULL },
126    { "LASTSEARCH", PM_SCALAR | PM_READONLY, GSU(lastsearch_gsu), NULL },
127    { "LASTWIDGET", PM_SCALAR | PM_READONLY, GSU(lastwidget_gsu), NULL },
128    { "LBUFFER", PM_SCALAR,  GSU(lbuffer_gsu), NULL },
129    { "MARK",  PM_INTEGER, GSU(mark_gsu), NULL },
130    { "NUMERIC", PM_INTEGER | PM_UNSET, GSU(numeric_gsu), NULL },
131    { "PENDING", PM_INTEGER | PM_READONLY, GSU(pending_gsu), NULL },
132    { "POSTDISPLAY", PM_SCALAR, GSU(postdisplay_gsu), NULL },
133    { "PREBUFFER",  PM_SCALAR | PM_READONLY,  GSU(prebuffer_gsu), NULL },
134    { "PREDISPLAY", PM_SCALAR, GSU(predisplay_gsu), NULL },
135    { "RBUFFER", PM_SCALAR,  GSU(rbuffer_gsu), NULL },
136    { "REGION_ACTIVE", PM_INTEGER, GSU(region_active_gsu), NULL},
137    { "region_highlight", PM_ARRAY, GSU(region_highlight_gsu), NULL },
138    { "UNDO_CHANGE_NO", PM_INTEGER | PM_READONLY, GSU(undo_change_no_gsu),
139      NULL },
140    { "WIDGET", PM_SCALAR | PM_READONLY, GSU(widget_gsu), NULL },
141    { "WIDGETFUNC", PM_SCALAR | PM_READONLY, GSU(widgetfunc_gsu), NULL },
142    { "WIDGETSTYLE", PM_SCALAR | PM_READONLY, GSU(widgetstyle_gsu), NULL },
143    { "ZLE_STATE", PM_SCALAR | PM_READONLY, GSU(zle_state_gsu), NULL },
144    { NULL, 0, NULL, NULL }
145};
146
147/* ro means parameters are readonly, used from completion */
148
149/**/
150mod_export void
151makezleparams(int ro)
152{
153    struct zleparam *zp;
154
155    for(zp = zleparams; zp->name; zp++) {
156	Param pm = createparam(zp->name, (zp->type |PM_SPECIAL|PM_REMOVABLE|
157					  PM_LOCAL|(ro ? PM_READONLY : 0)));
158	if (!pm)
159	    pm = (Param) paramtab->getnode(paramtab, zp->name);
160	DPUTS(!pm, "param not set in makezleparams");
161
162	pm->level = locallevel + 1;
163	pm->u.data = zp->data;
164	switch(PM_TYPE(zp->type)) {
165	    case PM_SCALAR:
166		pm->gsu.s = zp->gsu;
167		break;
168	    case PM_ARRAY:
169		pm->gsu.a = (GsuArray)zp->gsu;
170		break;
171	    case PM_INTEGER:
172		pm->gsu.i = (GsuInteger)zp->gsu;
173		pm->base = 10;
174		break;
175	}
176	if ((zp->type & PM_UNSET) && (zmod.flags & MOD_MULT))
177	    pm->node.flags &= ~PM_UNSET;
178    }
179}
180
181/* Special unset function for ZLE special parameters: act like the standard *
182 * unset function if this is a user-initiated unset, but nothing is done if *
183 * the parameter is merely going out of scope (which it will do).           */
184
185/**/
186static void
187zleunsetfn(Param pm, int exp)
188{
189    if(exp)
190	stdunsetfn(pm, exp);
191}
192
193/**/
194static void
195set_buffer(UNUSED(Param pm), char *x)
196{
197    if(x) {
198	setline(x, 0);
199	zsfree(x);
200    } else
201	zlecs = zlell = 0;
202    fixsuffix();
203    menucmp = 0;
204}
205
206/**/
207static char *
208get_buffer(UNUSED(Param pm))
209{
210    if (zlemetaline != 0)
211	return dupstring(zlemetaline);
212    return zlelineasstring(zleline, zlell, 0, NULL, NULL, 1);
213}
214
215/**/
216static void
217set_cursor(UNUSED(Param pm), zlong x)
218{
219    if(x < 0)
220	zlecs = 0;
221    else if(x > zlell)
222	zlecs = zlell;
223    else
224	zlecs = x;
225    fixsuffix();
226    menucmp = 0;
227}
228
229/**/
230static zlong
231get_cursor(UNUSED(Param pm))
232{
233    if (zlemetaline != NULL) {
234	/* A lot of work for one number, but still... */
235	ZLE_STRING_T tmpline;
236	int tmpcs, tmpll, tmpsz;
237	char *tmpmetaline = ztrdup(zlemetaline);
238	tmpline = stringaszleline(tmpmetaline, zlemetacs,
239				  &tmpll, &tmpsz, &tmpcs);
240	free(tmpmetaline);
241	free(tmpline);
242	return tmpcs;
243    }
244    return zlecs;
245}
246
247/**/
248static void
249set_mark(UNUSED(Param pm), zlong x)
250{
251    if (x < 0)
252	mark = 0;
253    else if (x > zlell)
254	mark = zlell;
255    else
256	mark = x;
257}
258
259/**/
260static zlong
261get_mark(UNUSED(Param pm))
262{
263    return mark;
264}
265
266/**/
267static void
268set_region_active(UNUSED(Param pm), zlong x)
269{
270    region_active = (int)!!x;
271}
272
273/**/
274static zlong
275get_region_active(UNUSED(Param pm))
276{
277    return region_active;
278}
279
280/**/
281static void
282set_lbuffer(UNUSED(Param pm), char *x)
283{
284    ZLE_STRING_T y;
285    int len;
286
287    if (x && *x != ZWC('\0'))
288	y = stringaszleline(x, 0, &len, NULL, NULL);
289    else
290	y = ZWS(""), len = 0;
291    sizeline(zlell - zlecs + len);
292    ZS_memmove(zleline + len, zleline + zlecs, zlell - zlecs);
293    ZS_memcpy(zleline, y, len);
294    zlell = zlell - zlecs + len;
295    zlecs = len;
296    zsfree(x);
297    if (len)
298	free(y);
299    fixsuffix();
300    menucmp = 0;
301}
302
303/**/
304static char *
305get_lbuffer(UNUSED(Param pm))
306{
307    if (zlemetaline != NULL)
308	return dupstrpfx(zlemetaline, zlemetacs);
309    return zlelineasstring(zleline, zlecs, 0, NULL, NULL, 1);
310}
311
312/**/
313static void
314set_rbuffer(UNUSED(Param pm), char *x)
315{
316    ZLE_STRING_T y;
317    int len;
318
319    if (x && *x != ZWC('\0'))
320	y = stringaszleline(x, 0, &len, NULL, NULL);
321    else
322	y = ZWS(""), len = 0;
323    sizeline(zlell = zlecs + len);
324    ZS_memcpy(zleline + zlecs, y, len);
325    zsfree(x);
326    if (len)
327	free(y);
328    fixsuffix();
329    menucmp = 0;
330}
331
332/**/
333static char *
334get_rbuffer(UNUSED(Param pm))
335{
336    if (zlemetaline != NULL)
337	return dupstrpfx((char *)zlemetaline + zlemetacs,
338			 zlemetall - zlemetacs);
339    return zlelineasstring(zleline + zlecs, zlell - zlecs, 0, NULL, NULL, 1);
340}
341
342/**/
343static char *
344get_prebuffer(UNUSED(Param pm))
345{
346    /*
347     * Use the editing current history line, not necessarily the
348     * history line that's currently in the history mechanism
349     * since our line may have been stacked.
350     */
351    if (zle_chline) {
352	/* zle_chline was NULL terminated when pushed onto the stack */
353	return dupstring(zle_chline);
354    }
355    if (chline) {
356	/* hptr is valid */
357	return dupstrpfx(chline, hptr - chline);
358    }
359    return dupstring("");
360}
361
362/**/
363static char *
364get_widget(UNUSED(Param pm))
365{
366    return bindk->nam;
367}
368
369/**/
370static char *
371get_widgetfunc(UNUSED(Param pm))
372{
373    Widget widget = bindk->widget;
374    int flags = widget->flags;
375
376    if (flags & WIDGET_INT)
377	return ".internal";	/* Don't see how this can ever be returned... */
378    if (flags & WIDGET_NCOMP)
379	return widget->u.comp.func;
380    return widget->u.fnnam;
381}
382
383/**/
384static char *
385get_widgetstyle(UNUSED(Param pm))
386{
387    Widget widget = bindk->widget;
388    int flags = widget->flags;
389
390    if (flags & WIDGET_INT)
391	return ".internal";	/* Don't see how this can ever be returned... */
392    if (flags & WIDGET_NCOMP)
393	return widget->u.comp.wid;
394    return "";
395}
396
397/**/
398static char *
399get_lwidget(UNUSED(Param pm))
400{
401    return (lbindk ? lbindk->nam : "");
402}
403
404/**/
405static char *
406get_keymap(UNUSED(Param pm))
407{
408    return dupstring(curkeymapname);
409}
410
411/**/
412static char *
413get_keys(UNUSED(Param pm))
414{
415    return keybuf;
416}
417
418/**/
419static void
420set_numeric(UNUSED(Param pm), zlong x)
421{
422    zmult = x;
423    zmod.flags = MOD_MULT;
424}
425
426/**/
427static zlong
428get_numeric(UNUSED(Param pm))
429{
430    return zmult;
431}
432
433/**/
434static void
435unset_numeric(Param pm, int exp)
436{
437    if (exp) {
438	stdunsetfn(pm, exp);
439	zmod.flags = 0;
440	zmult = 1;
441    }
442}
443
444/**/
445static void
446set_histno(UNUSED(Param pm), zlong x)
447{
448    Histent he;
449
450    if (!(he = quietgethist((int)x)))
451	return;
452    zle_setline(he);
453}
454
455/**/
456static zlong
457get_histno(UNUSED(Param pm))
458{
459    return histline;
460}
461
462/**/
463static zlong
464get_bufferlines(UNUSED(Param pm))
465{
466    return nlnct;
467}
468
469/**/
470static zlong
471get_pending(UNUSED(Param pm))
472{
473    return noquery(0);
474}
475
476/**/
477static char *
478get_cutbuffer(UNUSED(Param pm))
479{
480    if (cutbuf.buf)
481	return zlelineasstring(cutbuf.buf, cutbuf.len, 0, NULL, NULL, 1);
482    return "";
483}
484
485
486/**/
487static void
488set_cutbuffer(UNUSED(Param pm), char *x)
489{
490    if (cutbuf.buf)
491	free(cutbuf.buf);
492    cutbuf.flags = 0;
493    if (x) {
494	int n;
495	cutbuf.buf = stringaszleline(x, 0, &n, NULL, NULL);
496	cutbuf.len = n;
497	free(x);
498    } else {
499	cutbuf.buf = NULL;
500	cutbuf.len = 0;
501    }
502}
503
504/**/
505static void
506unset_cutbuffer(Param pm, int exp)
507{
508    if (exp) {
509	stdunsetfn(pm, exp);
510	if (cutbuf.buf) {
511	    free(cutbuf.buf);
512	    cutbuf.buf = NULL;
513	    cutbuf.len = 0;
514	}
515    }
516}
517
518/**/
519static void
520set_killring(UNUSED(Param pm), char **x)
521{
522    int kcnt;
523    Cutbuffer kptr;
524    char **p;
525
526    if (kring) {
527	for (kptr = kring, kcnt = 0; kcnt < kringsize; kcnt++, kptr++)
528	    if (kptr->buf)
529		free(kptr->buf);
530	zfree(kring, kringsize * sizeof(struct cutbuffer));
531	kring = NULL;
532	kringsize = kringnum = 0;
533    }
534    if (x) {
535	/*
536	 * Insert the elements into the kill ring.
537	 * Regardless of the old order, we number it with the current
538	 * entry first.
539	 *
540	 * Be careful to add elements by looping backwards; this
541	 * fits in with how we cycle the ring.
542	 */
543	int kpos = 0;
544	kringsize = arrlen(x);
545	if (kringsize != 0) {
546	    kring = (Cutbuffer)zshcalloc(kringsize * sizeof(struct cutbuffer));
547	    for (p = x; *p; p++) {
548		int n, len = strlen(*p);
549		kptr = kring + kpos;
550
551		kptr->buf = stringaszleline(*p, 0, &n, NULL, NULL);
552		kptr->len = n;
553
554		zfree(*p, len+1);
555		kpos = (kpos + kringsize -1 ) % kringsize;
556	    }
557	}
558	free(x);
559    }
560}
561
562/**/
563static char **
564get_killring(UNUSED(Param pm))
565{
566    /*
567     * Return the kill ring with the most recently killed first.
568     * Since the kill ring is no longer a fixed length, we return
569     * all entries even if empty.
570     */
571    int kpos, kcnt;
572    char **ret, **p;
573
574    /* Supposed to work even if kring is NULL */
575    if (!kring) {
576	kringsize = KRINGCTDEF;
577	kring = (Cutbuffer)zshcalloc(kringsize * sizeof(struct cutbuffer));
578    }
579
580    p = ret = (char **)zhalloc((kringsize+1) * sizeof(char *));
581
582    for (kpos = kringnum, kcnt = 0; kcnt < kringsize; kcnt++) {
583	Cutbuffer kptr = kring + kpos;
584	if (kptr->buf)
585	{
586	    /* Allocate on heap. */
587	    *p++ = zlelineasstring(kptr->buf, kptr->len, 0, NULL, NULL, 1);
588	}
589	else
590	    *p++ = dupstring("");
591	kpos = (kpos + kringsize - 1) % kringsize;
592    }
593    *p = NULL;
594
595    return ret;
596}
597
598/**/
599static void
600unset_killring(Param pm, int exp)
601{
602    if (exp) {
603	set_killring(pm, NULL);
604	stdunsetfn(pm, exp);
605    }
606}
607
608static void
609set_prepost(ZLE_STRING_T *textvar, int *lenvar, char *x)
610{
611    if (*lenvar) {
612	free(*textvar);
613	*textvar = NULL;
614	*lenvar = 0;
615    }
616    if (x) {
617	*textvar = stringaszleline(x, 0, lenvar, NULL, NULL);
618	free(x);
619    }
620}
621
622static char *
623get_prepost(ZLE_STRING_T text, int len)
624{
625    return zlelineasstring(text, len, 0, NULL, NULL, 1);
626}
627
628/**/
629static void
630set_predisplay(UNUSED(Param pm), char *x)
631{
632    set_prepost(&predisplay, &predisplaylen, x);
633}
634
635/**/
636static char *
637get_predisplay(UNUSED(Param pm))
638{
639    return get_prepost(predisplay, predisplaylen);
640}
641
642/**/
643static void
644set_postdisplay(UNUSED(Param pm), char *x)
645{
646    set_prepost(&postdisplay, &postdisplaylen, x);
647}
648
649/**/
650static char *
651get_postdisplay(UNUSED(Param pm))
652{
653    return get_prepost(postdisplay, postdisplaylen);
654}
655
656/**/
657void
658free_prepostdisplay(void)
659{
660    if (predisplaylen)
661	set_prepost(&predisplay, &predisplaylen, NULL);
662    if (postdisplaylen)
663	set_prepost(&postdisplay, &postdisplaylen, NULL);
664}
665
666/**/
667static char *
668get_lasearch(UNUSED(Param pm))
669{
670    if (previous_aborted_search)
671	return previous_aborted_search;
672    return "";
673}
674
675/**/
676static char *
677get_lsearch(UNUSED(Param pm))
678{
679    if (previous_search)
680	return previous_search;
681    return "";
682}
683
684/**/
685static char *
686get_context(UNUSED(Param pm))
687{
688    switch (zlecontext) {
689    case ZLCON_LINE_CONT:
690	return "cont";
691	break;
692
693    case ZLCON_SELECT:
694	return "select";
695	break;
696
697    case ZLCON_VARED:
698	return "vared";
699	break;
700
701    case ZLCON_LINE_START:
702    default:
703	return "start";
704	break;
705    }
706}
707
708/**/
709static char *
710get_zle_state(UNUSED(Param pm))
711{
712    char *zle_state = NULL, *ptr = NULL, **arr = NULL;
713    int itp, istate, len = 0;
714
715    /*
716     * Substrings are sorted at the end, so the user can
717     * easily match against this parameter:
718     * if [[ $ZLE_STATE == *bar*foo*zonk* ]]; then ...; fi
719     */
720    for (itp = 0; itp < 2; itp++) {
721	char *str;
722	for (istate = 0; istate < 2; istate++) {
723	    int slen;
724	    switch (istate) {
725	    case 0:
726		if (insmode) {
727		    str = "insert";
728		} else {
729		    str = "overwrite";
730		}
731		break;
732	    case 1:
733		if (hist_skip_flags & HIST_FOREIGN) {
734		    str = "localhistory";
735		} else {
736		    str = "globalhistory";
737		}
738		break;
739
740	    default:
741		str = "";
742	    }
743	    slen = strlen(str);
744	    if (itp == 0) {
745		/* Accumulating length */
746		if (istate)
747		    len++;	/* for space */
748		len += slen;
749	    } else {
750		/* Accumulating string */
751		if (istate)
752		    *ptr++ = ':';
753		memcpy(ptr, str, slen);
754		ptr += slen;
755	    }
756	}
757	if (itp == 0) {
758	    len++;		/* terminating NULL */
759	    ptr = zle_state = (char *)zhalloc(len);
760	} else {
761	    *ptr = '\0';
762	}
763    }
764
765    arr = colonsplit(zle_state, 0);
766    strmetasort(arr, SORTIT_ANYOLDHOW, NULL);
767    zle_state = zjoin(arr, ' ', 1);
768    freearray(arr);
769
770    return zle_state;
771}
772