|
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 } |