mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 18:10:56 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1542 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1542 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * zle_misc.c - miscellaneous editor routines
 | |
|  *
 | |
|  * This file is part of zsh, the Z shell.
 | |
|  *
 | |
|  * Copyright (c) 1992-1997 Paul Falstad
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, without written agreement and without
 | |
|  * license or royalty fees, to use, copy, modify, and distribute this
 | |
|  * software and to distribute modified versions of this software for any
 | |
|  * purpose, provided that the above copyright notice and the following
 | |
|  * two paragraphs appear in all copies of this software.
 | |
|  *
 | |
|  * In no event shall Paul Falstad or the Zsh Development Group be liable
 | |
|  * to any party for direct, indirect, special, incidental, or consequential
 | |
|  * damages arising out of the use of this software and its documentation,
 | |
|  * even if Paul Falstad and the Zsh Development Group have been advised of
 | |
|  * the possibility of such damage.
 | |
|  *
 | |
|  * Paul Falstad and the Zsh Development Group specifically disclaim any
 | |
|  * warranties, including, but not limited to, the implied warranties of
 | |
|  * merchantability and fitness for a particular purpose.  The software
 | |
|  * provided hereunder is on an "as is" basis, and Paul Falstad and the
 | |
|  * Zsh Development Group have no obligation to provide maintenance,
 | |
|  * support, updates, enhancements, or modifications.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "zle.mdh"
 | |
| #include "zle_misc.pro"
 | |
| 
 | |
| /* insert a zle string, with repetition and suffix removal */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| doinsert(ZLE_STRING_T zstr, int len)
 | |
| {
 | |
|     ZLE_STRING_T s;
 | |
|     ZLE_CHAR_T c1 = *zstr;	     /* first character */
 | |
|     int neg = zmult < 0;             /* insert *after* the cursor? */
 | |
|     int m = neg ? -zmult : zmult;    /* number of copies to insert */
 | |
|     int count;
 | |
| 
 | |
|     UNMETACHECK();
 | |
| 
 | |
|     iremovesuffix(c1, 0);
 | |
|     invalidatelist();
 | |
| 
 | |
|     if (insmode)
 | |
| 	spaceinline(m * len);
 | |
|     else
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     {
 | |
| 	int pos = zlecs, diff, i;
 | |
| 
 | |
| 	/*
 | |
| 	 * Calculate the number of character positions we are
 | |
| 	 * going to be using.  The algorithm is that
 | |
| 	 * anything that shows up as a logical single character
 | |
| 	 * (i.e. even if control, or double width, or with combining
 | |
| 	 * characters) is treated as 1 for the purpose of replacing
 | |
| 	 * what's there already.
 | |
| 	 *
 | |
| 	 * This can cause inserting of a combining character in
 | |
| 	 * places where it should overwrite, such as the start
 | |
| 	 * of a line.  However, combining characters aren't
 | |
| 	 * useful there anyway and this doesn't cause any
 | |
| 	 * particular harm.
 | |
| 	 */
 | |
| 	for (i = 0, count = 0; i < len; i++) {
 | |
| 	    if (!IS_COMBINING(zstr[i]))
 | |
| 		count++;
 | |
| 	}
 | |
| 	/*
 | |
| 	 * Ensure we replace a complete combining character
 | |
| 	 * for each character we overwrite.
 | |
| 	 */
 | |
| 	for (i = count; pos < zlell && i--; ) {
 | |
| 	    INCPOS(pos);
 | |
| 	}
 | |
| 	/*
 | |
| 	 * Calculate how many raw line places we need.
 | |
| 	 * pos - zlecs is the raw line distance we're replacing,
 | |
| 	 * m * len the number we're inserting.
 | |
| 	 */
 | |
| 	diff = pos - zlecs - m * len;
 | |
| 	if (diff < 0) {
 | |
| 	    spaceinline(-diff);
 | |
| 	} else if (diff > 0) {
 | |
| 	    /*
 | |
| 	     * We use shiftchars() here because we don't
 | |
| 	     * want combining char alignment fixed up: we
 | |
| 	     * are going to write over any that remain.
 | |
| 	     */
 | |
| 	    shiftchars(zlecs, diff);
 | |
| 	}
 | |
|     }
 | |
| #else
 | |
|     if (zlecs + m * len > zlell)
 | |
| 	spaceinline(zlecs + m * len - zlell);
 | |
| #endif
 | |
|     while (m--)
 | |
| 	for (s = zstr, count = len; count; s++, count--)
 | |
| 	    zleline[zlecs++] = *s;
 | |
|     if (neg)
 | |
| 	zlecs += zmult * len;
 | |
|     /* if we ended up on a combining character, skip over it */
 | |
|     CCRIGHT();
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export int
 | |
| selfinsert(UNUSED(char **args))
 | |
| {
 | |
|     ZLE_CHAR_T tmp;
 | |
| 
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     if (!lastchar_wide_valid)
 | |
| 	if (getrestchar(lastchar) == WEOF)
 | |
| 	    return 1;
 | |
| #endif
 | |
|     tmp = LASTFULLCHAR;
 | |
|     doinsert(&tmp, 1);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| fixunmeta(void)
 | |
| {
 | |
|     lastchar &= 0x7f;
 | |
|     if (lastchar == '\r')
 | |
| 	lastchar = '\n';
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     /*
 | |
|      * TODO: can we do this better?
 | |
|      * We need a wide character to insert.
 | |
|      * selfinsertunmeta is intrinsically problematic
 | |
|      * with multibyte input.
 | |
|      */
 | |
|     lastchar_wide = (ZLE_INT_T)lastchar;
 | |
|     lastchar_wide_valid = 1;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export int
 | |
| selfinsertunmeta(char **args)
 | |
| {
 | |
|     fixunmeta();
 | |
|     return selfinsert(args);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| deletechar(char **args)
 | |
| {
 | |
|     int n;
 | |
|     if (zmult < 0) {
 | |
| 	int ret;
 | |
| 	zmult = -zmult;
 | |
| 	ret = backwarddeletechar(args);
 | |
| 	zmult = -zmult;
 | |
| 	return ret;
 | |
|     }
 | |
| 
 | |
|     n = zmult;
 | |
|     while (n--) {
 | |
| 	if (zlecs == zlell)
 | |
| 	    return 1;
 | |
| 	INCCS();
 | |
|     }
 | |
|     backdel(zmult, 0);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| backwarddeletechar(char **args)
 | |
| {
 | |
|     if (zmult < 0) {
 | |
| 	int ret;
 | |
| 	zmult = -zmult;
 | |
| 	ret = deletechar(args);
 | |
| 	zmult = -zmult;
 | |
| 	return ret;
 | |
|     }
 | |
|     backdel(zmult > zlecs ? zlecs : zmult, 0);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| killwholeline(UNUSED(char **args))
 | |
| {
 | |
|     int i, fg, n = zmult;
 | |
| 
 | |
|     if (n < 0)
 | |
| 	return 1;
 | |
|     while (n--) {
 | |
| 	if ((fg = (zlecs && zlecs == zlell)))
 | |
| 	    zlecs--;
 | |
| 	while (zlecs && zleline[zlecs - 1] != '\n')
 | |
| 	    zlecs--;
 | |
| 	for (i = zlecs; i != zlell && zleline[i] != '\n'; i++);
 | |
| 	forekill(i - zlecs + (i != zlell), fg ? (CUT_FRONT|CUT_RAW) : CUT_RAW);
 | |
|     }
 | |
|     clearlist = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| killbuffer(UNUSED(char **args))
 | |
| {
 | |
|     zlecs = 0;
 | |
|     forekill(zlell, CUT_RAW);
 | |
|     clearlist = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| backwardkillline(char **args)
 | |
| {
 | |
|     int i = 0, n = zmult;
 | |
| 
 | |
|     if (n < 0) {
 | |
| 	int ret;
 | |
| 	zmult = -n;
 | |
| 	ret = killline(args);
 | |
| 	zmult = n;
 | |
| 	return ret;
 | |
|     }
 | |
|     while (n--) {
 | |
| 	if (zlecs && zleline[zlecs - 1] == '\n')
 | |
| 	    zlecs--, i++;
 | |
| 	else
 | |
| 	    while (zlecs && zleline[zlecs - 1] != '\n')
 | |
| 		zlecs--, i++;
 | |
|     }
 | |
|     forekill(i, CUT_FRONT|CUT_RAW);
 | |
|     clearlist = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| /*
 | |
|  * Transpose the chunk of the line from start to middle with
 | |
|  * that from middle to end.
 | |
|  */
 | |
| 
 | |
| static void
 | |
| transpose_swap(int start, int middle, int end)
 | |
| {
 | |
|     int len1, len2;
 | |
|     ZLE_STRING_T first;
 | |
| 
 | |
|     len1 = middle - start;
 | |
|     len2 = end - middle;
 | |
| 
 | |
|     first = (ZLE_STRING_T)zalloc(len1 * ZLE_CHAR_SIZE);
 | |
|     ZS_memcpy(first, zleline + start, len1);
 | |
|     /* Move may be overlapping... */
 | |
|     ZS_memmove(zleline + start, zleline + middle, len2);
 | |
|     ZS_memcpy(zleline + start + len2, first, len1);
 | |
|     zfree(first, len1 * ZLE_CHAR_SIZE);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /**/
 | |
| int
 | |
| gosmacstransposechars(UNUSED(char **args))
 | |
| {
 | |
|     if (zlecs < 2 || zleline[zlecs - 1] == '\n' || zleline[zlecs - 2] == '\n') {
 | |
| 	int twice = (zlecs == 0 || zleline[zlecs - 1] == '\n');
 | |
| 
 | |
| 	if (zlecs == zlell || zleline[zlecs] == '\n')
 | |
| 	    return 1;
 | |
| 
 | |
| 	INCCS();
 | |
| 	if (twice) {
 | |
| 	    if (zlecs == zlell || zleline[zlecs] == '\n')
 | |
| 		return 1;
 | |
| 	    INCCS();
 | |
| 	}
 | |
|     }
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     {
 | |
| 	int start, middle;
 | |
| 
 | |
| 	middle = zlecs;
 | |
| 	DECPOS(middle);
 | |
| 
 | |
| 	start = middle;
 | |
| 	DECPOS(start);
 | |
| 
 | |
| 	transpose_swap(start, middle, zlecs);
 | |
|     }
 | |
| #else
 | |
|     {
 | |
| 	ZLE_CHAR_T cc = zleline[zlecs - 2];
 | |
| 	zleline[zlecs - 2] = zleline[zlecs - 1];
 | |
| 	zleline[zlecs - 1] = cc;
 | |
|     }
 | |
| #endif
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| transposechars(UNUSED(char **args))
 | |
| {
 | |
|     int ct;
 | |
|     int n = zmult;
 | |
|     int neg = n < 0;
 | |
| 
 | |
|     if (neg)
 | |
| 	n = -n;
 | |
|     while (n--) {
 | |
| 	if (!(ct = zlecs) || zleline[zlecs - 1] == '\n') {
 | |
| 	    if (zlell == zlecs || zleline[zlecs] == '\n')
 | |
| 		return 1;
 | |
| 	    if (!neg)
 | |
| 		INCCS();
 | |
| 	    INCPOS(ct);
 | |
| 	}
 | |
| 	if (neg) {
 | |
| 	    if (zlecs && zleline[zlecs - 1] != '\n') {
 | |
| 		DECCS();
 | |
| 		if (ct > 1 && zleline[ct - 2] != '\n') {
 | |
| 		    DECPOS(ct);
 | |
| 		}
 | |
| 	    }
 | |
| 	} else {
 | |
| 	    if (zlecs != zlell && zleline[zlecs] != '\n')
 | |
| 		INCCS();
 | |
| 	}
 | |
| 	if (ct == zlell || zleline[ct] == '\n') {
 | |
| 	    DECPOS(ct);
 | |
| 	}
 | |
| 	if (ct < 1 || zleline[ct - 1] == '\n')
 | |
| 	    return 1;
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 	{
 | |
| 	    /*
 | |
| 	     * We should keep any accents etc. on their original characters.
 | |
| 	     */
 | |
| 	    int start = ct, end = ct;
 | |
| 	    DECPOS(start);
 | |
| 	    INCPOS(end);
 | |
| 
 | |
| 	    transpose_swap(start, ct, end);
 | |
| 	}
 | |
| #else
 | |
| 	{
 | |
| 	    ZLE_CHAR_T cc = zleline[ct - 1];
 | |
| 	    zleline[ct - 1] = zleline[ct];
 | |
| 	    zleline[ct] = cc;
 | |
| 	}
 | |
| #endif
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| poundinsert(UNUSED(char **args))
 | |
| {
 | |
|     zlecs = 0;
 | |
|     vifirstnonblank(zlenoargs);
 | |
|     if (zleline[zlecs] != '#') {
 | |
| 	spaceinline(1);
 | |
| 	zleline[zlecs] = '#';
 | |
| 	zlecs = findeol();
 | |
| 	while(zlecs != zlell) {
 | |
| 	    zlecs++;
 | |
| 	    vifirstnonblank(zlenoargs);
 | |
| 	    spaceinline(1);
 | |
| 	    zleline[zlecs] = '#';
 | |
| 	    zlecs = findeol();
 | |
| 	}
 | |
|     } else {
 | |
| 	foredel(1, 0);
 | |
| 	zlecs = findeol();
 | |
| 	while(zlecs != zlell) {
 | |
| 	    zlecs++;
 | |
| 	    vifirstnonblank(zlenoargs);
 | |
| 	    if(zleline[zlecs] == '#')
 | |
| 		foredel(1, 0);
 | |
| 	    zlecs = findeol();
 | |
| 	}
 | |
|     }
 | |
|     done = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| acceptline(UNUSED(char **args))
 | |
| {
 | |
|     done = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| acceptandhold(UNUSED(char **args))
 | |
| {
 | |
|     zpushnode(bufstack, zlelineasstring(zleline, zlell, 0, NULL, NULL, 0));
 | |
|     stackcs = zlecs;
 | |
|     done = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| killline(char **args)
 | |
| {
 | |
|     int i = 0, n = zmult;
 | |
| 
 | |
|     if (n < 0) {
 | |
| 	int ret;
 | |
| 	zmult = -n;
 | |
| 	ret = backwardkillline(args);
 | |
| 	zmult = n;
 | |
| 	return ret;
 | |
|     }
 | |
|     while (n--) {
 | |
| 	if (zleline[zlecs] == ZWC('\n'))
 | |
| 	    zlecs++, i++;
 | |
| 	else
 | |
| 	    while (zlecs != zlell && zleline[zlecs] != ZWC('\n'))
 | |
| 		zlecs++, i++;
 | |
|     }
 | |
|     backkill(i, CUT_RAW);
 | |
|     clearlist = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| killregion(UNUSED(char **args))
 | |
| {
 | |
|     if (mark > zlell)
 | |
| 	mark = zlell;
 | |
|     if (mark > zlecs)
 | |
| 	forekill(mark - zlecs, CUT_RAW);
 | |
|     else
 | |
| 	backkill(zlecs - mark, CUT_FRONT|CUT_RAW);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| copyregionaskill(char **args)
 | |
| {
 | |
|     if (*args) {
 | |
|         int len;
 | |
|         ZLE_STRING_T line = stringaszleline(*args, 0, &len, NULL, NULL);
 | |
| 	cuttext(line, len, CUT_REPLACE);
 | |
| 	free(line);
 | |
|     } else {
 | |
| 	if (mark > zlell)
 | |
| 	    mark = zlell;
 | |
| 	if (mark > zlecs)
 | |
| 	    cut(zlecs, mark - zlecs, 0);
 | |
| 	else
 | |
| 	    cut(mark, zlecs - mark, CUT_FRONT);
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * kct: index into kill ring, or -1 for original cutbuffer of yank.
 | |
|  * yankb, yanke: mark the start and end of last yank in editing buffer.
 | |
|  */
 | |
| static int kct, yankb, yanke;
 | |
| /* The original cutbuffer, either cutbuf or one of the vi buffers. */
 | |
| static Cutbuffer kctbuf;
 | |
| 
 | |
| /**/
 | |
| int
 | |
| yank(UNUSED(char **args))
 | |
| {
 | |
|     int n = zmult;
 | |
| 
 | |
|     if (n < 0)
 | |
| 	return 1;
 | |
|     if (zmod.flags & MOD_VIBUF)
 | |
| 	kctbuf = &vibuf[zmod.vibuf];
 | |
|     else
 | |
| 	kctbuf = &cutbuf;
 | |
|     if (!kctbuf->buf)
 | |
| 	return 1;
 | |
|     mark = zlecs;
 | |
|     yankb = zlecs;
 | |
|     while (n--) {
 | |
| 	kct = -1;
 | |
| 	spaceinline(kctbuf->len);
 | |
| 	ZS_memcpy(zleline + zlecs, kctbuf->buf, kctbuf->len);
 | |
| 	zlecs += kctbuf->len;
 | |
| 	yanke = zlecs;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| yankpop(UNUSED(char **args))
 | |
| {
 | |
|     int cc, kctstart = kct;
 | |
|     Cutbuffer buf;
 | |
| 
 | |
|     if (!(lastcmd & ZLE_YANK) || !kring || !kctbuf) {
 | |
| 	kctbuf = NULL;
 | |
| 	return 1;
 | |
|     }
 | |
|     do {
 | |
| 	/*
 | |
| 	 * This is supposed to make the yankpop loop
 | |
| 	 *   original buffer -> kill ring in order -> original buffer -> ...
 | |
| 	 * where the original buffer is -1 and the remainder are
 | |
| 	 * indices into the kill ring, remember that we need to start
 | |
| 	 * that at kringnum rather than zero.
 | |
| 	 */
 | |
| 	if (kct == -1)
 | |
| 	    kct = kringnum;
 | |
| 	else {
 | |
| 	    int kctnew = (kct + kringsize - 1) % kringsize;
 | |
| 	    if (kctnew == kringnum)
 | |
| 		kct = -1;
 | |
| 	    else
 | |
| 		kct = kctnew;
 | |
| 	}
 | |
| 	if (kct == -1)
 | |
| 	    buf = kctbuf;	/* Use original cutbuffer */
 | |
| 	else
 | |
| 	    buf = kring+kct;	/* Use somewhere in the kill ring */
 | |
| 	/* Careful to prevent infinite looping */
 | |
| 	if (kct == kctstart)
 | |
| 	    return 1;
 | |
| 	/*
 | |
| 	 * Skip unset buffers instead of stopping as we used to do.
 | |
| 	 * Also skip zero-length buffers.
 | |
| 	 * There are two reasons for this:
 | |
| 	 * 1. We now map the array $killring directly into the
 | |
| 	 *    killring, instead of via some extra size-checking logic.
 | |
| 	 *    When $killring has been set, a buffer will always have
 | |
| 	 *    at least a zero-length string in it.
 | |
| 	 * 2. The old logic was inconsistent; when the kill ring
 | |
| 	 *    was full, we could loop round and round it, otherwise
 | |
| 	 *    we just stopped when we hit the first empty buffer.
 | |
| 	 */
 | |
|     } while (!buf->buf || *buf->buf == ZWC('\0'));
 | |
| 
 | |
|     zlecs = yankb;
 | |
|     foredel(yanke - yankb, CUT_RAW);
 | |
|     cc = buf->len;
 | |
|     spaceinline(cc);
 | |
|     ZS_memcpy(zleline + zlecs, buf->buf, cc);
 | |
|     zlecs += cc;
 | |
|     yanke = zlecs;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| overwritemode(UNUSED(char **args))
 | |
| {
 | |
|     insmode ^= 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| whatcursorposition(UNUSED(char **args))
 | |
| {
 | |
|     char msg[100];
 | |
|     char *s = msg, *mbstr;
 | |
|     int bol = findbol(), len;
 | |
|     ZLE_CHAR_T c = zleline[zlecs];
 | |
| 
 | |
|     if (zlecs == zlell)
 | |
| 	strucpy(&s, "EOF");
 | |
|     else {
 | |
| 	strucpy(&s, "Char: ");
 | |
| 	switch (c) {
 | |
| 	case ZWC(' '):
 | |
| 	    strucpy(&s, "SPC");
 | |
| 	    break;
 | |
| 	case ZWC('\t'):
 | |
| 	    strucpy(&s, "TAB");
 | |
| 	    break;
 | |
| 	case ZWC('\n'):
 | |
| 	    strucpy(&s, "LFD");
 | |
| 	    break;
 | |
| 	default:
 | |
| 	    /*
 | |
| 	     * convert a single character, remembering it may
 | |
| 	     * turn into a multibyte string or be metafied.
 | |
| 	     */
 | |
| 	    mbstr = zlelineasstring(zleline+zlecs, 1, 0, &len, NULL, 1);
 | |
| 	    strcpy(s, mbstr);
 | |
| 	    s += len;
 | |
| 	}
 | |
| 	sprintf(s, " (0%o, %u, 0x%x)", (unsigned int)c,
 | |
| 		(unsigned int)c, (unsigned int)c);
 | |
| 	s += strlen(s);
 | |
|     }
 | |
|     sprintf(s, "  point %d of %d(%d%%)  column %d", zlecs+1, zlell+1,
 | |
| 	    zlell ? 100 * zlecs / zlell : 0, zlecs - bol);
 | |
|     showmsg(msg);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| undefinedkey(UNUSED(char **args))
 | |
| {
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| quotedinsert(char **args)
 | |
| {
 | |
| #ifndef HAS_TIO
 | |
|     struct sgttyb sob;
 | |
| 
 | |
|     sob = shttyinfo.sgttyb;
 | |
|     sob.sg_flags = (sob.sg_flags | RAW) & ~ECHO;
 | |
|     ioctl(SHTTY, TIOCSETN, &sob);
 | |
| #endif
 | |
|     getfullchar(0);
 | |
| #ifndef HAS_TIO
 | |
|     zsetterm();
 | |
| #endif
 | |
|     if (LASTFULLCHAR == ZLEEOF)
 | |
| 	return 1;
 | |
|     else
 | |
| 	return selfinsert(args);
 | |
| }
 | |
| 
 | |
| static int
 | |
| parsedigit(int inkey)
 | |
| {
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     /*
 | |
|      * It's too dangerous to allow metafied input.  See
 | |
|      * universalargument for comments on (possibly suboptimal) handling
 | |
|      * of digits.  We are assuming ASCII is a subset of the multibyte
 | |
|      * encoding.
 | |
|      */
 | |
| #else
 | |
|     /* allow metafied as well as ordinary digits */
 | |
|     inkey &= 0x7f;
 | |
| #endif
 | |
| 
 | |
|     /* remember inkey is not a wide character */
 | |
|     if (zmod.base > 10) {
 | |
| 	if (inkey >= 'a' && inkey < 'a' + zmod.base - 10)
 | |
| 	    return inkey - 'a' + 10;
 | |
| 	if (inkey >= 'A' && inkey < 'A' + zmod.base - 10)
 | |
| 	    return inkey - 'A' + 10;
 | |
| 	if (idigit(inkey))
 | |
| 	    return inkey - '0';
 | |
| 	return -1;
 | |
|     }
 | |
|     if (inkey >= '0' && inkey < '0' + zmod.base)
 | |
| 	return inkey - '0';
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| digitargument(UNUSED(char **args))
 | |
| {
 | |
|     int sign = (zmult < 0) ? -1 : 1;
 | |
|     int newdigit = parsedigit(lastchar);
 | |
| 
 | |
|     if (newdigit < 0)
 | |
| 	return 1;
 | |
| 
 | |
|     if (!(zmod.flags & MOD_TMULT))
 | |
| 	zmod.tmult = 0;
 | |
|     if (zmod.flags & MOD_NEG) {
 | |
| 	/* If we just had a negative argument, this is the digit, *
 | |
| 	 * rather than the -1 assumed by negargument()            */
 | |
| 	zmod.tmult = sign * newdigit;
 | |
| 	zmod.flags &= ~MOD_NEG;
 | |
|     } else
 | |
| 	zmod.tmult = zmod.tmult * zmod.base + sign * newdigit;
 | |
|     zmod.flags |= MOD_TMULT;
 | |
|     prefixflag = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| negargument(UNUSED(char **args))
 | |
| {
 | |
|     if (zmod.flags & MOD_TMULT)
 | |
| 	return 1;
 | |
|     zmod.tmult = -1;
 | |
|     zmod.flags |= MOD_TMULT|MOD_NEG;
 | |
|     prefixflag = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| universalargument(char **args)
 | |
| {
 | |
|     int digcnt = 0, pref = 0, minus = 1, gotk;
 | |
|     if (*args) {
 | |
| 	zmod.mult = atoi(*args);
 | |
| 	zmod.flags |= MOD_MULT;
 | |
| 	return 0;
 | |
|     }
 | |
|     /*
 | |
|      * TODO: this is quite tricky to do when trying to maintain
 | |
|      * compatibility between the old input system and Unicode.
 | |
|      * We don't know what follows the digits, so if we try to
 | |
|      * read wide characters we may fail (e.g. we may come across an old
 | |
|      * \M-style binding).
 | |
|      *
 | |
|      * If we assume individual bytes are either explicitly ASCII or
 | |
|      * not (a la UTF-8), we get away with it; we can back up individual
 | |
|      * bytes and everything will work.  We may want to relax this
 | |
|      * assumption later.  ("Much later" - (C) Steven Singer,
 | |
|      * CSR BlueCore firmware, ca. 2000.)
 | |
|      *
 | |
|      * Hence for now this remains byte-by-byte.
 | |
|      */
 | |
|     while ((gotk = getbyte(0L, NULL)) != EOF) {
 | |
| 	if (gotk == '-' && !digcnt) {
 | |
| 	    minus = -1;
 | |
| 	    digcnt++;
 | |
| 	} else {
 | |
| 	    int newdigit = parsedigit(gotk);
 | |
| 
 | |
| 	    if (newdigit >= 0) {
 | |
| 		pref = pref * zmod.base + newdigit;
 | |
| 		digcnt++;
 | |
| 	    } else {
 | |
| 		ungetbyte(gotk);
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     if (digcnt)
 | |
| 	zmod.tmult = minus * (pref ? pref : 1);
 | |
|     else
 | |
| 	zmod.tmult *= 4;
 | |
|     zmod.flags |= MOD_TMULT;
 | |
|     prefixflag = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Set the base for a digit argument. */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| argumentbase(char **args)
 | |
| {
 | |
|     int multbase;
 | |
| 
 | |
|     if (*args)
 | |
| 	multbase = (int)zstrtol(*args, NULL, 0);
 | |
|     else
 | |
| 	multbase = zmod.mult;
 | |
| 
 | |
|     if (multbase < 2 || multbase > ('9' - '0' + 1) + ('z' - 'a' + 1))
 | |
| 	return 1;
 | |
| 
 | |
|     zmod.base = multbase;
 | |
| 
 | |
|     /* reset modifier, apart from base... */
 | |
|     zmod.flags = 0;
 | |
|     zmod.mult = 1;
 | |
|     zmod.tmult = 1;
 | |
|     zmod.vibuf = 0;
 | |
| 
 | |
|     /* ...but indicate we are still operating on a prefix argument. */
 | |
|     prefixflag = 1;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| copyprevword(UNUSED(char **args))
 | |
| {
 | |
|     int len, t0 = zlecs, t1;
 | |
| 
 | |
|     if (zmult > 0) {
 | |
| 	int count = zmult;
 | |
| 
 | |
| 	for (;;) {
 | |
| 	    t1 = t0;
 | |
| 
 | |
| 	    while (t0) {
 | |
| 		int prev = t0;
 | |
| 		DECPOS(prev);
 | |
| 		if (ZC_iword(zleline[prev]))
 | |
| 		    break;
 | |
| 		t0 = prev;
 | |
| 	    }
 | |
| 	    while (t0) {
 | |
| 		int prev = t0;
 | |
| 		DECPOS(prev);
 | |
| 		if (!ZC_iword(zleline[prev]))
 | |
| 		    break;
 | |
| 		t0 = prev;
 | |
| 	    }
 | |
| 
 | |
| 	    if (!--count)
 | |
| 		break;
 | |
| 	    if (t0 == 0)
 | |
| 		return 1;
 | |
| 	}
 | |
|     }
 | |
|     else
 | |
| 	return 1;
 | |
|     len = t1 - t0;
 | |
|     spaceinline(len);
 | |
|     ZS_memcpy(zleline + zlecs, zleline + t0, len);
 | |
|     zlecs += len;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| copyprevshellword(UNUSED(char **args))
 | |
| {
 | |
|     LinkList l;
 | |
|     LinkNode n;
 | |
|     int i;
 | |
|     char *p = NULL;
 | |
| 
 | |
|     if (zmult <= 0)
 | |
| 	return 1;
 | |
| 
 | |
|     if ((l = bufferwords(NULL, NULL, &i, LEXFLAGS_ZLE))) {
 | |
| 	i -= (zmult-1);
 | |
| 	if (i < 0)
 | |
| 	    return 1;
 | |
|         for (n = firstnode(l); n; incnode(n))
 | |
|             if (!i--) {
 | |
|                 p = getdata(n);
 | |
|                 break;
 | |
|             }
 | |
|     }
 | |
| 
 | |
|     if (p) {
 | |
| 	int len;
 | |
| 	ZLE_STRING_T lineadd = stringaszleline(p, 0, &len, NULL, NULL);
 | |
| 
 | |
| 	spaceinline(len);
 | |
| 	ZS_memcpy(zleline + zlecs, lineadd, len);
 | |
| 	zlecs += len;
 | |
| 
 | |
| 	free(lineadd);
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| sendbreak(UNUSED(char **args))
 | |
| {
 | |
|     errflag = 1;
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| quoteregion(UNUSED(char **args))
 | |
| {
 | |
|     ZLE_STRING_T str;
 | |
|     size_t len;
 | |
| 
 | |
|     if (mark > zlell)
 | |
| 	mark = zlell;
 | |
|     if (mark < zlecs) {
 | |
| 	int tmp = mark;
 | |
| 	mark = zlecs;
 | |
| 	zlecs = tmp;
 | |
|     }
 | |
|     str = (ZLE_STRING_T)hcalloc((len = mark - zlecs) * ZLE_CHAR_SIZE);
 | |
|     ZS_memcpy(str, zleline + zlecs, len);
 | |
|     foredel(len, CUT_RAW);
 | |
|     str = makequote(str, &len);
 | |
|     spaceinline(len);
 | |
|     ZS_memcpy(zleline + zlecs, str, len);
 | |
|     mark = zlecs;
 | |
|     zlecs += len;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| quoteline(UNUSED(char **args))
 | |
| {
 | |
|     ZLE_STRING_T str;
 | |
|     size_t len = zlell;
 | |
| 
 | |
|     str = makequote(zleline, &len);
 | |
|     sizeline(len);
 | |
|     ZS_memcpy(zleline, str, len);
 | |
|     zlecs = zlell = len;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static ZLE_STRING_T
 | |
| makequote(ZLE_STRING_T str, size_t *len)
 | |
| {
 | |
|     int qtct = 0;
 | |
|     ZLE_STRING_T l, ol;
 | |
|     ZLE_STRING_T end = str + *len;
 | |
| 
 | |
|     for (l = str; l < end; l++)
 | |
| 	if (*l == ZWC('\''))
 | |
| 	    qtct++;
 | |
|     *len += 2 + qtct*3;
 | |
|     l = ol = (ZLE_STRING_T)zhalloc(*len * ZLE_CHAR_SIZE);
 | |
|     *l++ = ZWC('\'');
 | |
|     for (; str < end; str++)
 | |
| 	if (*str == ZWC('\'')) {
 | |
| 	    *l++ = ZWC('\'');
 | |
| 	    *l++ = ZWC('\\');
 | |
| 	    *l++ = ZWC('\'');
 | |
| 	    *l++ = ZWC('\'');
 | |
| 	} else
 | |
| 	    *l++ = *str;
 | |
|     *l++ = ZWC('\'');
 | |
|     return ol;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * cmdstr is the buffer used for execute-named-command converted
 | |
|  * to a metafied multibyte string.
 | |
|  */
 | |
| static char *namedcmdstr;
 | |
| static LinkList namedcmdll;
 | |
| static int namedcmdambig;
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| scancompcmd(HashNode hn, UNUSED(int flags))
 | |
| {
 | |
|     int l;
 | |
|     Thingy t = (Thingy) hn;
 | |
| 
 | |
|     if(strpfx(namedcmdstr, t->nam)) {
 | |
| 	addlinknode(namedcmdll, t->nam);
 | |
| 	l = pfxlen(peekfirst(namedcmdll), t->nam);
 | |
| 	if (l < namedcmdambig)
 | |
| 	    namedcmdambig = l;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| #define NAMLEN 60
 | |
| 
 | |
| /*
 | |
|  * Local keymap used when reading a command name for the
 | |
|  * execute-named-command and where-is widgets.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| Keymap command_keymap;
 | |
| 
 | |
| /**/
 | |
| Thingy
 | |
| executenamedcommand(char *prmt)
 | |
| {
 | |
|     Thingy cmd, retval = NULL;
 | |
|     int l, len, feep = 0, listed = 0, curlist = 0;
 | |
|     int ols = (listshown && validlist), olll = lastlistlen;
 | |
|     char *cmdbuf, *ptr;
 | |
|     char *okeymap = ztrdup(curkeymapname);
 | |
| 
 | |
|     clearlist = 1;
 | |
|     /* prmt may be constant */
 | |
|     prmt = ztrdup(prmt);
 | |
|     l = strlen(prmt);
 | |
|     cmdbuf = (char *)zhalloc(l + NAMLEN + 2
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 			     + 2 * MB_CUR_MAX
 | |
| #endif
 | |
| 			     );
 | |
|     strcpy(cmdbuf, prmt);
 | |
|     zsfree(prmt);
 | |
|     statusline = cmdbuf;
 | |
|     selectlocalmap(command_keymap);
 | |
|     selectkeymap("main", 1);
 | |
|     ptr = cmdbuf += l;
 | |
|     len = 0;
 | |
|     for (;;) {
 | |
| 	*ptr = '_';
 | |
| 	ptr[1] = '\0';
 | |
| 	zrefresh();
 | |
| 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 | |
| 	    statusline = NULL;
 | |
| 	    selectkeymap(okeymap, 1);
 | |
| 	    zsfree(okeymap);
 | |
| 	    if ((listshown = ols)) {
 | |
| 		showinglist = -2;
 | |
| 		lastlistlen = olll;
 | |
| 	    } else if (listed)
 | |
| 		clearlist = listshown = 1;
 | |
| 
 | |
| 	    retval = NULL;
 | |
| 	    goto done;
 | |
| 	}
 | |
| 	if(cmd == Th(z_clearscreen)) {
 | |
| 	    clearscreen(zlenoargs);
 | |
| 	    if (curlist) {
 | |
| 		int zmultsav = zmult;
 | |
| 
 | |
| 		zmult = 1;
 | |
| 		listlist(namedcmdll);
 | |
| 		showinglist = 0;
 | |
| 		zmult = zmultsav;
 | |
| 	    }
 | |
| 	} else if(cmd == Th(z_redisplay)) {
 | |
| 	    redisplay(zlenoargs);
 | |
| 	    if (curlist) {
 | |
| 		int zmultsav = zmult;
 | |
| 
 | |
| 		zmult = 1;
 | |
| 		listlist(namedcmdll);
 | |
| 		showinglist = 0;
 | |
| 		zmult = zmultsav;
 | |
| 	    }
 | |
| 	} else if(cmd == Th(z_viquotedinsert)) {
 | |
| 	    *ptr = '^';
 | |
| 	    zrefresh();
 | |
| 	    getfullchar(0);
 | |
| 	    if(LASTFULLCHAR == ZLEEOF || !LASTFULLCHAR || len >= NAMLEN)
 | |
| 		feep = 1;
 | |
| 	    else {
 | |
| 		int ret = zlecharasstring(LASTFULLCHAR, ptr);
 | |
| 		len += ret;
 | |
| 		ptr += ret;
 | |
| 		curlist = 0;
 | |
| 	    }
 | |
| 	} else if(cmd == Th(z_quotedinsert)) {
 | |
| 	    if(getfullchar(0) == ZLEEOF ||
 | |
| 	       !LASTFULLCHAR || len == NAMLEN)
 | |
| 		feep = 1;
 | |
| 	    else {
 | |
| 		int ret = zlecharasstring(LASTFULLCHAR, ptr);
 | |
| 		len += ret;
 | |
| 		ptr += ret;
 | |
| 		curlist = 0;
 | |
| 	    }
 | |
| 	} else if(cmd == Th(z_backwarddeletechar) ||
 | |
| 		  cmd == Th(z_vibackwarddeletechar)) {
 | |
| 	    if (len) {
 | |
| 		ptr = backwardmetafiedchar(cmdbuf, ptr, NULL);
 | |
| 		len = ptr - cmdbuf;
 | |
| 		curlist = 0;
 | |
| 	    }
 | |
| 	} else if(cmd == Th(z_killregion) || cmd == Th(z_backwardkillword) ||
 | |
| 		  cmd == Th(z_vibackwardkillword)) {
 | |
| 	    if (len)
 | |
| 		curlist = 0;
 | |
| 	    while (len) {
 | |
| 		convchar_t cc;
 | |
| 		ptr = backwardmetafiedchar(cmdbuf, ptr, &cc);
 | |
| 		len = ptr - cmdbuf;
 | |
| 		if (cc == ZWC('-'))
 | |
| 		    break;
 | |
| 	    }
 | |
| 	} else if(cmd == Th(z_killwholeline) || cmd == Th(z_vikillline) ||
 | |
| 	    	cmd == Th(z_backwardkillline)) {
 | |
| 	    len = 0;
 | |
| 	    ptr = cmdbuf;
 | |
| 	    if (listed)
 | |
| 		clearlist = listshown = 1;
 | |
| 	    curlist = 0;
 | |
| 	} else {
 | |
| 	    if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) {
 | |
| 		Thingy r;
 | |
| 		unambiguous:
 | |
| 		*ptr = 0;
 | |
| 		r = rthingy(cmdbuf);
 | |
| 		if (!(r->flags & DISABLED)) {
 | |
| 		    unrefthingy(r);
 | |
| 		    statusline = NULL;
 | |
| 		    selectkeymap(okeymap, 1);
 | |
| 		    zsfree(okeymap);
 | |
| 		    if ((listshown = ols)) {
 | |
| 			showinglist = -2;
 | |
| 			lastlistlen = olll;
 | |
| 		    } else if (listed)
 | |
| 			clearlist = listshown = 1;
 | |
| 
 | |
| 		    retval = r;
 | |
| 		    goto done;
 | |
| 		}
 | |
| 		unrefthingy(r);
 | |
| 	    }
 | |
| 	    if(cmd == Th(z_selfinsertunmeta)) {
 | |
| 		fixunmeta();
 | |
| 		cmd = Th(z_selfinsert);
 | |
| 	    }
 | |
| 	    if (cmd == Th(z_listchoices) || cmd == Th(z_deletecharorlist) ||
 | |
| 		cmd == Th(z_expandorcomplete) || cmd == Th(z_completeword) ||
 | |
| 		cmd == Th(z_expandorcompleteprefix) || cmd == Th(z_vicmdmode) ||
 | |
| 		cmd == Th(z_acceptline) || lastchar == ' ' || lastchar == '\t') {
 | |
| 		namedcmdambig = 100;
 | |
| 
 | |
| 		namedcmdll = newlinklist();
 | |
| 
 | |
| 		*ptr = '\0';
 | |
| 		namedcmdstr = cmdbuf;
 | |
| 		scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
 | |
| 		namedcmdstr = NULL;
 | |
| 
 | |
| 		if (empty(namedcmdll)) {
 | |
| 		    feep = 1;
 | |
| 		    if (listed)
 | |
| 			clearlist = listshown = 1;
 | |
| 		    curlist = 0;
 | |
| 		} else if (cmd == Th(z_listchoices) ||
 | |
| 		    cmd == Th(z_deletecharorlist)) {
 | |
| 		    int zmultsav = zmult;
 | |
| 		    *ptr = '_';
 | |
| 		    ptr[1] = '\0';
 | |
| 		    zmult = 1;
 | |
| 		    listlist(namedcmdll);
 | |
| 		    listed = curlist = 1;
 | |
| 		    showinglist = 0;
 | |
| 		    zmult = zmultsav;
 | |
| 		} else if (!nextnode(firstnode(namedcmdll))) {
 | |
| 		    strcpy(ptr = cmdbuf, peekfirst(namedcmdll));
 | |
| 		    len = strlen(ptr);
 | |
| 		    ptr += len;
 | |
| 		    if (cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode))
 | |
| 			goto unambiguous;
 | |
| 		} else {
 | |
| 		    strcpy(cmdbuf, peekfirst(namedcmdll));
 | |
| 		    ptr = cmdbuf + namedcmdambig;
 | |
| 		    *ptr = '_';
 | |
| 		    ptr[1] = '\0';
 | |
| 		    if (isset(AUTOLIST) &&
 | |
| 			!(isset(LISTAMBIGUOUS) && namedcmdambig > len)) {
 | |
| 			int zmultsav = zmult;
 | |
| 			if (isset(LISTBEEP))
 | |
| 			    feep = 1;
 | |
| 			zmult = 1;
 | |
| 			listlist(namedcmdll);
 | |
| 			listed = curlist = 1;
 | |
| 			showinglist = 0;
 | |
| 			zmult = zmultsav;
 | |
| 		    }
 | |
| 		    len = namedcmdambig;
 | |
| 		}
 | |
| 	    } else {
 | |
| 		if (len == NAMLEN || cmd != Th(z_selfinsert))
 | |
| 		    feep = 1;
 | |
| 		else {
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 		    if (!lastchar_wide_valid)
 | |
| 			getrestchar(lastchar);
 | |
| 		    if (lastchar_wide == WEOF)
 | |
| 			feep = 1;
 | |
| 		    else
 | |
| #endif
 | |
| 		    if (ZC_icntrl(LASTFULLCHAR))
 | |
| 			feep = 1;
 | |
| 		    else {
 | |
| 			int ret = zlecharasstring(LASTFULLCHAR, ptr);
 | |
| 			len += ret;
 | |
| 			ptr += ret;
 | |
| 			if (listed) {
 | |
| 			    clearlist = listshown = 1;
 | |
| 			    listed = 0;
 | |
| 			} else
 | |
| 			    curlist = 0;
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 	if (feep)
 | |
| 	    handlefeep(zlenoargs);
 | |
| 	feep = 0;
 | |
|     }
 | |
| 
 | |
|  done:
 | |
|     selectlocalmap(NULL);
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| /*****************/
 | |
| /* Suffix system */
 | |
| /*****************/
 | |
| 
 | |
| /*
 | |
|  * The completion system sometimes tentatively adds a suffix to a word,
 | |
|  * which can be removed depending on what is inserted next.  These
 | |
|  * functions provide the capability to handle a removable suffix.
 | |
|  *
 | |
|  * Any removable suffix consists of characters immediately before the
 | |
|  * cursor.  Whether it is removed depends on the next editing action.
 | |
|  * There can be more than one suffix simultaneously present, with
 | |
|  * different actions deleting different numbers of characters.
 | |
|  *
 | |
|  * If the next editing action changes the buffer other than by inserting
 | |
|  * characters, normally the suffix should be removed so as to leave a
 | |
|  * meaningful complete word.  The behaviour should be the same if the
 | |
|  * next character inserted is a word separator.  If the next character
 | |
|  * reasonably belongs where it is typed, or if the next editing action
 | |
|  * is a deletion, the suffix should not be removed.  Other reasons for
 | |
|  * suffix removal may have other behaviour.
 | |
|  *
 | |
|  * In order to maintain a consistent state, after a suffix has been added
 | |
|  * the table *must* be zeroed, one way or another, before the buffer is
 | |
|  * changed.  If the suffix is not being removed, call fixsuffix() to
 | |
|  * indicate that it is being permanently fixed.
 | |
|  */
 | |
| 
 | |
| struct suffixset;
 | |
| 
 | |
| /* An element of a suffix specification */
 | |
| struct suffixset {
 | |
|     struct suffixset *next;	/* Next in the list */
 | |
|     int tp;			/* The SUFTYP_* from enum suffixtype */
 | |
|     int flags;			/* Some of SUFFLAGS_* */
 | |
|     ZLE_STRING_T chars;		/* Set of characters to match (or not) */
 | |
|     int lenstr;			/* Length of chars */
 | |
|     int lensuf;			/* Length of suffix */
 | |
| };
 | |
| 
 | |
| /* The list of suffix structures */
 | |
| struct suffixset *suffixlist;
 | |
| 
 | |
| /* Shell function to call to remove the suffix. */
 | |
| 
 | |
| /**/
 | |
| static char *suffixfunc;
 | |
| 
 | |
| /* Length associated with the suffix function */
 | |
| static int suffixfunclen;
 | |
| 
 | |
| /* Length associated with uninsertable characters */
 | |
| /**/
 | |
| mod_export int
 | |
| suffixnoinslen;
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| addsuffix(int tp, int flags, ZLE_STRING_T chars, int lenstr, int lensuf)
 | |
| {
 | |
|     struct suffixset *newsuf = zalloc(sizeof(struct suffixset));
 | |
|     newsuf->next = suffixlist;
 | |
|     suffixlist = newsuf;
 | |
| 
 | |
|     newsuf->tp = tp;
 | |
|     newsuf->flags = flags;
 | |
|     if (lenstr) {
 | |
| 	newsuf->chars = zalloc(lenstr*sizeof(ZLE_CHAR_T));
 | |
| 	ZS_memcpy(newsuf->chars, chars, lenstr);
 | |
|     } else
 | |
| 	newsuf->chars = NULL;
 | |
|     newsuf->lenstr = lenstr;
 | |
|     newsuf->lensuf = lensuf;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Same as addsuffix, but from metafied string */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| addsuffixstring(int tp, int flags, char *chars, int lensuf)
 | |
| {
 | |
|     int slen, alloclen;
 | |
|     ZLE_STRING_T suffixstr;
 | |
| 
 | |
|     /* string needs to be writable... I've been regretting this for years.. */
 | |
|     chars = ztrdup(chars);
 | |
|     suffixstr = stringaszleline(chars, 0, &slen, &alloclen, NULL);
 | |
|     addsuffix(tp, flags, suffixstr, slen, lensuf);
 | |
|     zfree(suffixstr, alloclen);
 | |
|     zsfree(chars);
 | |
| }
 | |
| 
 | |
| /* Set up suffix: the last n characters are a suffix that should be *
 | |
|  * removed in the usual word end conditions.                        */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| makesuffix(int n)
 | |
| {
 | |
|     char *suffixchars;
 | |
| 
 | |
|     if (!(suffixchars = getsparam("ZLE_REMOVE_SUFFIX_CHARS")))
 | |
| 	suffixchars = " \t\n;&|";
 | |
| 
 | |
|     addsuffixstring(SUFTYP_POSSTR, 0, suffixchars, n);
 | |
| 
 | |
|     /* Do this second so it takes precedence */
 | |
|     if ((suffixchars = getsparam("ZLE_SPACE_SUFFIX_CHARS")) && *suffixchars)
 | |
| 	addsuffixstring(SUFTYP_POSSTR, SUFFLAGS_SPACE, suffixchars, n);
 | |
| 
 | |
|     suffixnoinslen = n;
 | |
| }
 | |
| 
 | |
| /* Set up suffix for parameter names: the last n characters are a suffix *
 | |
|  * that should be removed if the next character is one of the ones that  *
 | |
|  * needs to go immediately after the parameter name.  br indicates that  *
 | |
|  * the name is in braces (${PATH} instead of $PATH), so the extra        *
 | |
|  * characters that can only be used in braces are included.              */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| makeparamsuffix(int br, int n)
 | |
| {
 | |
|     ZLE_STRING_T charstr = ZWS(":[#%?-+=");
 | |
|     int lenstr = 0;
 | |
| 
 | |
|     if (br || unset(KSHARRAYS)) {
 | |
| 	lenstr = 2;
 | |
| 	if (br)
 | |
| 	    lenstr += 6;
 | |
|     }
 | |
|     if (lenstr)
 | |
| 	addsuffix(SUFTYP_POSSTR, 0, charstr, lenstr, n);
 | |
| }
 | |
| 
 | |
| /* Set up suffix given a string containing the characters on which to   *
 | |
|  * remove the suffix. */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| makesuffixstr(char *f, char *s, int n)
 | |
| {
 | |
|     if (f) {
 | |
| 	zsfree(suffixfunc);
 | |
| 	suffixfunc = ztrdup(f);
 | |
| 	suffixfunclen = n;
 | |
|     } else if (s) {
 | |
| 	int inv, i, z = 0;
 | |
| 	ZLE_STRING_T ws, lasts, wptr;
 | |
| 
 | |
| 	if (*s == '^' || *s == '!') {
 | |
| 	    inv = 1;
 | |
| 	    s++;
 | |
| 	} else
 | |
| 	    inv = 0;
 | |
| 	s = getkeystring(s, &i, GETKEYS_SUFFIX, &z);
 | |
| 	s = metafy(s, i, META_USEHEAP);
 | |
| 	ws = stringaszleline(s, 0, &i, NULL, NULL);
 | |
| 
 | |
| 	if (z)
 | |
| 	    suffixnoinslen = inv ? 0 : n;
 | |
| 	else if (inv) {
 | |
| 	    /*
 | |
| 	     * negative match, \- wasn't present, so it *should*
 | |
| 	     * have this suffix length
 | |
| 	     */
 | |
| 	    suffixnoinslen = n;
 | |
| 	}
 | |
| 
 | |
| 	lasts = wptr = ws;
 | |
| 	while (i) {
 | |
| 	    if (i >= 3 && wptr[1] == ZWC('-')) {
 | |
| 		ZLE_CHAR_T str[2];
 | |
| 
 | |
| 		if (wptr > lasts)
 | |
| 		    addsuffix(inv ? SUFTYP_NEGSTR : SUFTYP_POSSTR, 0,
 | |
| 			      lasts, wptr - lasts, n);
 | |
| 		str[0] = *wptr;
 | |
| 		str[1] = wptr[2];
 | |
| 		addsuffix(inv ? SUFTYP_NEGRNG : SUFTYP_POSRNG, 0,
 | |
| 			  str, 2, n);
 | |
| 
 | |
| 		wptr += 3;
 | |
| 		i -= 3;
 | |
| 		lasts = wptr;
 | |
| 	    } else {
 | |
| 		wptr++;
 | |
| 		i--;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (wptr > lasts)
 | |
| 	    addsuffix(inv ? SUFTYP_NEGSTR : SUFTYP_POSSTR, 0,
 | |
| 		      lasts, wptr - lasts, n);
 | |
| 	free(ws);
 | |
|     } else
 | |
| 	makesuffix(n);
 | |
| }
 | |
| 
 | |
| /* Remove suffix, if there is one, when inserting character c. */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| iremovesuffix(ZLE_INT_T c, int keep)
 | |
| {
 | |
|     if (suffixfunc) {
 | |
| 	Shfunc shfunc = getshfunc(suffixfunc);
 | |
| 
 | |
| 	if (shfunc) {
 | |
| 	    LinkList args = newlinklist();
 | |
| 	    char buf[20];
 | |
| 	    int osc = sfcontext;
 | |
| 	    int wasmeta = (zlemetaline != 0);
 | |
| 
 | |
| 	    if (wasmeta) {
 | |
| 		/*
 | |
| 		 * The suffix removal function runs as a normal
 | |
| 		 * ZLE function, not a completion function, so
 | |
| 		 * the line should be unmetafied if this was
 | |
| 		 * called from completion.  (It may not be since
 | |
| 		 * we may decide to remove the suffix later.)
 | |
| 		 */
 | |
| 		unmetafy_line();
 | |
| 	    }
 | |
| 
 | |
| 	    sprintf(buf, "%d", suffixfunclen);
 | |
| 	    addlinknode(args, suffixfunc);
 | |
| 	    addlinknode(args, buf);
 | |
| 
 | |
| 	    startparamscope();
 | |
| 	    makezleparams(0);
 | |
| 	    sfcontext = SFC_COMPLETE;
 | |
| 	    doshfunc(shfunc, args, 1);
 | |
| 	    sfcontext = osc;
 | |
| 	    endparamscope();
 | |
| 
 | |
| 	    if (wasmeta)
 | |
| 		metafy_line();
 | |
| 	}
 | |
| 	zsfree(suffixfunc);
 | |
| 	suffixfunc = NULL;
 | |
|     } else {
 | |
| 	int sl = 0, flags = 0;
 | |
| 	struct suffixset *ss;
 | |
| 
 | |
| 	if (c == NO_INSERT_CHAR) {
 | |
| 	    sl = suffixnoinslen;
 | |
| 	} else {
 | |
| 	    ZLE_CHAR_T ch = c;
 | |
| 	    /*
 | |
| 	     * Search for a match for ch in the suffix list.
 | |
| 	     * We stop if we encounter a match in a positive or negative
 | |
| 	     * list, using the suffix length specified or zero respectively.
 | |
| 	     * If we reached the end and passed through a negative
 | |
| 	     * list, we use the suffix length for that, else zero.
 | |
| 	     * This would break if it were possible to have negative
 | |
| 	     * sets with different suffix length:  that's not supposed
 | |
| 	     * to happen.
 | |
| 	     */
 | |
| 	    int negsuflen = 0, found = 0;
 | |
| 
 | |
| 	    for (ss = suffixlist; ss; ss = ss->next) {
 | |
| 		switch (ss->tp) {
 | |
| 		case SUFTYP_POSSTR:
 | |
| 		    if (ZS_memchr(ss->chars, ch, ss->lenstr)) {
 | |
| 			sl = ss->lensuf;
 | |
| 			found = 1;
 | |
| 		    }
 | |
| 		    break;
 | |
| 
 | |
| 		case SUFTYP_NEGSTR:
 | |
| 		    if (ZS_memchr(ss->chars, ch, ss->lenstr)) {
 | |
| 			sl = 0;
 | |
| 			found = 1;
 | |
| 		    } else {
 | |
| 			negsuflen = ss->lensuf;
 | |
| 		    }
 | |
| 		    break;
 | |
| 
 | |
| 		case SUFTYP_POSRNG:
 | |
| 		    if (ss->chars[0] <= ch && ch <= ss->chars[1]) {
 | |
| 			sl = ss->lensuf;
 | |
| 			found = 1;
 | |
| 		    }
 | |
| 		    break;
 | |
| 
 | |
| 		case SUFTYP_NEGRNG:
 | |
| 		    if (ss->chars[0] <= ch && ch <= ss->chars[1]) {
 | |
| 			sl = 0;
 | |
| 			found = 1;
 | |
| 		    } else {
 | |
| 			negsuflen = ss->lensuf;
 | |
| 		    }
 | |
| 		    break;
 | |
| 		}
 | |
| 		if (found) {
 | |
| 		    flags = ss->flags;
 | |
| 		    break;
 | |
| 		}
 | |
| 	    }
 | |
| 
 | |
| 	    if (!found)
 | |
| 		sl = negsuflen;
 | |
| 	}
 | |
| 	if (sl) {
 | |
| 	    /* must be shifting wide character lengths */
 | |
| 	    backdel(sl, CUT_RAW);
 | |
| 	    if (flags & SUFFLAGS_SPACE)
 | |
| 	    {
 | |
| 		/* Add a space and advance over it */
 | |
| 		spaceinline(1);
 | |
| 		if (zlemetaline) {
 | |
| 		    zlemetaline[zlemetacs++] = ' ';
 | |
| 		} else {
 | |
| 		    zleline[zlecs++] = ZWC(' ');
 | |
| 		}
 | |
| 	    }
 | |
| 	    if (!keep)
 | |
| 		invalidatelist();
 | |
| 	}
 | |
|     }
 | |
|     fixsuffix();
 | |
| }
 | |
| 
 | |
| /* Fix the suffix in place, if there is one, making it non-removable. */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| fixsuffix(void)
 | |
| {
 | |
|     while (suffixlist) {
 | |
| 	struct suffixset *next = suffixlist->next;
 | |
| 
 | |
| 	if (suffixlist->lenstr)
 | |
| 	    zfree(suffixlist->chars, suffixlist->lenstr * sizeof(ZLE_CHAR_T));
 | |
| 	zfree(suffixlist, sizeof(struct suffixset));
 | |
| 
 | |
| 	suffixlist = next;
 | |
|     }
 | |
| 
 | |
|     suffixfunclen = suffixnoinslen = 0;
 | |
| }
 |