openenvutils/commandshell/shell/src/prompt.c
changeset 0 2e3d3ce01487
child 1 0fdb7f6b0309
equal deleted inserted replaced
-1:000000000000 0:2e3d3ce01487
       
     1 // prompt.c - construct zsh prompts
       
     2 //
       
     3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved.
       
     4 //
       
     5 /*
       
     6  * This file is part of zsh, the Z shell.
       
     7  *
       
     8  * Copyright (c) 1992-1997 Paul Falstad
       
     9  * All rights reserved.
       
    10  *
       
    11  * Permission is hereby granted, without written agreement and without
       
    12  * license or royalty fees, to use, copy, modify, and distribute this
       
    13  * software and to distribute modified versions of this software for any
       
    14  * purpose, provided that the above copyright notice and the following
       
    15  * two paragraphs appear in all copies of this software.
       
    16  *
       
    17  * In no event shall Paul Falstad or the Zsh Development Group be liable
       
    18  * to any party for direct, indirect, special, incidental, or consequential
       
    19  * damages arising out of the use of this software and its documentation,
       
    20  * even if Paul Falstad and the Zsh Development Group have been advised of
       
    21  * the possibility of such damage.
       
    22  *
       
    23  * Paul Falstad and the Zsh Development Group specifically disclaim any
       
    24  * warranties, including, but not limited to, the implied warranties of
       
    25  * merchantability and fitness for a particular purpose.  The software
       
    26  * provided hereunder is on an "as is" basis, and Paul Falstad and the
       
    27  * Zsh Development Group have no obligation to provide maintenance,
       
    28  * support, updates, enhancements, or modifications.
       
    29  *
       
    30  */
       
    31 
       
    32 #include "zsh.mdh"
       
    33 #include "prompt.pro"
       
    34 
       
    35 #ifdef __SYMBIAN32__
       
    36 #ifdef __WINSCW__
       
    37 #pragma warn_possunwant off
       
    38 #endif//__WINSCW__
       
    39 #endif//__SYMBIAN32__
       
    40 
       
    41 /* text attribute mask */
       
    42  
       
    43 /**/
       
    44 unsigned txtattrmask;
       
    45 
       
    46 /* text change - attribute change made by prompts */
       
    47 
       
    48 /**/
       
    49 mod_export unsigned txtchange;
       
    50 
       
    51 /* the command stack for use with %_ in prompts */
       
    52  
       
    53 /**/
       
    54 unsigned char *cmdstack;
       
    55 /**/
       
    56 int cmdsp;
       
    57 
       
    58 /* parser states, for %_ */
       
    59 
       
    60 static char *cmdnames[CS_COUNT] = {
       
    61     "for",      "while",     "repeat",    "select",
       
    62     "until",    "if",        "then",      "else",
       
    63     "elif",     "math",      "cond",      "cmdor",
       
    64     "cmdand",   "pipe",      "errpipe",   "foreach",
       
    65     "case",     "function",  "subsh",     "cursh",
       
    66     "array",    "quote",     "dquote",    "bquote",
       
    67     "cmdsubst", "mathsubst", "elif-then", "heredoc",
       
    68     "heredocd", "brace",     "braceparam", "always",
       
    69 };
       
    70  
       
    71 /* The buffer into which an expanded and metafied prompt is being written, *
       
    72  * and its size.                                                           */
       
    73 
       
    74 static char *buf;
       
    75 static int bufspc;
       
    76 
       
    77 /* bp is the pointer to the current position in the buffer, where the next *
       
    78  * character will be added.                                                */
       
    79 
       
    80 static char *bp;
       
    81 
       
    82 /* Position of the start of the current line in the buffer */
       
    83 
       
    84 static char *bufline;
       
    85 
       
    86 /* bp1 is an auxiliary pointer into the buffer, which when non-NULL is *
       
    87  * moved whenever the buffer is reallocated.  It is used when data is   *
       
    88  * being temporarily held in the buffer.                                */
       
    89 
       
    90 static char *bp1;
       
    91 
       
    92 /* The format string, for %-expansion. */
       
    93 
       
    94 static char *fm;
       
    95 
       
    96 /* Non-zero if truncating the current segment of the buffer. */
       
    97 
       
    98 static int trunclen;
       
    99 
       
   100 /* Current level of nesting of %{ / %} sequences. */
       
   101 
       
   102 static int dontcount;
       
   103 
       
   104 /* Level of %{ / %} surrounding a truncation segment. */
       
   105 
       
   106 static int trunccount;
       
   107 
       
   108 /* Strings to use for %r and %R (for the spelling prompt). */
       
   109 
       
   110 static char *rstring, *Rstring;
       
   111 
       
   112 /*
       
   113  * Expand path p; maximum is npath segments where 0 means the whole path.
       
   114  * If tilde is 1, try and find a named directory to use.
       
   115  */
       
   116 
       
   117 static void
       
   118 promptpath(char *p, int npath, int tilde)
       
   119 {
       
   120     char *modp = p;
       
   121     Nameddir nd;
       
   122 
       
   123     if (tilde && ((nd = finddir(p))))
       
   124 	modp = tricat("~", nd->nam, p + strlen(nd->dir));
       
   125 
       
   126     if (npath) {
       
   127 	char *sptr;
       
   128 	if (npath > 0) {
       
   129 	    for (sptr = modp + strlen(modp); sptr > modp; sptr--) {
       
   130 		if (*sptr == '/' && !--npath) {
       
   131 		    sptr++;
       
   132 		    break;
       
   133 		}
       
   134 	    }
       
   135 	    if (*sptr == '/' && sptr[1] && sptr != modp)
       
   136 		sptr++;
       
   137 	    stradd(sptr);
       
   138 	} else {
       
   139 	    char cbu;
       
   140 	    for (sptr = modp+1; *sptr; sptr++)
       
   141 		if (*sptr == '/' && !++npath)
       
   142 		    break;
       
   143 	    cbu = *sptr;
       
   144 	    *sptr = 0;
       
   145 	    stradd(modp);
       
   146 	    *sptr = cbu;
       
   147 	}
       
   148     } else
       
   149 	stradd(modp);
       
   150 
       
   151     if (p != modp)
       
   152 	zsfree(modp);
       
   153 }
       
   154 
       
   155 /* Perform prompt expansion on a string, putting the result in a *
       
   156  * permanently-allocated string.  If ns is non-zero, this string *
       
   157  * may have embedded Inpar and Outpar, which indicate a toggling *
       
   158  * between spacing and non-spacing parts of the prompt, and      *
       
   159  * Nularg, which (in a non-spacing sequence) indicates a         *
       
   160  * `glitch' space.                                               */
       
   161 
       
   162 /**/
       
   163 mod_export char *
       
   164 promptexpand(char *s, int ns, char *rs, char *Rs)
       
   165 {
       
   166     if(!s)
       
   167 	return ztrdup("");
       
   168 
       
   169     if ((termflags & TERM_UNKNOWN) && (unset(INTERACTIVE)))
       
   170         init_term();
       
   171 
       
   172     if (isset(PROMPTSUBST)) {
       
   173 	int olderr = errflag;
       
   174 	int oldval = lastval;
       
   175 
       
   176 	s = dupstring(s);
       
   177 	if (!parsestr(s))
       
   178 	    singsub(&s);
       
   179 
       
   180 	/* Ignore errors and status change in prompt substitution */
       
   181 	errflag = olderr;
       
   182 	lastval = oldval;
       
   183     }
       
   184 
       
   185     rstring = rs;
       
   186     Rstring = Rs;
       
   187     fm = s;
       
   188     bp = bufline = buf = zshcalloc(bufspc = 256);
       
   189     bp1 = NULL;
       
   190     trunclen = 0;
       
   191     putpromptchar(1, '\0');
       
   192     addbufspc(1);
       
   193     if(dontcount)
       
   194 	*bp++ = Outpar;
       
   195     *bp = 0;
       
   196     if (!ns) {
       
   197 	/* If zero, Inpar, Outpar and Nularg should be removed. */
       
   198 	for (bp = buf; *bp; ) {
       
   199 	    if (*bp == Meta)
       
   200 		bp += 2;
       
   201 	    else if (*bp == Inpar || *bp == Outpar || *bp == Nularg)
       
   202 		chuck(bp);
       
   203 	    else
       
   204 		bp++;
       
   205 	}
       
   206     }
       
   207     return buf;
       
   208 }
       
   209 
       
   210 /* Perform %- and !-expansion as required on a section of the prompt.  The *
       
   211  * section is ended by an instance of endchar.  If doprint is 0, the valid *
       
   212  * % sequences are merely skipped over, and nothing is stored.             */
       
   213 
       
   214 /**/
       
   215 static int
       
   216 putpromptchar(int doprint, int endchar)
       
   217 {
       
   218     char *ss, *tmbuf = NULL, *hostnam;
       
   219     int t0, arg, test, sep, j, numjobs;
       
   220     struct tm *tm;
       
   221     time_t timet;
       
   222     Nameddir nd;
       
   223 
       
   224     for (; *fm && *fm != endchar; fm++) {
       
   225 	arg = 0;
       
   226 	if (*fm == '%' && isset(PROMPTPERCENT)) {
       
   227 	    int minus = 0;
       
   228 	    fm++;
       
   229 	    if (*fm == '-') {
       
   230 		minus = 1;
       
   231 		fm++;
       
   232 	    }
       
   233 	    if (idigit(*fm)) {
       
   234 		arg = zstrtol(fm, &fm, 10);
       
   235 		if (minus)
       
   236 		    arg *= -1;
       
   237 	    } else if (minus)
       
   238 		arg = -1;
       
   239 	    if (*fm == '(') {
       
   240 		int tc, otrunclen;
       
   241 
       
   242 		if (idigit(*++fm)) {
       
   243 		    arg = zstrtol(fm, &fm, 10);
       
   244 		} else if (arg < 0) {
       
   245 		    /* negative numbers don't make sense here */
       
   246 		    arg *= -1;
       
   247 		}
       
   248 		test = 0;
       
   249 		ss = pwd;
       
   250 		switch (tc = *fm) {
       
   251 		case 'c':
       
   252 		case '.':
       
   253 		case '~':
       
   254 		    if ((nd = finddir(ss))) {
       
   255 			arg--;
       
   256 			ss += strlen(nd->dir);
       
   257 		    } /*FALLTHROUGH*/
       
   258 		case '/':
       
   259 		case 'C':
       
   260 		    /* `/' gives 0, `/any' gives 1, etc. */
       
   261 		    if (*ss++ == '/' && *ss)
       
   262 			arg--;
       
   263 		    for (; *ss; ss++)
       
   264 			if (*ss == '/')
       
   265 			    arg--;
       
   266 		    if (arg <= 0)
       
   267 			test = 1;
       
   268 		    break;
       
   269 		case 't':
       
   270 		case 'T':
       
   271 		case 'd':
       
   272 		case 'D':
       
   273 		case 'w':
       
   274 		    timet = time(NULL);
       
   275 		    tm = localtime(&timet);
       
   276 		    switch (tc) {
       
   277 		    case 't':
       
   278 			test = (arg == tm->tm_min);
       
   279 			break;
       
   280 		    case 'T':
       
   281 			test = (arg == tm->tm_hour);
       
   282 			break;
       
   283 		    case 'd':
       
   284 			test = (arg == tm->tm_mday);
       
   285 			break;
       
   286 		    case 'D':
       
   287 			test = (arg == tm->tm_mon);
       
   288 			break;
       
   289 		    case 'w':
       
   290 			test = (arg == tm->tm_wday);
       
   291 			break;
       
   292 		    }
       
   293 		    break;
       
   294 		case '?':
       
   295 		    if (lastval == arg)
       
   296 			test = 1;
       
   297 		    break;
       
   298 		case '#':
       
   299 		    if (geteuid() == (uid_t)arg)
       
   300 			test = 1;
       
   301 		    break;
       
   302 		case 'g':
       
   303 		    if (getegid() == (gid_t)arg)
       
   304 			test = 1;
       
   305 		    break;
       
   306 		case 'j':
       
   307 		    for (numjobs = 0, j = 1; j <= maxjob; j++)
       
   308 			if (jobtab[j].stat && jobtab[j].procs &&
       
   309 		    	    !(jobtab[j].stat & STAT_NOPRINT)) numjobs++;
       
   310 		    if (numjobs >= arg)
       
   311 		    	test = 1;
       
   312 		    break;
       
   313 		case 'l':
       
   314 		    *bp = '\0';
       
   315 		    countprompt(bufline, &t0, 0, 0);
       
   316 		    if (t0 >= arg)
       
   317 			test = 1;
       
   318 		    break;
       
   319 		case 'L':
       
   320 		    if (shlvl >= arg)
       
   321 			test = 1;
       
   322 		    break;
       
   323 		case 'S':
       
   324 		    if (time(NULL) - shtimer.tv_sec >= arg)
       
   325 			test = 1;
       
   326 		    break;
       
   327 		case 'v':
       
   328 		    if (arrlen(psvar) >= arg)
       
   329 			test = 1;
       
   330 		    break;
       
   331 		case '_':
       
   332 		    test = (cmdsp >= arg);
       
   333 		    break;
       
   334 		case '!':
       
   335 		    test = privasserted();
       
   336 		    break;
       
   337 		default:
       
   338 		    test = -1;
       
   339 		    break;
       
   340 		}
       
   341 		if (!*fm || !(sep = *++fm))
       
   342 		    return 0;
       
   343 		fm++;
       
   344 		/* Don't do the current truncation until we get back */
       
   345 		otrunclen = trunclen;
       
   346 		trunclen = 0;
       
   347 		if (!putpromptchar(test == 1 && doprint, sep) || !*++fm ||
       
   348 		    !putpromptchar(test == 0 && doprint, ')')) {
       
   349 		    trunclen = otrunclen;
       
   350 		    return 0;
       
   351 		}
       
   352 		trunclen = otrunclen;
       
   353 		continue;
       
   354 	    }
       
   355 	    if (!doprint)
       
   356 		switch(*fm) {
       
   357 		  case '[':
       
   358 		    while(idigit(*++fm));
       
   359 		    while(*++fm != ']');
       
   360 		    continue;
       
   361 		  case '<':
       
   362 		    while(*++fm != '<');
       
   363 		    continue;
       
   364 		  case '>':
       
   365 		    while(*++fm != '>');
       
   366 		    continue;
       
   367 		  case 'D':
       
   368 		    if(fm[1]=='{')
       
   369 			while(*++fm != '}');
       
   370 		    continue;
       
   371 		  default:
       
   372 		    continue;
       
   373 		}
       
   374 	    switch (*fm) {
       
   375 	    case '~':
       
   376 		promptpath(pwd, arg, 1);
       
   377 		break;
       
   378 	    case 'd':
       
   379 	    case '/':
       
   380 		promptpath(pwd, arg, 0);
       
   381 		break;
       
   382 	    case 'c':
       
   383 	    case '.':
       
   384 		promptpath(pwd, arg ? arg : 1, 1);
       
   385 		break;
       
   386 	    case 'C':
       
   387 		promptpath(pwd, arg ? arg : 1, 0);
       
   388 		break;
       
   389 	    case 'N':
       
   390 		promptpath(scriptname ? scriptname : argzero, arg, 0);
       
   391 		break;
       
   392 	    case 'h':
       
   393 	    case '!':
       
   394 		addbufspc(DIGBUFSIZE);
       
   395 		convbase(bp, curhist, 10);
       
   396 		bp += strlen(bp);
       
   397 		break;
       
   398 	    case 'j':
       
   399 		for (numjobs = 0, j = 1; j <= maxjob; j++)
       
   400 		    if (jobtab[j].stat && jobtab[j].procs &&
       
   401 		    	!(jobtab[j].stat & STAT_NOPRINT)) numjobs++;
       
   402 		addbufspc(DIGBUFSIZE);
       
   403 		sprintf(bp, "%d", numjobs);
       
   404 		bp += strlen(bp);
       
   405 		break;
       
   406 	    case 'M':
       
   407 		queue_signals();
       
   408 		if ((hostnam = getsparam("HOST")))
       
   409 		    stradd(hostnam);
       
   410 		unqueue_signals();
       
   411 		break;
       
   412 	    case 'm':
       
   413 		if (!arg)
       
   414 		    arg++;
       
   415 		queue_signals();
       
   416 		if (!(hostnam = getsparam("HOST")))
       
   417 		    break;
       
   418 		if (arg < 0) {
       
   419 		    for (ss = hostnam + strlen(hostnam); ss > hostnam; ss--)
       
   420 			if (ss[-1] == '.' && !++arg)
       
   421 			    break;
       
   422 		    stradd(ss);
       
   423 		} else {
       
   424 		    for (ss = hostnam; *ss; ss++)
       
   425 			if (*ss == '.' && !--arg)
       
   426 			    break;
       
   427 		    stradd(*ss ? dupstrpfx(hostnam, ss - hostnam) : hostnam);
       
   428 		}
       
   429 		unqueue_signals();
       
   430 		break;
       
   431 	    case 'S':
       
   432 		txtchangeset(TXTSTANDOUT, TXTNOSTANDOUT);
       
   433 		txtset(TXTSTANDOUT);
       
   434 		tsetcap(TCSTANDOUTBEG, 1);
       
   435 		break;
       
   436 	    case 's':
       
   437 		txtchangeset(TXTNOSTANDOUT, TXTSTANDOUT);
       
   438 		txtset(TXTDIRTY);
       
   439 		txtunset(TXTSTANDOUT);
       
   440 		tsetcap(TCSTANDOUTEND, 1);
       
   441 		break;
       
   442 	    case 'B':
       
   443 		txtchangeset(TXTBOLDFACE, TXTNOBOLDFACE);
       
   444 		txtset(TXTDIRTY);
       
   445 		txtset(TXTBOLDFACE);
       
   446 		tsetcap(TCBOLDFACEBEG, 1);
       
   447 		break;
       
   448 	    case 'b':
       
   449 		txtchangeset(TXTNOBOLDFACE, TXTBOLDFACE);
       
   450 		txtchangeset(TXTNOSTANDOUT, TXTSTANDOUT);
       
   451 		txtchangeset(TXTNOUNDERLINE, TXTUNDERLINE);
       
   452 		txtset(TXTDIRTY);
       
   453 		txtunset(TXTBOLDFACE);
       
   454 		tsetcap(TCALLATTRSOFF, 1);
       
   455 		break;
       
   456 	    case 'U':
       
   457 		txtchangeset(TXTUNDERLINE, TXTNOUNDERLINE);
       
   458 		txtset(TXTUNDERLINE);
       
   459 		tsetcap(TCUNDERLINEBEG, 1);
       
   460 		break;
       
   461 	    case 'u':
       
   462 		txtchangeset(TXTNOUNDERLINE, TXTUNDERLINE);
       
   463 		txtset(TXTDIRTY);
       
   464 		txtunset(TXTUNDERLINE);
       
   465 		tsetcap(TCUNDERLINEEND, 1);
       
   466 		break;
       
   467 	    case '[':
       
   468 		if (idigit(*++fm))
       
   469 		    arg = zstrtol(fm, &fm, 10);
       
   470 		if (!prompttrunc(arg, ']', doprint, endchar))
       
   471 		    return *fm;
       
   472 		break;
       
   473 	    case '<':
       
   474 	    case '>':
       
   475 		if (!prompttrunc(arg, *fm, doprint, endchar))
       
   476 		    return *fm;
       
   477 		break;
       
   478 	    case '{': /*}*/
       
   479 		if (!dontcount++) {
       
   480 		    addbufspc(1);
       
   481 		    *bp++ = Inpar;
       
   482 		}
       
   483 		break;
       
   484 	    case /*{*/ '}':
       
   485 		if (trunccount && trunccount >= dontcount)
       
   486 		    return *fm;
       
   487 		if (dontcount && !--dontcount) {
       
   488 		    addbufspc(1);
       
   489 		    *bp++ = Outpar;
       
   490 		}
       
   491 		break;
       
   492 	    case 't':
       
   493 	    case '@':
       
   494 	    case 'T':
       
   495 	    case '*':
       
   496 	    case 'w':
       
   497 	    case 'W':
       
   498 	    case 'D':
       
   499 		{
       
   500 		    char *tmfmt, *dd;
       
   501 
       
   502 		    switch (*fm) {
       
   503 		    case 'T':
       
   504 			tmfmt = "%K:%M";
       
   505 			break;
       
   506 		    case '*':
       
   507 			tmfmt = "%K:%M:%S";
       
   508 			break;
       
   509 		    case 'w':
       
   510 			tmfmt = "%a %f";
       
   511 			break;
       
   512 		    case 'W':
       
   513 			tmfmt = "%m/%d/%y";
       
   514 			break;
       
   515 		    case 'D':
       
   516 			if (fm[1] == '{' /*}*/) {
       
   517 			    for (ss = fm + 2; *ss && *ss != /*{*/ '}'; ss++)
       
   518 				if(*ss == '\\' && ss[1])
       
   519 				    ss++;
       
   520 			    dd = tmfmt = tmbuf = zalloc(ss - fm);
       
   521 			    for (ss = fm + 2; *ss && *ss != /*{*/ '}';
       
   522 				 ss++) {
       
   523 				if(*ss == '\\' && ss[1])
       
   524 				    ss++;
       
   525 				*dd++ = *ss;
       
   526 			    }
       
   527 			    *dd = 0;
       
   528 			    fm = ss - !*ss;
       
   529 			    if (!*tmfmt) {
       
   530 				free(tmbuf);
       
   531 				continue;
       
   532 			    }
       
   533 			} else
       
   534 			    tmfmt = "%y-%m-%d";
       
   535 			break;
       
   536 		    default:
       
   537 			tmfmt = "%l:%M%p";
       
   538 			break;
       
   539 		    }
       
   540 		    timet = time(NULL);
       
   541 		    tm = localtime(&timet);
       
   542 		    /*
       
   543 		     * Hack because strftime won't say how
       
   544 		     * much space it actually needs.  Try to add it
       
   545 		     * a few times until it works.  Some formats don't
       
   546 		     * actually have a length, so we could go on for
       
   547 		     * ever.
       
   548 		     */
       
   549 		    for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) {
       
   550 			addbufspc(t0);
       
   551 			if (ztrftime(bp, t0, tmfmt, tm) >= 0)
       
   552 			    break;
       
   553 		    }
       
   554 		    bp += strlen(bp);
       
   555 		    free(tmbuf);
       
   556 		    tmbuf = NULL;
       
   557 		    break;
       
   558 		}
       
   559 	    case 'n':
       
   560 		stradd(get_username());
       
   561 		break;
       
   562 	    case 'l':
       
   563 		if (*ttystrname) {
       
   564                    ss = (strncmp(ttystrname, "/dev/tty", 8) ?
       
   565                            ttystrname + 5 : ttystrname + 8);
       
   566 		    stradd(ss);
       
   567 		} else
       
   568 		    stradd("()");
       
   569 		break;
       
   570 	    case 'y':
       
   571 		if (*ttystrname) {
       
   572 		    ss = (strncmp(ttystrname, "/dev/", 5) ?
       
   573 			    ttystrname : ttystrname + 5);
       
   574 		    stradd(ss);
       
   575 		} else
       
   576 		    stradd("()");
       
   577 		break;
       
   578 	    case 'L':
       
   579 		addbufspc(DIGBUFSIZE);
       
   580 		sprintf(bp, "%ld", (long)shlvl);
       
   581 		bp += strlen(bp);
       
   582 		break;
       
   583 	    case '?':
       
   584 		addbufspc(DIGBUFSIZE);
       
   585 		sprintf(bp, "%ld", (long)lastval);
       
   586 		bp += strlen(bp);
       
   587 		break;
       
   588 	    case '%':
       
   589 	    case ')':
       
   590 		addbufspc(1);
       
   591 		*bp++ = *fm;
       
   592 		break;
       
   593 	    case '#':
       
   594 		addbufspc(1);
       
   595 		*bp++ = privasserted() ? '#' : '%';
       
   596 		break;
       
   597 	    case 'v':
       
   598 		if (!arg)
       
   599 		    arg = 1;
       
   600 		else if (arg < 0)
       
   601 		    arg += arrlen(psvar) + 1;
       
   602 		if (arg > 0 && arrlen(psvar) >= arg)
       
   603 		    stradd(psvar[arg - 1]);
       
   604 		break;
       
   605 	    case 'E':
       
   606                 tsetcap(TCCLEAREOL, 1);
       
   607 		break;
       
   608 	    case '^':
       
   609 		if (cmdsp) {
       
   610 		    if (arg >= 0) {
       
   611 			if (arg > cmdsp || arg == 0)
       
   612 			    arg = cmdsp;
       
   613 			for (t0 = cmdsp - 1; arg--; t0--) {
       
   614 			    stradd(cmdnames[cmdstack[t0]]);
       
   615 			    if (arg) {
       
   616 				addbufspc(1);
       
   617 				*bp++=' ';
       
   618 			    }
       
   619 			}
       
   620 		    } else {
       
   621 			arg = -arg;
       
   622 			if (arg > cmdsp)
       
   623 			    arg = cmdsp;
       
   624 			for (t0 = arg - 1; arg--; t0--) {
       
   625 			    stradd(cmdnames[cmdstack[t0]]);
       
   626 			    if (arg) {
       
   627 				addbufspc(1);
       
   628 				*bp++=' ';
       
   629 			    }
       
   630 			}
       
   631 		    }
       
   632 		}
       
   633 		break;
       
   634 	    case '_':
       
   635 		if (cmdsp) {
       
   636 		    if (arg >= 0) {
       
   637 			if (arg > cmdsp || arg == 0)
       
   638 			    arg = cmdsp;
       
   639 			for (t0 = cmdsp - arg; arg--; t0++) {
       
   640 			    stradd(cmdnames[cmdstack[t0]]);
       
   641 			    if (arg) {
       
   642 				addbufspc(1);
       
   643 				*bp++=' ';
       
   644 			    }
       
   645 			}
       
   646 		    } else {
       
   647 			arg = -arg;
       
   648 			if (arg > cmdsp)
       
   649 			    arg = cmdsp;
       
   650 			for (t0 = 0; arg--; t0++) {
       
   651 			    stradd(cmdnames[cmdstack[t0]]);
       
   652 			    if (arg) {
       
   653 				addbufspc(1);
       
   654 				*bp++=' ';
       
   655 			    }
       
   656 			}
       
   657 		    }
       
   658 		}
       
   659 		break;
       
   660 	    case 'r':
       
   661 		if(rstring)
       
   662 		    stradd(rstring);
       
   663 		break;
       
   664 	    case 'R':
       
   665 		if(Rstring)
       
   666 		    stradd(Rstring);
       
   667 		break;
       
   668 	    case 'i':
       
   669 		addbufspc(DIGBUFSIZE);
       
   670 		sprintf(bp, "%ld", (long)lineno);
       
   671 		bp += strlen(bp);
       
   672 		break;
       
   673 	    case '\0':
       
   674 		return 0;
       
   675 	    case Meta:
       
   676 		fm++;
       
   677 		break;
       
   678 	    }
       
   679 	} else if(*fm == '!' && isset(PROMPTBANG)) {
       
   680 	    if(doprint) {
       
   681 		if(fm[1] == '!') {
       
   682 		    fm++;
       
   683 		    addbufspc(1);
       
   684 		    pputc('!');
       
   685 		} else {
       
   686 		    addbufspc(DIGBUFSIZE);
       
   687 		    convbase(bp, curhist, 10);
       
   688 		    bp += strlen(bp);
       
   689 		}
       
   690 	    }
       
   691 	} else {
       
   692 	    char c = *fm == Meta ? *++fm ^ 32 : *fm;
       
   693 
       
   694 	    if (doprint) {
       
   695 		addbufspc(1);
       
   696 		pputc(c);
       
   697 	    }
       
   698 	}
       
   699     }
       
   700 
       
   701     return *fm;
       
   702 }
       
   703 
       
   704 /* pputc adds a character to the buffer, metafying.  There must *
       
   705  * already be space.                                            */
       
   706 
       
   707 /**/
       
   708 static void
       
   709 pputc(char c)
       
   710 {
       
   711     if(imeta(STOUC(c))) {
       
   712 	*bp++ = Meta;
       
   713 	c ^= 32;
       
   714     }
       
   715     *bp++ = c;
       
   716     if (c == '\n' && !dontcount)
       
   717 	bufline = bp;
       
   718 }
       
   719 
       
   720 /* Make sure there is room for `need' more characters in the buffer. */
       
   721 
       
   722 /**/
       
   723 static void
       
   724 addbufspc(int need)
       
   725 {
       
   726     need *= 2;   /* for metafication */
       
   727     if((bp - buf) + need > bufspc) {
       
   728 	int bo = bp - buf;
       
   729 	int bo1 = bp1 ? bp1 - buf : -1;
       
   730 
       
   731 	if(need & 255)
       
   732 	    need = (need | 255) + 1;
       
   733 	buf = realloc(buf, bufspc += need);
       
   734 	bp = buf + bo;
       
   735 	if(bo1 != -1)
       
   736 	    bp1 = buf + bo1;
       
   737     }
       
   738 }
       
   739 
       
   740 /* stradd() adds a metafied string to the prompt, *
       
   741  * in a visible representation.                   */
       
   742 
       
   743 /**/
       
   744 void
       
   745 stradd(char *d)
       
   746 {
       
   747     char *ps, *pc;
       
   748     addbufspc(niceztrlen(d));
       
   749     /* This loop puts the nice representation of the string into the prompt *
       
   750      * buffer.                                                              */
       
   751     for(ps=d; *ps; ps++)
       
   752 	for(pc=nicechar(*ps == Meta ? STOUC(*++ps)^32 : STOUC(*ps)); *pc; pc++)
       
   753 	    *bp++ = *pc;
       
   754 }
       
   755 
       
   756 /* tsetcap(), among other things, can write a termcap string into the buffer. */
       
   757 
       
   758 /**/
       
   759 mod_export void
       
   760 tsetcap(int cap, int flag)
       
   761 {
       
   762     if (tccan(cap) && !isset(SINGLELINEZLE) &&
       
   763         !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) {
       
   764 	switch(flag) {
       
   765 	case -1:
       
   766 	    tputs(tcstr[cap], 1, putraw);
       
   767 	    break;
       
   768 	case 0:
       
   769 	    tputs(tcstr[cap], 1, putshout);
       
   770 	    break;
       
   771 	case 1:
       
   772 	    if (!dontcount) {
       
   773 		addbufspc(1);
       
   774 		*bp++ = Inpar;
       
   775 	    }
       
   776 	    tputs(tcstr[cap], 1, putstr);
       
   777 	    if (!dontcount) {
       
   778 		int glitch = 0;
       
   779 
       
   780 		if (cap == TCSTANDOUTBEG || cap == TCSTANDOUTEND)
       
   781 		    glitch = tgetnum("sg");
       
   782 		else if (cap == TCUNDERLINEBEG || cap == TCUNDERLINEEND)
       
   783 		    glitch = tgetnum("ug");
       
   784 		if(glitch < 0)
       
   785 		    glitch = 0;
       
   786 		addbufspc(glitch + 1);
       
   787 		while(glitch--)
       
   788 		    *bp++ = Nularg;
       
   789 		*bp++ = Outpar;
       
   790 	    }
       
   791 	    break;
       
   792 	}
       
   793 
       
   794 	if (txtisset(TXTDIRTY)) {
       
   795 	    txtunset(TXTDIRTY);
       
   796 	    if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG)
       
   797 		tsetcap(TCBOLDFACEBEG, flag);
       
   798 	    if (txtisset(TXTSTANDOUT))
       
   799 		tsetcap(TCSTANDOUTBEG, flag);
       
   800 	    if (txtisset(TXTUNDERLINE))
       
   801 		tsetcap(TCUNDERLINEBEG, flag);
       
   802 	}
       
   803     }
       
   804 }
       
   805 
       
   806 /**/
       
   807 int
       
   808 putstr(int d)
       
   809 {
       
   810     addbufspc(1);
       
   811     pputc(d);
       
   812     return 0;
       
   813 }
       
   814 
       
   815 /* Count height etc. of a prompt string returned by promptexpand(). *
       
   816  * This depends on the current terminal width, and tabs and         *
       
   817  * newlines require nontrivial processing.                          *
       
   818  * Passing `overf' as -1 means to ignore columns (absolute width).  */
       
   819 
       
   820 /**/
       
   821 mod_export void
       
   822 countprompt(char *str, int *wp, int *hp, int overf)
       
   823 {
       
   824     int w = 0, h = 1;
       
   825     int s = 1;
       
   826     for(; *str; str++) {
       
   827 	if(w >= columns && overf >= 0) {
       
   828 	    w = 0;
       
   829 	    h++;
       
   830 	}
       
   831 	if(*str == Meta)
       
   832 	    str++;
       
   833 	if(*str == Inpar)
       
   834 	    s = 0;
       
   835 	else if(*str == Outpar)
       
   836 	    s = 1;
       
   837 	else if(*str == Nularg)
       
   838 	    w++;
       
   839 	else if(s) {
       
   840 	    if(*str == '\t')
       
   841 		w = (w | 7) + 1;
       
   842 	    else if(*str == '\n') {
       
   843 		w = 0;
       
   844 		h++;
       
   845 	    } else
       
   846 		w++;
       
   847 	}
       
   848     }
       
   849     if(w >= columns && overf >= 0) {
       
   850 	if (!overf || w > columns) {
       
   851 	    w = 0;
       
   852 	    h++;
       
   853 	}
       
   854     }
       
   855     if(wp)
       
   856 	*wp = w;
       
   857     if(hp)
       
   858 	*hp = h;
       
   859 }
       
   860 
       
   861 /**/
       
   862 static int
       
   863 prompttrunc(int arg, int truncchar, int doprint, int endchar)
       
   864 {
       
   865     if (arg > 0) {
       
   866 	char ch = *fm, *ptr, *truncstr;
       
   867 	int truncatleft = ch == '<';
       
   868 	int w = bp - buf;
       
   869 
       
   870 	/*
       
   871 	 * If there is already a truncation active, return so that
       
   872 	 * can be finished, backing up so that the new truncation
       
   873 	 * can be started afterwards.
       
   874 	 */
       
   875 	if (trunclen) {
       
   876 	    while (*--fm != '%')
       
   877 		;
       
   878 	    fm--;
       
   879 	    return 0;
       
   880 	}
       
   881 
       
   882 	trunclen = arg;
       
   883 	if (*fm != ']')
       
   884 	    fm++;
       
   885 	while (*fm && *fm != truncchar) {
       
   886 	    if (*fm == '\\' && fm[1])
       
   887 		++fm;
       
   888 	    addbufspc(1);
       
   889 	    *bp++ = *fm++;
       
   890 	}
       
   891 	if (!*fm)
       
   892 	    return 0;
       
   893 	if (bp - buf == w && truncchar == ']') {
       
   894 	    addbufspc(1);
       
   895 	    *bp++ = '<';
       
   896 	}
       
   897 	ptr = buf + w;		/* addbufspc() may have realloc()'d buf */
       
   898 	truncstr = ztrduppfx(ptr, bp - ptr);
       
   899 
       
   900 	bp = ptr;
       
   901 	w = bp - buf;
       
   902 	fm++;
       
   903 	trunccount = dontcount;
       
   904 	putpromptchar(doprint, endchar);
       
   905 	trunccount = 0;
       
   906 	ptr = buf + w;		/* putpromptchar() may have realloc()'d */
       
   907 	*bp = '\0';
       
   908 
       
   909 	countprompt(ptr, &w, 0, -1);
       
   910 	if (w > trunclen) {
       
   911 	    /*
       
   912 	     * We need to truncate.  t points to the truncation string -- *
       
   913 	     * which is inserted literally, without nice representation.  *
       
   914 	     * tlen is its length, and maxlen is the amount of the main	  *
       
   915 	     * string that we want to keep.  Note that if the truncation  *
       
   916 	     * string is longer than the truncation length (tlen >	  *
       
   917 	     * trunclen), the truncation string is used in full.	  *
       
   918 	     */
       
   919 	    char *t = truncstr;
       
   920 	    int fullen = bp - ptr;
       
   921 	    int tlen = ztrlen(t), maxlen;
       
   922 	    maxlen = tlen < trunclen ? trunclen - tlen : 0;
       
   923 	    if (w < fullen) {
       
   924 		/* Invisible substrings, lots of shuffling. */
       
   925 		int n = strlen(t);
       
   926 		char *p = ptr, *q = buf;
       
   927 		addbufspc(n);
       
   928 		ptr = buf + (p - q); /* addbufspc() may have realloc()'d */
       
   929 
       
   930 		if (truncatleft) {
       
   931 		    p = ptr + n;
       
   932 		    q = p;
       
   933 
       
   934 		    n = fullen - w;
       
   935 
       
   936 		    /* Shift the whole string right, then *
       
   937 		     * selectively copy to the left.      */
       
   938 		    memmove(p, ptr, fullen);
       
   939 		    while (w > 0 || n > 0) {
       
   940 			if (*p == Inpar)
       
   941 			    do {
       
   942 				*q++ = *p;
       
   943 				--n;
       
   944 			    } while (*p++ != Outpar && *p && n);
       
   945 			else if (w) {
       
   946 			    if (--w < maxlen)
       
   947 				*q++ = *p;
       
   948 			    ++p;
       
   949 			}
       
   950 		    }
       
   951 		    bp = q;
       
   952 		} else {
       
   953 		    /* Truncate on the right, selectively */
       
   954 		    q = ptr + fullen;
       
   955 
       
   956 		    /* First skip over as much as will "fit". */
       
   957 		    while (w > 0 && maxlen > 0) {
       
   958 			if (*ptr == Inpar)
       
   959 			    while (*ptr++ != Outpar && *ptr) {;}
       
   960 			else
       
   961 			    ++ptr, --w, --maxlen;
       
   962 		    }
       
   963 		    if (ptr < q) {
       
   964 			/* We didn't reach the end of the string. *
       
   965 			 * In case there are more invisible bits, *
       
   966 			 * insert the truncstr and keep looking.  */
       
   967 			memmove(ptr + n, ptr, q - ptr);
       
   968 			q = ptr + n;
       
   969 			while (*t)
       
   970 			    *ptr++ = *t++;
       
   971 			while (*q) {
       
   972 			    if (*q == Inpar)
       
   973 				do {
       
   974 				    *ptr++ = *q;
       
   975 				} while (*q++ != Outpar && *q);
       
   976 			    else
       
   977 				++q;
       
   978 			}
       
   979 			bp = ptr;
       
   980 			*bp = 0;
       
   981 		    } else
       
   982 			bp = ptr + n;
       
   983 		}
       
   984 	    } else {
       
   985 		/* No invisible substrings. */
       
   986 		if (tlen > fullen) {
       
   987 		    addbufspc(tlen - fullen);
       
   988 		    ptr = bp;	/* addbufspc() may have realloc()'d buf */
       
   989 		    bp += tlen - fullen;
       
   990 		} else
       
   991 		    bp -= fullen - trunclen;
       
   992 		if (truncatleft) {
       
   993 		    if (maxlen)
       
   994 			memmove(ptr + strlen(t), ptr + fullen - maxlen,
       
   995 				maxlen);
       
   996 		} else
       
   997 		    ptr += maxlen;
       
   998 	    }
       
   999 	    /* Finally, copy the truncstr into place. */
       
  1000 	    while (*t)
       
  1001 		*ptr++ = *t++;
       
  1002 	}
       
  1003 	zsfree(truncstr);
       
  1004 	trunclen = 0;
       
  1005 	/*
       
  1006 	 * We may have returned early from the previous putpromptchar *
       
  1007 	 * because we found another truncation following this one.    *
       
  1008 	 * In that case we need to do the rest now.                   *
       
  1009 	 */
       
  1010 	if (!*fm)
       
  1011 	    return 0;
       
  1012 	if (*fm != endchar) {
       
  1013 	    fm++;
       
  1014 	    /*
       
  1015 	     * With trunclen set to zero, we always reach endchar *
       
  1016 	     * (or the terminating NULL) this time round.         *
       
  1017 	     */
       
  1018 	    if (!putpromptchar(doprint, endchar))
       
  1019 		return 0;
       
  1020 	}
       
  1021 	/* Now we have to trick it into matching endchar again */
       
  1022 	fm--;
       
  1023     } else {
       
  1024 	if (*fm != ']')
       
  1025 	    fm++;
       
  1026 	while(*fm && *fm != truncchar) {
       
  1027 	    if (*fm == '\\' && fm[1])
       
  1028 		fm++;
       
  1029 	    fm++;
       
  1030 	}
       
  1031 	if (trunclen || !*fm)
       
  1032 	    return 0;
       
  1033     }
       
  1034     return 1;
       
  1035 }