tc.prompt.c revision 167466
165483Simp/* $Header: /p/tcsh/cvsroot/tcsh/tc.prompt.c,v 3.67 2006/11/17 16:26:58 christos Exp $ */ 252506Simp/* 3119418Sobrien * tc.prompt.c: Prompt printing stuff 4119418Sobrien */ 5119418Sobrien/*- 652506Simp * Copyright (c) 1980, 1991 The Regents of the University of California. 752506Simp * All rights reserved. 8139749Simp * 952506Simp * Redistribution and use in source and binary forms, with or without 1052506Simp * modification, are permitted provided that the following conditions 1152506Simp * are met: 1252506Simp * 1. Redistributions of source code must retain the above copyright 1352506Simp * notice, this list of conditions and the following disclaimer. 1452506Simp * 2. Redistributions in binary form must reproduce the above copyright 1552506Simp * notice, this list of conditions and the following disclaimer in the 1652506Simp * documentation and/or other materials provided with the distribution. 1752506Simp * 3. Neither the name of the University nor the names of its contributors 1852506Simp * may be used to endorse or promote products derived from this software 1952506Simp * without specific prior written permission. 2052506Simp * 2152506Simp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2252506Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2352506Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2452506Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2552506Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2652506Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2752506Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2852506Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2952506Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3052506Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3152506Simp * SUCH DAMAGE. 3252506Simp */ 3352506Simp#include "sh.h" 3452506Simp 3552506SimpRCSID("$tcsh: tc.prompt.c,v 3.67 2006/11/17 16:26:58 christos Exp $") 3652506Simp 3752506Simp#include "ed.h" 38129762Simp#include "tw.h" 39129781Simp 4052506Simp/* 4152506Simp * kfk 21oct1983 -- add @ (time) and / ($cwd) in prompt. 4252506Simp * PWP 4/27/87 -- rearange for tcsh. 4352506Simp * mrdch@com.tau.edu.il 6/26/89 - added ~, T and .# - rearanged to switch() 44129762Simp * instead of if/elseif 45129762Simp * Luke Mewburn, <lukem@cs.rmit.edu.au> 46150371Sglebius * 6-Sep-91 changed date format 47129781Simp * 16-Feb-94 rewrote directory prompt code, added $ellipsis 48129740Simp * 29-Dec-96 added rprompt support 4952506Simp */ 5052506Simp 5152506Simpstatic const char *month_list[12]; 5252506Simpstatic const char *day_list[7]; 5352506Simp 5452506Simpvoid 5552506Simpdateinit(void) 5652506Simp{ 5752506Simp#ifdef notyet 5852506Simp int i; 5952506Simp 6052506Simp setlocale(LC_TIME, ""); 6152506Simp 6252506Simp for (i = 0; i < 12; i++) 6352506Simp xfree((ptr_t) month_list[i]); 6452506Simp month_list[0] = strsave(_time_info->abbrev_month[0]); 6552506Simp month_list[1] = strsave(_time_info->abbrev_month[1]); 6652506Simp month_list[2] = strsave(_time_info->abbrev_month[2]); 6752506Simp month_list[3] = strsave(_time_info->abbrev_month[3]); 6852506Simp month_list[4] = strsave(_time_info->abbrev_month[4]); 6952506Simp month_list[5] = strsave(_time_info->abbrev_month[5]); 7052506Simp month_list[6] = strsave(_time_info->abbrev_month[6]); 7152506Simp month_list[7] = strsave(_time_info->abbrev_month[7]); 7252506Simp month_list[8] = strsave(_time_info->abbrev_month[8]); 7352506Simp month_list[9] = strsave(_time_info->abbrev_month[9]); 7452506Simp month_list[10] = strsave(_time_info->abbrev_month[10]); 7552506Simp month_list[11] = strsave(_time_info->abbrev_month[11]); 7652506Simp 7752506Simp for (i = 0; i < 7; i++) 7852506Simp xfree((ptr_t) day_list[i]); 7952506Simp day_list[0] = strsave(_time_info->abbrev_wkday[0]); 8052506Simp day_list[1] = strsave(_time_info->abbrev_wkday[1]); 8152506Simp day_list[2] = strsave(_time_info->abbrev_wkday[2]); 8252506Simp day_list[3] = strsave(_time_info->abbrev_wkday[3]); 8352506Simp day_list[4] = strsave(_time_info->abbrev_wkday[4]); 8452506Simp day_list[5] = strsave(_time_info->abbrev_wkday[5]); 8552506Simp day_list[6] = strsave(_time_info->abbrev_wkday[6]); 8652506Simp#else 8752506Simp month_list[0] = "Jan"; 8852506Simp month_list[1] = "Feb"; 8952506Simp month_list[2] = "Mar"; 9052506Simp month_list[3] = "Apr"; 9152506Simp month_list[4] = "May"; 9252506Simp month_list[5] = "Jun"; 9352506Simp month_list[6] = "Jul"; 9452506Simp month_list[7] = "Aug"; 9552506Simp month_list[8] = "Sep"; 9652506Simp month_list[9] = "Oct"; 9752506Simp month_list[10] = "Nov"; 9853813Simp month_list[11] = "Dec"; 9953813Simp 10053813Simp day_list[0] = "Sun"; 10153813Simp day_list[1] = "Mon"; 10253813Simp day_list[2] = "Tue"; 10353813Simp day_list[3] = "Wed"; 10453813Simp day_list[4] = "Thu"; 10553813Simp day_list[5] = "Fri"; 10653813Simp day_list[6] = "Sat"; 10753813Simp#endif 10853813Simp} 10953813Simp 11053813Simpvoid 11153813Simpprintprompt(int promptno, const char *str) 11253813Simp{ 11353813Simp static const Char *ocp = NULL; 11453813Simp static const char *ostr = NULL; 11553813Simp time_t lclock = time(NULL); 11653813Simp const Char *cp; 11753813Simp 11853813Simp switch (promptno) { 11953813Simp default: 12053813Simp case 0: 12153813Simp cp = varval(STRprompt); 12253813Simp break; 12353813Simp case 1: 12453813Simp cp = varval(STRprompt2); 12553813Simp break; 12653813Simp case 2: 12753813Simp cp = varval(STRprompt3); 12853813Simp break; 12953813Simp case 3: 13053813Simp if (ocp != NULL) { 13153813Simp cp = ocp; 13253813Simp str = ostr; 13353813Simp } 13453813Simp else 13553813Simp cp = varval(STRprompt); 13653813Simp break; 13753813Simp } 13853813Simp 13953813Simp if (promptno < 2) { 140182142Simp ocp = cp; 141182142Simp ostr = str; 142182142Simp } 143182142Simp 144182142Simp xfree(Prompt); 145182142Simp Prompt = NULL; 146182142Simp Prompt = tprintf(FMT_PROMPT, cp, str, lclock, NULL); 147182142Simp if (!editing) { 148182142Simp for (cp = Prompt; *cp ; ) 149182142Simp (void) putwraw(*cp++); 150182142Simp SetAttributes(0); 151182142Simp flush(); 152182142Simp } 153182142Simp 154182142Simp xfree(RPrompt); 155182142Simp RPrompt = NULL; 156182142Simp if (promptno == 0) { /* determine rprompt if using main prompt */ 157182142Simp cp = varval(STRrprompt); 158182142Simp RPrompt = tprintf(FMT_PROMPT, cp, NULL, lclock, NULL); 159182142Simp /* if not editing, put rprompt after prompt */ 160182142Simp if (!editing && RPrompt[0] != '\0') { 16152506Simp for (cp = RPrompt; *cp ; ) 16252506Simp (void) putwraw(*cp++); 16352506Simp SetAttributes(0); 16452506Simp putraw(' '); 16552506Simp flush(); 16652506Simp } 16752506Simp } 16852506Simp} 16952506Simp 17052506Simpstatic void 17152506Simptprintf_append_mbs(struct Strbuf *buf, const char *mbs, Char attributes) 17252506Simp{ 17352506Simp while (*mbs != 0) { 17452506Simp Char wc; 17552506Simp 17652506Simp mbs += one_mbtowc(&wc, mbs, MB_LEN_MAX); 17752506Simp Strbuf_append1(buf, wc | attributes); 17852506Simp } 17952506Simp} 18052506Simp 18152506SimpChar * 18252506Simptprintf(int what, const Char *fmt, const char *str, time_t tim, ptr_t info) 18365483Simp{ 18465483Simp struct Strbuf buf = Strbuf_INIT; 18565483Simp Char *z, *q; 18665483Simp Char attributes = 0; 18765483Simp static int print_prompt_did_ding = 0; 18865483Simp char *cz; 18965483Simp 19065483Simp Char *p; 19165483Simp const Char *cp = fmt; 19265483Simp Char Scp; 19365483Simp struct tm *t = localtime(&tim); 19465483Simp 19565483Simp /* prompt stuff */ 19665483Simp static Char *olduser = NULL; 19765483Simp int updirs; 19865483Simp size_t pdirs; 19965483Simp 20065483Simp cleanup_push(&buf, Strbuf_cleanup); 20165483Simp for (; *cp; cp++) { 20265483Simp if ((*cp == '%') && ! (cp[1] == '\0')) { 20365483Simp cp++; 204172572Sremko switch (*cp) { 205172572Sremko case 'R': 206172572Sremko if (what == FMT_HISTORY) { 207172572Sremko cz = fmthist('R', info); 208172572Sremko tprintf_append_mbs(&buf, cz, attributes); 209172572Sremko xfree(cz); 210172572Sremko } else { 211172572Sremko if (str != NULL) 212172572Sremko tprintf_append_mbs(&buf, str, attributes); 213172572Sremko } 214172572Sremko break; 215172572Sremko case '#': 216172572Sremko Strbuf_append1(&buf, 217172572Sremko attributes | ((uid == 0) ? PRCHROOT : PRCH)); 218172572Sremko break; 219172572Sremko case '!': 220172572Sremko case 'h': 221172572Sremko switch (what) { 222172572Sremko case FMT_HISTORY: 223172572Sremko cz = fmthist('h', info); 224172572Sremko break; 22552506Simp case FMT_SCHED: 22686272Simp cz = xasprintf("%d", *(int *)info); 22752506Simp break; 22886272Simp default: 22952506Simp cz = xasprintf("%d", eventno + 1); 23086272Simp break; 23153813Simp } 23286272Simp tprintf_append_mbs(&buf, cz, attributes); 23353813Simp xfree(cz); 23486272Simp break; 23586272Simp case 'T': /* 24 hour format */ 23653813Simp case '@': 23786272Simp case 't': /* 12 hour am/pm format */ 23886272Simp case 'p': /* With seconds */ 23953813Simp case 'P': 240172572Sremko { 241172572Sremko char ampm = 'a'; 242172572Sremko int hr = t->tm_hour; 243172572Sremko 244172572Sremko /* addition by Hans J. Albertsson */ 245172572Sremko /* and another adapted from Justin Bur */ 246172572Sremko if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) { 247172572Sremko if (hr >= 12) { 248172572Sremko if (hr > 12) 249172572Sremko hr -= 12; 250172572Sremko ampm = 'p'; 251172572Sremko } 252182142Simp else if (hr == 0) 253182142Simp hr = 12; 25486272Simp } /* else do a 24 hour clock */ 25552506Simp 25686272Simp /* "DING!" stuff by Hans also */ 25765483Simp if (t->tm_min || print_prompt_did_ding || 25852506Simp what != FMT_PROMPT || adrof(STRnoding)) { 25952506Simp if (t->tm_min) 26052506Simp print_prompt_did_ding = 0; 26152506Simp /* 26252506Simp * Pad hour to 2 characters if padhour is set, 263120849Simp * by ADAM David Alan Martin 264120849Simp */ 265120849Simp p = Itoa(hr, adrof(STRpadhour) ? 2 : 0, attributes); 266120849Simp Strbuf_append(&buf, p); 267120849Simp xfree(p); 268120849Simp Strbuf_append1(&buf, attributes | ':'); 269120849Simp p = Itoa(t->tm_min, 2, attributes); 270120849Simp Strbuf_append(&buf, p); 271120849Simp xfree(p); 272120849Simp if (*cp == 'p' || *cp == 'P') { 273120849Simp Strbuf_append1(&buf, attributes | ':'); 274120849Simp p = Itoa(t->tm_sec, 2, attributes); 275120849Simp Strbuf_append(&buf, p); 276120849Simp xfree(p); 277120849Simp } 278120849Simp if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) { 279120849Simp Strbuf_append1(&buf, attributes | ampm); 28052506Simp Strbuf_append1(&buf, attributes | 'm'); 28152506Simp } 28264850Simp } 28352506Simp else { /* we need to ding */ 28452506Simp size_t i; 28552506Simp 28652506Simp for (i = 0; STRDING[i] != 0; i++) 287120849Simp Strbuf_append1(&buf, attributes | STRDING[i]); 28852506Simp print_prompt_did_ding = 1; 28952506Simp } 29052506Simp } 29152506Simp break; 29252506Simp 293120849Simp case 'M': 294120849Simp#ifndef HAVENOUTMP 295120849Simp if (what == FMT_WHO) 296120849Simp cz = who_info(info, 'M'); 297120849Simp else 298120849Simp#endif /* HAVENOUTMP */ 299120849Simp cz = getenv("HOST"); 300120849Simp /* 301120849Simp * Bug pointed out by Laurent Dami <dami@cui.unige.ch>: don't 302120849Simp * derefrence that NULL (if HOST is not set)... 303120849Simp */ 304120849Simp if (cz != NULL) 30552506Simp tprintf_append_mbs(&buf, cz, attributes); 306120849Simp if (what == FMT_WHO) 307120849Simp xfree(cz); 30852506Simp break; 309120849Simp 310120849Simp case 'm': { 311120849Simp char *scz = NULL; 312120849Simp#ifndef HAVENOUTMP 313120849Simp if (what == FMT_WHO) 314120849Simp scz = cz = who_info(info, 'm'); 31552506Simp else 316120849Simp#endif /* HAVENOUTMP */ 317120849Simp cz = getenv("HOST"); 31852506Simp 31952506Simp if (cz != NULL) 320120849Simp while (*cz != 0 && (what == FMT_WHO || *cz != '.')) { 321120849Simp Char wc; 322120849Simp 32352506Simp cz += one_mbtowc(&wc, cz, MB_LEN_MAX); 324120849Simp Strbuf_append1(&buf, wc | attributes); 325120849Simp } 326144158Ssam if (scz) 327144158Ssam xfree(scz); 328144158Ssam break; 329144158Ssam } 330120849Simp 331120849Simp /* lukem: new directory prompt code */ 332120849Simp case '~': 333120849Simp case '/': 334144158Ssam case '.': 335144158Ssam case 'c': 336144158Ssam case 'C': 337144158Ssam Scp = *cp; 338144158Ssam if (Scp == 'c') /* store format type (c == .) */ 339120849Simp Scp = '.'; 340120849Simp if ((z = varval(STRcwd)) == STRNULL) 341120849Simp break; /* no cwd, so don't do anything */ 342144158Ssam 343144158Ssam /* show ~ whenever possible - a la dirs */ 344144158Ssam if (Scp == '~' || Scp == '.' ) { 345144158Ssam static Char *olddir = NULL; 346144158Ssam 347120849Simp if (tlength == 0 || olddir != z) { 348120849Simp olddir = z; /* have we changed dir? */ 349120849Simp olduser = getusername(&olddir); 350120849Simp } 35152506Simp if (olduser) 35252506Simp z = olddir; 35352506Simp } 354 updirs = pdirs = 0; 355 356 /* option to determine fixed # of dirs from path */ 357 if (Scp == '.' || Scp == 'C') { 358 int skip; 359#ifdef WINNT_NATIVE 360 Char *oldz = z; 361 if (z[1] == ':') { 362 Strbuf_append1(&buf, attributes | *z++); 363 Strbuf_append1(&buf, attributes | *z++); 364 } 365 if (*z == '/' && z[1] == '/') { 366 Strbuf_append1(&buf, attributes | *z++); 367 Strbuf_append1(&buf, attributes | *z++); 368 do { 369 Strbuf_append1(&buf, attributes | *z++); 370 } while(*z != '/'); 371 } 372#endif /* WINNT_NATIVE */ 373 q = z; 374 while (*z) /* calc # of /'s */ 375 if (*z++ == '/') 376 updirs++; 377 378#ifdef WINNT_NATIVE 379 /* 380 * for format type c, prompt will be following... 381 * c:/path => c:/path 382 * c:/path/to => c:to 383 * //machine/share => //machine/share 384 * //machine/share/folder => //machine:folder 385 */ 386 if (oldz[0] == '/' && oldz[1] == '/' && updirs > 1) 387 Strbuf_append1(&buf, attributes | ':'); 388#endif /* WINNT_NATIVE */ 389 if ((Scp == 'C' && *q != '/')) 390 updirs++; 391 392 if (cp[1] == '0') { /* print <x> or ... */ 393 pdirs = 1; 394 cp++; 395 } 396 if (cp[1] >= '1' && cp[1] <= '9') { /* calc # to skip */ 397 skip = cp[1] - '0'; 398 cp++; 399 } 400 else 401 skip = 1; 402 403 updirs -= skip; 404 while (skip-- > 0) { 405 while ((z > q) && (*z != '/')) 406 z--; /* back up */ 407 if (skip && z > q) 408 z--; 409 } 410 if (*z == '/' && z != q) 411 z++; 412 } /* . || C */ 413 414 /* print ~[user] */ 415 if ((olduser) && ((Scp == '~') || 416 (Scp == '.' && (pdirs || (!pdirs && updirs <= 0))) )) { 417 Strbuf_append1(&buf, attributes | '~'); 418 for (q = olduser; *q; q++) 419 Strbuf_append1(&buf, attributes | *q); 420 } 421 422 /* RWM - tell you how many dirs we've ignored */ 423 /* and add '/' at front of this */ 424 if (updirs > 0 && pdirs) { 425 if (adrof(STRellipsis)) { 426 Strbuf_append1(&buf, attributes | '.'); 427 Strbuf_append1(&buf, attributes | '.'); 428 Strbuf_append1(&buf, attributes | '.'); 429 } else { 430 Strbuf_append1(&buf, attributes | '/'); 431 Strbuf_append1(&buf, attributes | '<'); 432 if (updirs > 9) { 433 Strbuf_append1(&buf, attributes | '9'); 434 Strbuf_append1(&buf, attributes | '+'); 435 } else 436 Strbuf_append1(&buf, attributes | ('0' + updirs)); 437 Strbuf_append1(&buf, attributes | '>'); 438 } 439 } 440 441 while (*z) 442 Strbuf_append1(&buf, attributes | *z++); 443 break; 444 /* lukem: end of new directory prompt code */ 445 446 case 'n': 447#ifndef HAVENOUTMP 448 if (what == FMT_WHO) { 449 cz = who_info(info, 'n'); 450 tprintf_append_mbs(&buf, cz, attributes); 451 xfree(cz); 452 } 453 else 454#endif /* HAVENOUTMP */ 455 { 456 if ((z = varval(STRuser)) != STRNULL) 457 while (*z) 458 Strbuf_append1(&buf, attributes | *z++); 459 } 460 break; 461 case 'l': 462#ifndef HAVENOUTMP 463 if (what == FMT_WHO) { 464 cz = who_info(info, 'l'); 465 tprintf_append_mbs(&buf, cz, attributes); 466 xfree(cz); 467 } 468 else 469#endif /* HAVENOUTMP */ 470 { 471 if ((z = varval(STRtty)) != STRNULL) 472 while (*z) 473 Strbuf_append1(&buf, attributes | *z++); 474 } 475 break; 476 case 'd': 477 tprintf_append_mbs(&buf, day_list[t->tm_wday], attributes); 478 break; 479 case 'D': 480 p = Itoa(t->tm_mday, 2, attributes); 481 Strbuf_append(&buf, p); 482 xfree(p); 483 break; 484 case 'w': 485 tprintf_append_mbs(&buf, month_list[t->tm_mon], attributes); 486 break; 487 case 'W': 488 p = Itoa(t->tm_mon + 1, 2, attributes); 489 Strbuf_append(&buf, p); 490 xfree(p); 491 break; 492 case 'y': 493 p = Itoa(t->tm_year % 100, 2, attributes); 494 Strbuf_append(&buf, p); 495 xfree(p); 496 break; 497 case 'Y': 498 p = Itoa(t->tm_year + 1900, 4, attributes); 499 Strbuf_append(&buf, p); 500 xfree(p); 501 break; 502 case 'S': /* start standout */ 503 attributes |= STANDOUT; 504 break; 505 case 'B': /* start bold */ 506 attributes |= BOLD; 507 break; 508 case 'U': /* start underline */ 509 attributes |= UNDER; 510 break; 511 case 's': /* end standout */ 512 attributes &= ~STANDOUT; 513 break; 514 case 'b': /* end bold */ 515 attributes &= ~BOLD; 516 break; 517 case 'u': /* end underline */ 518 attributes &= ~UNDER; 519 break; 520 case 'L': 521 ClearToBottom(); 522 break; 523 524 case 'j': 525 { 526 int njobs = -1; 527 struct process *pp; 528 529 for (pp = proclist.p_next; pp; pp = pp->p_next) 530 njobs++; 531 p = Itoa(njobs, 1, attributes); 532 Strbuf_append(&buf, p); 533 xfree(p); 534 break; 535 } 536 case '?': 537 if ((z = varval(STRstatus)) != STRNULL) 538 while (*z) 539 Strbuf_append1(&buf, attributes | *z++); 540 break; 541 case '$': 542 expdollar(&buf, &cp, attributes); 543 /* cp should point the last char of current % sequence */ 544 cp--; 545 break; 546 case '%': 547 Strbuf_append1(&buf, attributes | '%'); 548 break; 549 case '{': /* literal characters start */ 550#if LITERAL == 0 551 /* 552 * No literal capability, so skip all chars in the literal 553 * string 554 */ 555 while (*cp != '\0' && (cp[-1] != '%' || *cp != '}')) 556 cp++; 557#endif /* LITERAL == 0 */ 558 attributes |= LITERAL; 559 break; 560 case '}': /* literal characters end */ 561 attributes &= ~LITERAL; 562 break; 563 default: 564#ifndef HAVENOUTMP 565 if (*cp == 'a' && what == FMT_WHO) { 566 cz = who_info(info, 'a'); 567 tprintf_append_mbs(&buf, cz, attributes); 568 xfree(cz); 569 } 570 else 571#endif /* HAVENOUTMP */ 572 { 573 Strbuf_append1(&buf, attributes | '%'); 574 Strbuf_append1(&buf, attributes | *cp); 575 } 576 break; 577 } 578 } 579 else if (*cp == '\\' || *cp == '^') 580 Strbuf_append1(&buf, attributes | parseescape(&cp)); 581 else if (*cp == HIST) { /* EGS: handle '!'s in prompts */ 582 if (what == FMT_HISTORY) 583 cz = fmthist('h', info); 584 else 585 cz = xasprintf("%d", eventno + 1); 586 tprintf_append_mbs(&buf, cz, attributes); 587 xfree(cz); 588 } 589 else 590 Strbuf_append1(&buf, attributes | *cp); /* normal character */ 591 } 592 cleanup_ignore(&buf); 593 cleanup_until(&buf); 594 return Strbuf_finish(&buf); 595} 596 597int 598expdollar(struct Strbuf *buf, const Char **srcp, Char attr) 599{ 600 struct varent *vp; 601 const Char *src = *srcp; 602 Char *var, *val; 603 size_t i; 604 int curly = 0; 605 606 /* found a variable, expand it */ 607 var = xmalloc((Strlen(src) + 1) * sizeof (*var)); 608 for (i = 0; ; i++) { 609 var[i] = *++src & TRIM; 610 if (i == 0 && var[i] == '{') { 611 curly = 1; 612 var[i] = *++src & TRIM; 613 } 614 if (!alnum(var[i]) && var[i] != '_') { 615 616 var[i] = '\0'; 617 break; 618 } 619 } 620 if (curly && (*src & TRIM) == '}') 621 src++; 622 623 vp = adrof(var); 624 if (vp && vp->vec) { 625 for (i = 0; vp->vec[i] != NULL; i++) { 626 for (val = vp->vec[i]; *val; val++) 627 if (*val != '\n' && *val != '\r') 628 Strbuf_append1(buf, *val | attr); 629 if (vp->vec[i+1]) 630 Strbuf_append1(buf, ' ' | attr); 631 } 632 } 633 else { 634 val = (!vp) ? tgetenv(var) : NULL; 635 if (val) { 636 for (; *val; val++) 637 if (*val != '\n' && *val != '\r') 638 Strbuf_append1(buf, *val | attr); 639 } else { 640 *srcp = src; 641 xfree(var); 642 return 0; 643 } 644 } 645 646 *srcp = src; 647 xfree(var); 648 return 1; 649} 650