mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 06:00:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			766 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			766 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * prompt.c - construct zsh prompts
 | |
|  *
 | |
|  * 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 "zsh.mdh"
 | |
| #include "prompt.pro"
 | |
| 
 | |
| /* text attribute mask */
 | |
|  
 | |
| /**/
 | |
| unsigned txtattrmask;
 | |
| 
 | |
| /* text change - attribute change made by prompts */
 | |
| 
 | |
| /**/
 | |
| unsigned txtchange;
 | |
| 
 | |
| /* the command stack for use with %_ in prompts */
 | |
|  
 | |
| /**/
 | |
| unsigned char *cmdstack;
 | |
| /**/
 | |
| int cmdsp;
 | |
| 
 | |
| /* parser states, for %_ */
 | |
| 
 | |
| static char *cmdnames[] = {
 | |
|     "for",      "while",     "repeat",    "select",
 | |
|     "until",    "if",        "then",      "else",
 | |
|     "elif",     "math",      "cond",      "cmdor",
 | |
|     "cmdand",   "pipe",      "errpipe",   "foreach",
 | |
|     "case",     "function",  "subsh",     "cursh",
 | |
|     "array",    "quote",     "dquote",    "bquote",
 | |
|     "cmdsubst", "mathsubst", "elif-then", "heredoc",
 | |
|     "heredocd", "brace",     "braceparam",
 | |
| };
 | |
|  
 | |
| /* The buffer into which an expanded and metafied prompt is being written, *
 | |
|  * and its size.                                                           */
 | |
| 
 | |
| static char *buf;
 | |
| static int bufspc;
 | |
| 
 | |
| /* bp is the pointer to the current position in the buffer, where the next *
 | |
|  * character will be added.                                                */
 | |
| 
 | |
| static char *bp;
 | |
| 
 | |
| /* bp1 is an auxilliary pointer into the buffer, which when non-NULL is *
 | |
|  * moved whenever the buffer is reallocated.  It is used when data is   *
 | |
|  * being temporarily held in the buffer.                                */
 | |
| 
 | |
| static char *bp1;
 | |
| 
 | |
| /* The format string, for %-expansion. */
 | |
| 
 | |
| static char *fm;
 | |
| 
 | |
| /* Current truncation string (metafied), the length at which truncation *
 | |
|  * occurs, and the direction in which it occurs.                        */
 | |
| 
 | |
| static char *truncstr;
 | |
| static int trunclen, truncatleft;
 | |
| 
 | |
| /* Current level of nesting of %{ / %} sequences. */
 | |
| 
 | |
| static int dontcount;
 | |
| 
 | |
| /* Strings to use for %r and %R (for the spelling prompt). */
 | |
| 
 | |
| static char *rstring, *Rstring;
 | |
| 
 | |
| /* If non-zero, Inpar, Outpar and Nularg can be added to the buffer. */
 | |
| 
 | |
| static int nonsp;
 | |
| 
 | |
| /* Perform prompt expansion on a string, putting the result in a *
 | |
|  * permanently-allocated string.  If ns is non-zero, this string *
 | |
|  * may have embedded Inpar and Outpar, which indicate a toggling *
 | |
|  * between spacing and non-spacing parts of the prompt, and      *
 | |
|  * Nularg, which (in a non-spacing sequence) indicates a         *
 | |
|  * `glitch' space.                                               */
 | |
| 
 | |
| /**/
 | |
| char *
 | |
| promptexpand(char *s, int ns, char *rs, char *Rs)
 | |
| {
 | |
|     if(!s)
 | |
| 	return ztrdup("");
 | |
| 
 | |
|     if ((termflags & TERM_UNKNOWN) && (unset(INTERACTIVE)))
 | |
|         init_term();
 | |
| 
 | |
|     if (isset(PROMPTSUBST)) {
 | |
| 	int olderr = errflag;
 | |
| 
 | |
| 	HEAPALLOC {
 | |
| 	    s = dupstring(s);
 | |
| 	    if (!parsestr(s))
 | |
| 		singsub(&s);
 | |
| 	} LASTALLOC;
 | |
| 	/* Ignore errors in prompt substitution */
 | |
| 	errflag = olderr;
 | |
|     }
 | |
| 
 | |
|     rstring = rs;
 | |
|     Rstring = Rs;
 | |
|     nonsp = ns;
 | |
|     fm = s;
 | |
|     bp = buf = zalloc(bufspc = 256);
 | |
|     bp1 = NULL;
 | |
|     trunclen = 0;
 | |
|     putpromptchar(1, '\0');
 | |
|     addbufspc(1);
 | |
|     if(dontcount)
 | |
| 	*bp++ = Outpar;
 | |
|     *bp = 0;
 | |
|     return buf;
 | |
| }
 | |
| 
 | |
| /* Perform %- and !-expansion as required on a section of the prompt.  The *
 | |
|  * section is ended by an instance of endchar.  If doprint is 0, the valid *
 | |
|  * % sequences are merely skipped over, and nothing is stored.             */
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| putpromptchar(int doprint, int endchar)
 | |
| {
 | |
|     char *ss, *tmbuf = NULL;
 | |
|     int t0, arg, test, sep;
 | |
|     struct tm *tm;
 | |
|     time_t timet;
 | |
|     Nameddir nd;
 | |
| 
 | |
|     for (; *fm && *fm != endchar; fm++) {
 | |
| 	arg = 0;
 | |
| 	if (*fm == '%' && isset(PROMPTPERCENT)) {
 | |
| 	    if (idigit(*++fm)) {
 | |
| 		arg = zstrtol(fm, &fm, 10);
 | |
| 	    }
 | |
| 	    if (*fm == '(') {
 | |
| 		int tc;
 | |
| 
 | |
| 		if (idigit(*++fm)) {
 | |
| 		    arg = zstrtol(fm, &fm, 10);
 | |
| 		}
 | |
| 		test = 0;
 | |
| 		ss = pwd;
 | |
| 		switch (tc = *fm) {
 | |
| 		case 'c':
 | |
| 		case '.':
 | |
| 		case '~':
 | |
| 		    if ((nd = finddir(ss))) {
 | |
| 			arg--;
 | |
| 			ss += strlen(nd->dir);
 | |
| 		    }
 | |
| 		case '/':
 | |
| 		case 'C':
 | |
| 		    for (; *ss; ss++)
 | |
| 			if (*ss == '/')
 | |
| 			    arg--;
 | |
| 		    if (arg <= 0)
 | |
| 			test = 1;
 | |
| 		    break;
 | |
| 		case 't':
 | |
| 		case 'T':
 | |
| 		case 'd':
 | |
| 		case 'D':
 | |
| 		case 'w':
 | |
| 		    timet = time(NULL);
 | |
| 		    tm = localtime(&timet);
 | |
| 		    switch (tc) {
 | |
| 		    case 't':
 | |
| 			test = (arg == tm->tm_min);
 | |
| 			break;
 | |
| 		    case 'T':
 | |
| 			test = (arg == tm->tm_hour);
 | |
| 			break;
 | |
| 		    case 'd':
 | |
| 			test = (arg == tm->tm_mday);
 | |
| 			break;
 | |
| 		    case 'D':
 | |
| 			test = (arg == tm->tm_mon);
 | |
| 			break;
 | |
| 		    case 'w':
 | |
| 			test = (arg == tm->tm_wday);
 | |
| 			break;
 | |
| 		    }
 | |
| 		    break;
 | |
| 		case '?':
 | |
| 		    if (lastval == arg)
 | |
| 			test = 1;
 | |
| 		    break;
 | |
| 		case '#':
 | |
| 		    if (geteuid() == arg)
 | |
| 			test = 1;
 | |
| 		    break;
 | |
| 		case 'g':
 | |
| 		    if (getegid() == arg)
 | |
| 			test = 1;
 | |
| 		    break;
 | |
| 		case 'L':
 | |
| 		    if (shlvl >= arg)
 | |
| 			test = 1;
 | |
| 		    break;
 | |
| 		case 'S':
 | |
| 		    if (time(NULL) - shtimer.tv_sec >= arg)
 | |
| 			test = 1;
 | |
| 		    break;
 | |
| 		case 'v':
 | |
| 		    if (arrlen(psvar) >= arg)
 | |
| 			test = 1;
 | |
| 		    break;
 | |
| 		case '_':
 | |
| 		    test = (cmdsp >= arg);
 | |
| 		    break;
 | |
| 		case '!':
 | |
| 		    test = privasserted();
 | |
| 		    break;
 | |
| 		default:
 | |
| 		    test = -1;
 | |
| 		    break;
 | |
| 		}
 | |
| 		if (!*fm || !(sep = *++fm))
 | |
| 		    return 0;
 | |
| 		fm++;
 | |
| 		if (!putpromptchar(test == 1 && doprint, sep) || !*++fm ||
 | |
| 		    !putpromptchar(test == 0 && doprint, ')')) {
 | |
| 		    return 0;
 | |
| 		}
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    if (!doprint)
 | |
| 		switch(*fm) {
 | |
| 		  case '[':
 | |
| 		    while(idigit(*++fm));
 | |
| 		    while(*++fm != ']');
 | |
| 		    continue;
 | |
| 		  case '<':
 | |
| 		    while(*++fm != '<');
 | |
| 		    continue;
 | |
| 		  case '>':
 | |
| 		    while(*++fm != '>');
 | |
| 		    continue;
 | |
| 		  case 'D':
 | |
| 		    if(fm[1]=='{')
 | |
| 			while(*++fm != '}');
 | |
| 		    continue;
 | |
| 		  default:
 | |
| 		    continue;
 | |
| 		}
 | |
| 	    switch (*fm) {
 | |
| 	    case '~':
 | |
| 		if ((nd = finddir(pwd))) {
 | |
| 		    char *t = tricat("~", nd->nam, pwd + strlen(nd->dir));
 | |
| 		    stradd(t);
 | |
| 		    zsfree(t);
 | |
| 		    break;
 | |
| 		}
 | |
| 	    case 'd':
 | |
| 	    case '/':
 | |
| 		stradd(pwd);
 | |
| 		break;
 | |
| 	    case 'c':
 | |
| 	    case '.':
 | |
| 	        {
 | |
| 		    char *t;
 | |
| 
 | |
| 		    if ((nd = finddir(pwd)))
 | |
| 			t = tricat("~", nd->nam, pwd + strlen(nd->dir));
 | |
| 		    else
 | |
| 			t = ztrdup(pwd);
 | |
| 		    if (!arg)
 | |
| 			arg++;
 | |
| 		    for (ss = t + strlen(t); ss > t; ss--)
 | |
| 			if (*ss == '/' && !--arg) {
 | |
| 			    ss++;
 | |
| 			    break;
 | |
| 			}
 | |
| 		    if(*ss == '/' && ss[1] && ss != t)
 | |
| 			ss++;
 | |
| 		    stradd(ss);
 | |
| 		    zsfree(t);
 | |
| 		    break;
 | |
| 		}
 | |
| 	    case 'C':
 | |
| 		if (!arg)
 | |
| 		    arg++;
 | |
| 		for (ss = pwd + strlen(pwd); ss > pwd; ss--)
 | |
| 		    if (*ss == '/' && !--arg) {
 | |
| 			ss++;
 | |
| 			break;
 | |
| 		    }
 | |
| 		if (*ss == '/' && ss[1] && (ss != pwd))
 | |
| 		    ss++;
 | |
| 		stradd(ss);
 | |
| 		break;
 | |
| 	    case 'h':
 | |
| 	    case '!':
 | |
| 		addbufspc(DIGBUFSIZE);
 | |
| 		sprintf(bp, "%d", curhist);
 | |
| 		bp += strlen(bp);
 | |
| 		break;
 | |
| 	    case 'M':
 | |
| 		stradd(hostnam);
 | |
| 		break;
 | |
| 	    case 'm':
 | |
| 		if (!arg)
 | |
| 		    arg++;
 | |
| 		for (ss = hostnam; *ss; ss++)
 | |
| 		    if (*ss == '.' && !--arg)
 | |
| 			break;
 | |
| 		t0 = *ss;
 | |
| 		*ss = '\0';
 | |
| 		stradd(hostnam);
 | |
| 		*ss = t0;
 | |
| 		break;
 | |
| 	    case 'S':
 | |
| 		txtchangeset(TXTSTANDOUT, TXTNOSTANDOUT);
 | |
| 		txtset(TXTSTANDOUT);
 | |
| 		tsetcap(TCSTANDOUTBEG, 1);
 | |
| 		break;
 | |
| 	    case 's':
 | |
| 		txtchangeset(TXTNOSTANDOUT, TXTSTANDOUT);
 | |
| 		txtset(TXTDIRTY);
 | |
| 		txtunset(TXTSTANDOUT);
 | |
| 		tsetcap(TCSTANDOUTEND, 1);
 | |
| 		break;
 | |
| 	    case 'B':
 | |
| 		txtchangeset(TXTBOLDFACE, TXTNOBOLDFACE);
 | |
| 		txtset(TXTDIRTY);
 | |
| 		txtset(TXTBOLDFACE);
 | |
| 		tsetcap(TCBOLDFACEBEG, 1);
 | |
| 		break;
 | |
| 	    case 'b':
 | |
| 		txtchangeset(TXTNOBOLDFACE, TXTBOLDFACE);
 | |
| 		txtchangeset(TXTNOSTANDOUT, TXTSTANDOUT);
 | |
| 		txtchangeset(TXTNOUNDERLINE, TXTUNDERLINE);
 | |
| 		txtset(TXTDIRTY);
 | |
| 		txtunset(TXTBOLDFACE);
 | |
| 		tsetcap(TCALLATTRSOFF, 1);
 | |
| 		break;
 | |
| 	    case 'U':
 | |
| 		txtchangeset(TXTUNDERLINE, TXTNOUNDERLINE);
 | |
| 		txtset(TXTUNDERLINE);
 | |
| 		tsetcap(TCUNDERLINEBEG, 1);
 | |
| 		break;
 | |
| 	    case 'u':
 | |
| 		txtchangeset(TXTNOUNDERLINE, TXTUNDERLINE);
 | |
| 		txtset(TXTDIRTY);
 | |
| 		txtunset(TXTUNDERLINE);
 | |
| 		tsetcap(TCUNDERLINEEND, 1);
 | |
| 		break;
 | |
| 	    case '[':
 | |
|                 if (idigit(*++fm))
 | |
|                     trunclen = zstrtol(fm, &fm, 10);
 | |
|                 else
 | |
|                     trunclen = arg;
 | |
|                 if (trunclen) {
 | |
| 		    truncatleft = *fm && *fm != ']' && *fm++ == '<';
 | |
| 		    bp1 = bp;
 | |
| 		    while (*fm && *fm != ']') {
 | |
| 			if (*fm == '\\' && fm[1])
 | |
| 			    ++fm;
 | |
| 			addbufspc(1);
 | |
| 			*bp++ = *fm++;
 | |
| 		    }
 | |
| 		    addbufspc(2);
 | |
| 		    if (bp1 == bp)
 | |
| 			*bp++ = '<';
 | |
|                     *bp = '\0';
 | |
| 		    zsfree(truncstr);
 | |
|                     truncstr = ztrdup(bp = bp1);
 | |
| 		    bp1 = NULL;
 | |
|                 } else {
 | |
| 		    while (*fm && *fm != ']') {
 | |
| 			if (*fm == '\\' && fm[1])
 | |
| 			    fm++;
 | |
| 			fm++;
 | |
| 		    }
 | |
| 		}
 | |
| 		if(!*fm)
 | |
| 		    return 0;
 | |
| 		break;
 | |
| 	    case '<':
 | |
| 	    case '>':
 | |
| 		if((trunclen = arg)) {
 | |
| 		    char ch = *fm++;
 | |
| 		    truncatleft = ch == '<';
 | |
| 		    bp1 = bp;
 | |
| 		    while (*fm && *fm != ch) {
 | |
| 			if (*fm == '\\' && fm[1])
 | |
| 			    ++fm;
 | |
| 			addbufspc(1);
 | |
| 			*bp++ = *fm++;
 | |
| 		    }
 | |
| 		    addbufspc(1);
 | |
|                     *bp = '\0';
 | |
| 		    zsfree(truncstr);
 | |
|                     truncstr = ztrdup(bp = bp1);
 | |
| 		    bp1 = NULL;
 | |
| 		} else {
 | |
| 		    char ch = *fm++;
 | |
| 		    while(*fm && *fm != ch) {
 | |
| 			if (*fm == '\\' && fm[1])
 | |
| 			    fm++;
 | |
| 			fm++;
 | |
| 		    }
 | |
| 		}
 | |
| 		if(!*fm)
 | |
| 		    return 0;
 | |
| 		break;
 | |
| 	    case '{': /*}*/
 | |
| 		if (!dontcount++ && nonsp) {
 | |
| 		    addbufspc(1);
 | |
| 		    *bp++ = Inpar;
 | |
| 		}
 | |
| 		break;
 | |
| 	    case /*{*/ '}':
 | |
| 		if (dontcount && !--dontcount && nonsp) {
 | |
| 		    addbufspc(1);
 | |
| 		    *bp++ = Outpar;
 | |
| 		}
 | |
| 		break;
 | |
| 	    case 't':
 | |
| 	    case '@':
 | |
| 	    case 'T':
 | |
| 	    case '*':
 | |
| 	    case 'w':
 | |
| 	    case 'W':
 | |
| 	    case 'D':
 | |
| 		{
 | |
| 		    char *tmfmt, *dd;
 | |
| 
 | |
| 		    switch (*fm) {
 | |
| 		    case 'T':
 | |
| 			tmfmt = "%K:%M";
 | |
| 			break;
 | |
| 		    case '*':
 | |
| 			tmfmt = "%K:%M:%S";
 | |
| 			break;
 | |
| 		    case 'w':
 | |
| 			tmfmt = "%a %f";
 | |
| 			break;
 | |
| 		    case 'W':
 | |
| 			tmfmt = "%m/%d/%y";
 | |
| 			break;
 | |
| 		    case 'D':
 | |
| 			if (fm[1] == '{') /*}*/ {
 | |
| 			    for (ss = fm + 2; *ss && *ss != /*{*/ '}'; ss++)
 | |
| 				if(*ss == '\\' && ss[1])
 | |
| 				    ss++;
 | |
| 			    dd = tmfmt = tmbuf = zalloc(ss - fm);
 | |
| 			    for (ss = fm + 2; *ss && *ss != /*{*/ '}'; ss++) {
 | |
| 				if(*ss == '\\' && ss[1])
 | |
| 				    ss++;
 | |
| 				*dd++ = *ss;
 | |
| 			    }
 | |
| 			    *dd = 0;
 | |
| 			    fm = ss - !*ss;
 | |
| 			} else
 | |
| 			    tmfmt = "%y-%m-%d";
 | |
| 			break;
 | |
| 		    default:
 | |
| 			tmfmt = "%l:%M%p";
 | |
| 			break;
 | |
| 		    }
 | |
| 		    timet = time(NULL);
 | |
| 		    tm = localtime(&timet);
 | |
| 		    for(t0=80; ; t0*=2) {
 | |
| 			addbufspc(t0);
 | |
| 			if(ztrftime(bp, t0, tmfmt, tm) != t0)
 | |
| 			    break;
 | |
| 		    }
 | |
| 		    bp += strlen(bp);
 | |
| 		    free(tmbuf);
 | |
| 		    tmbuf = NULL;
 | |
| 		    break;
 | |
| 		}
 | |
| 	    case 'n':
 | |
| 		stradd(get_username());
 | |
| 		break;
 | |
| 	    case 'l':
 | |
| 		if (*ttystrname) {
 | |
| 		    ss = (strncmp(ttystrname, "/dev/tty", 8) ?
 | |
| 			    ttystrname + 5 : ttystrname + 8);
 | |
| 		    stradd(ss);
 | |
| 		} else
 | |
| 		    stradd("()");
 | |
| 		break;
 | |
| 	    case 'L':
 | |
| 		addbufspc(DIGBUFSIZE);
 | |
| 		sprintf(bp, "%ld", (long)shlvl);
 | |
| 		bp += strlen(bp);
 | |
| 		break;
 | |
| 	    case '?':
 | |
| 		addbufspc(DIGBUFSIZE);
 | |
| 		sprintf(bp, "%ld", (long)lastval);
 | |
| 		bp += strlen(bp);
 | |
| 		break;
 | |
| 	    case '%':
 | |
| 	    case ')':
 | |
| 		addbufspc(1);
 | |
| 		*bp++ = *fm;
 | |
| 		break;
 | |
| 	    case '#':
 | |
| 		addbufspc(1);
 | |
| 		*bp++ = privasserted() ? '#' : '%';
 | |
| 		break;
 | |
| 	    case 'v':
 | |
| 		if (!arg)
 | |
| 		    arg = 1;
 | |
| 		if (arrlen(psvar) >= arg)
 | |
| 		    stradd(psvar[arg - 1]);
 | |
| 		break;
 | |
| 	    case 'E':
 | |
|                 tsetcap(TCCLEAREOL, 1);
 | |
| 		break;
 | |
| 	    case '_':
 | |
| 		if (cmdsp) {
 | |
| 		    if (arg > cmdsp || arg <= 0)
 | |
| 			arg = cmdsp;
 | |
| 		    for (t0 = cmdsp - arg; arg--; t0++) {
 | |
| 			stradd(cmdnames[cmdstack[t0]]);
 | |
| 			if (arg) {
 | |
| 			    addbufspc(1);
 | |
| 			    *bp++=' ';
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 		break;
 | |
| 	    case 'r':
 | |
| 		if(rstring)
 | |
| 		    stradd(rstring);
 | |
| 		break;
 | |
| 	    case 'R':
 | |
| 		if(Rstring)
 | |
| 		    stradd(Rstring);
 | |
| 		break;
 | |
| 	    case '\0':
 | |
| 		return 0;
 | |
| 	    case Meta:
 | |
| 		fm++;
 | |
| 		break;
 | |
| 	    }
 | |
| 	} else if(*fm == '!' && isset(PROMPTBANG)) {
 | |
| 	    if(doprint)
 | |
| 		if(fm[1] == '!') {
 | |
| 		    fm++;
 | |
| 		    addbufspc(1);
 | |
| 		    pputc('!');
 | |
| 		} else {
 | |
| 		    addbufspc(DIGBUFSIZE);
 | |
| 		    sprintf(bp, "%d", curhist);
 | |
| 		    bp += strlen(bp);
 | |
| 		}
 | |
| 	} else {
 | |
| 	    char c = *fm == Meta ? *++fm ^ 32 : *fm;
 | |
| 
 | |
| 	    if (doprint) {
 | |
| 		addbufspc(1);
 | |
| 		pputc(c);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return *fm;
 | |
| }
 | |
| 
 | |
| /* pputc adds a character to the buffer, metafying.  There must *
 | |
|  * already be space.                                            */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| pputc(char c)
 | |
| {
 | |
|     if(imeta(STOUC(c))) {
 | |
| 	*bp++ = Meta;
 | |
| 	c ^= 32;
 | |
|     }
 | |
|     *bp++ = c;
 | |
| }
 | |
| 
 | |
| /* Make sure there is room for `need' more characters in the buffer. */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| addbufspc(int need)
 | |
| {
 | |
|     need *= 2;   /* for metafication */
 | |
|     if((bp - buf) + need > bufspc) {
 | |
| 	int bo = bp - buf;
 | |
| 	int bo1 = bp1 ? bp1 - buf : -1;
 | |
| 
 | |
| 	if(need & 255)
 | |
| 	    need = (need | 255) + 1;
 | |
| 	buf = realloc(buf, bufspc += need);
 | |
| 	bp = buf + bo;
 | |
| 	if(bo1 != -1)
 | |
| 	    bp1 = buf + bo1;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* stradd() adds a metafied string to the prompt, *
 | |
|  * in a visible representation, doing truncation. */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| stradd(char *d)
 | |
| {
 | |
|     /* dlen is the full length of the string we want to add */
 | |
|     int dlen = niceztrlen(d);
 | |
|     char *ps, *pd, *pc, *t;
 | |
|     int tlen, maxlen;
 | |
|     addbufspc(dlen);
 | |
|     /* This loop puts the nice representation of the string into the prompt *
 | |
|      * buffer.  It might be modified later.  Note that bp isn't changed.    */
 | |
|     for(ps=d, pd=bp; *ps; ps++)
 | |
| 	for(pc=nicechar(*ps == Meta ? STOUC(*++ps)^32 : STOUC(*ps)); *pc; pc++)
 | |
| 	    *pd++ = *pc;
 | |
|     if(!trunclen || dlen <= trunclen) {
 | |
| 	/* No truncation is needed, so update bp and return, *
 | |
| 	 * leaving the full string in the prompt.            */
 | |
| 	bp += dlen;
 | |
| 	return;
 | |
|     }
 | |
|     /* We need to truncate.  t points to the truncation string -- which is *
 | |
|      * inserted literally, without nice representation.  tlen is its       *
 | |
|      * length, and maxlen is the amout of the main string that we want to  *
 | |
|      * keep.  Note that if the truncation string is longer than the        *
 | |
|      * truncation length (tlen > trunclen), the truncation string is used  *
 | |
|      * in full.                                                            */
 | |
|     addbufspc(tlen = ztrlen(t = truncstr));
 | |
|     maxlen = tlen < trunclen ? trunclen - tlen : 0;
 | |
|     if(truncatleft) {
 | |
| 	memmove(bp + strlen(t), bp + dlen - maxlen, maxlen);
 | |
| 	while(*t)
 | |
| 	    *bp++ = *t++;
 | |
| 	bp += maxlen;
 | |
|     } else {
 | |
| 	bp += maxlen;
 | |
| 	while(*t)
 | |
| 	    *bp++ = *t++;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* tsetcap(), among other things, can write a termcap string into the buffer. */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| tsetcap(int cap, int flag)
 | |
| {
 | |
|     if (!(termflags & TERM_SHORT) && tcstr[cap]) {
 | |
| 	switch(flag) {
 | |
| 	case -1:
 | |
| 	    tputs(tcstr[cap], 1, putraw);
 | |
| 	    break;
 | |
| 	case 0:
 | |
| 	    tputs(tcstr[cap], 1, putshout);
 | |
| 	    break;
 | |
| 	case 1:
 | |
| 	    if (!dontcount && nonsp) {
 | |
| 		addbufspc(1);
 | |
| 		*bp++ = Inpar;
 | |
| 	    }
 | |
| 	    tputs(tcstr[cap], 1, putstr);
 | |
| 	    if (!dontcount && nonsp) {
 | |
| 		int glitch = 0;
 | |
| 
 | |
| 		if (cap == TCSTANDOUTBEG || cap == TCSTANDOUTEND)
 | |
| 		    glitch = tgetnum("sg");
 | |
| 		else if (cap == TCUNDERLINEBEG || cap == TCUNDERLINEEND)
 | |
| 		    glitch = tgetnum("ug");
 | |
| 		if(glitch < 0)
 | |
| 		    glitch = 0;
 | |
| 		addbufspc(glitch + 1);
 | |
| 		while(glitch--)
 | |
| 		    *bp++ = Nularg;
 | |
| 		*bp++ = Outpar;
 | |
| 	    }
 | |
| 	    break;
 | |
| 	}
 | |
| 
 | |
| 	if (txtisset(TXTDIRTY)) {
 | |
| 	    txtunset(TXTDIRTY);
 | |
| 	    if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG)
 | |
| 		tsetcap(TCBOLDFACEBEG, flag);
 | |
| 	    if (txtisset(TXTSTANDOUT))
 | |
| 		tsetcap(TCSTANDOUTBEG, flag);
 | |
| 	    if (txtisset(TXTUNDERLINE))
 | |
| 		tsetcap(TCUNDERLINEBEG, flag);
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| putstr(int d)
 | |
| {
 | |
|     addbufspc(1);
 | |
|     pputc(d);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Count height etc. of a prompt string returned by promptexpand(). *
 | |
|  * This depends on the current terminal width, and tabs and         *
 | |
|  * newlines require nontrivial processing.                          */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| countprompt(char *str, int *wp, int *hp)
 | |
| {
 | |
|     int w = 0, h = 1;
 | |
|     int s = 1;
 | |
|     for(; *str; str++) {
 | |
| 	if(*str == Meta)
 | |
| 	    str++;
 | |
| 	if(*str == Inpar)
 | |
| 	    s = 0;
 | |
| 	else if(*str == Outpar)
 | |
| 	    s = 1;
 | |
| 	else if(*str == Nularg)
 | |
| 	    w++;
 | |
| 	else if(s) {
 | |
| 	    if(*str == '\t')
 | |
| 		w = (w | 7) + 1;
 | |
| 	    else if(*str == '\n')
 | |
| 		w = columns;
 | |
| 	    else
 | |
| 		w++;
 | |
| 	}
 | |
| 	if(w >= columns) {
 | |
| 	    w = 0;
 | |
| 	    h++;
 | |
| 	}
 | |
|     }
 | |
|     if(wp)
 | |
| 	*wp = w;
 | |
|     if(hp)
 | |
| 	*hp = h;
 | |
| }
 |