/contrib/tcsh/tc.prompt.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 657 lines · 537 code · 45 blank · 75 comment · 216 complexity · e5c5e97866f8a1f428520ca2e6841ec9 MD5 · raw file

  1. /* $Header: /p/tcsh/cvsroot/tcsh/tc.prompt.c,v 3.70 2011/10/27 22:41:06 christos Exp $ */
  2. /*
  3. * tc.prompt.c: Prompt printing stuff
  4. */
  5. /*-
  6. * Copyright (c) 1980, 1991 The Regents of the University of California.
  7. * All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. * 3. Neither the name of the University nor the names of its contributors
  18. * may be used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. */
  33. #include "sh.h"
  34. RCSID("$tcsh: tc.prompt.c,v 3.70 2011/10/27 22:41:06 christos Exp $")
  35. #include "ed.h"
  36. #include "tw.h"
  37. /*
  38. * kfk 21oct1983 -- add @ (time) and / ($cwd) in prompt.
  39. * PWP 4/27/87 -- rearange for tcsh.
  40. * mrdch@com.tau.edu.il 6/26/89 - added ~, T and .# - rearanged to switch()
  41. * instead of if/elseif
  42. * Luke Mewburn, <lukem@cs.rmit.edu.au>
  43. * 6-Sep-91 changed date format
  44. * 16-Feb-94 rewrote directory prompt code, added $ellipsis
  45. * 29-Dec-96 added rprompt support
  46. */
  47. static const char *month_list[12];
  48. static const char *day_list[7];
  49. void
  50. dateinit(void)
  51. {
  52. #ifdef notyet
  53. int i;
  54. setlocale(LC_TIME, "");
  55. for (i = 0; i < 12; i++)
  56. xfree((ptr_t) month_list[i]);
  57. month_list[0] = strsave(_time_info->abbrev_month[0]);
  58. month_list[1] = strsave(_time_info->abbrev_month[1]);
  59. month_list[2] = strsave(_time_info->abbrev_month[2]);
  60. month_list[3] = strsave(_time_info->abbrev_month[3]);
  61. month_list[4] = strsave(_time_info->abbrev_month[4]);
  62. month_list[5] = strsave(_time_info->abbrev_month[5]);
  63. month_list[6] = strsave(_time_info->abbrev_month[6]);
  64. month_list[7] = strsave(_time_info->abbrev_month[7]);
  65. month_list[8] = strsave(_time_info->abbrev_month[8]);
  66. month_list[9] = strsave(_time_info->abbrev_month[9]);
  67. month_list[10] = strsave(_time_info->abbrev_month[10]);
  68. month_list[11] = strsave(_time_info->abbrev_month[11]);
  69. for (i = 0; i < 7; i++)
  70. xfree((ptr_t) day_list[i]);
  71. day_list[0] = strsave(_time_info->abbrev_wkday[0]);
  72. day_list[1] = strsave(_time_info->abbrev_wkday[1]);
  73. day_list[2] = strsave(_time_info->abbrev_wkday[2]);
  74. day_list[3] = strsave(_time_info->abbrev_wkday[3]);
  75. day_list[4] = strsave(_time_info->abbrev_wkday[4]);
  76. day_list[5] = strsave(_time_info->abbrev_wkday[5]);
  77. day_list[6] = strsave(_time_info->abbrev_wkday[6]);
  78. #else
  79. month_list[0] = "Jan";
  80. month_list[1] = "Feb";
  81. month_list[2] = "Mar";
  82. month_list[3] = "Apr";
  83. month_list[4] = "May";
  84. month_list[5] = "Jun";
  85. month_list[6] = "Jul";
  86. month_list[7] = "Aug";
  87. month_list[8] = "Sep";
  88. month_list[9] = "Oct";
  89. month_list[10] = "Nov";
  90. month_list[11] = "Dec";
  91. day_list[0] = "Sun";
  92. day_list[1] = "Mon";
  93. day_list[2] = "Tue";
  94. day_list[3] = "Wed";
  95. day_list[4] = "Thu";
  96. day_list[5] = "Fri";
  97. day_list[6] = "Sat";
  98. #endif
  99. }
  100. void
  101. printprompt(int promptno, const char *str)
  102. {
  103. static const Char *ocp = NULL;
  104. static const char *ostr = NULL;
  105. time_t lclock = time(NULL);
  106. const Char *cp;
  107. switch (promptno) {
  108. default:
  109. case 0:
  110. cp = varval(STRprompt);
  111. break;
  112. case 1:
  113. cp = varval(STRprompt2);
  114. break;
  115. case 2:
  116. cp = varval(STRprompt3);
  117. break;
  118. case 3:
  119. if (ocp != NULL) {
  120. cp = ocp;
  121. str = ostr;
  122. }
  123. else
  124. cp = varval(STRprompt);
  125. break;
  126. }
  127. if (promptno < 2) {
  128. ocp = cp;
  129. ostr = str;
  130. }
  131. xfree(Prompt);
  132. Prompt = NULL;
  133. Prompt = tprintf(FMT_PROMPT, cp, str, lclock, NULL);
  134. if (!editing) {
  135. for (cp = Prompt; *cp ; )
  136. (void) putwraw(*cp++);
  137. SetAttributes(0);
  138. flush();
  139. }
  140. xfree(RPrompt);
  141. RPrompt = NULL;
  142. if (promptno == 0) { /* determine rprompt if using main prompt */
  143. cp = varval(STRrprompt);
  144. RPrompt = tprintf(FMT_PROMPT, cp, NULL, lclock, NULL);
  145. /* if not editing, put rprompt after prompt */
  146. if (!editing && RPrompt[0] != '\0') {
  147. for (cp = RPrompt; *cp ; )
  148. (void) putwraw(*cp++);
  149. SetAttributes(0);
  150. putraw(' ');
  151. flush();
  152. }
  153. }
  154. }
  155. static void
  156. tprintf_append_mbs(struct Strbuf *buf, const char *mbs, Char attributes)
  157. {
  158. while (*mbs != 0) {
  159. Char wc;
  160. mbs += one_mbtowc(&wc, mbs, MB_LEN_MAX);
  161. Strbuf_append1(buf, wc | attributes);
  162. }
  163. }
  164. Char *
  165. tprintf(int what, const Char *fmt, const char *str, time_t tim, ptr_t info)
  166. {
  167. struct Strbuf buf = Strbuf_INIT;
  168. Char *z, *q;
  169. Char attributes = 0;
  170. static int print_prompt_did_ding = 0;
  171. char *cz;
  172. Char *p;
  173. const Char *cp = fmt;
  174. Char Scp;
  175. struct tm *t = localtime(&tim);
  176. /* prompt stuff */
  177. static Char *olduser = NULL;
  178. int updirs;
  179. size_t pdirs;
  180. cleanup_push(&buf, Strbuf_cleanup);
  181. for (; *cp; cp++) {
  182. if ((*cp == '%') && ! (cp[1] == '\0')) {
  183. cp++;
  184. switch (*cp) {
  185. case 'R':
  186. if (what == FMT_HISTORY) {
  187. cz = fmthist('R', info);
  188. tprintf_append_mbs(&buf, cz, attributes);
  189. xfree(cz);
  190. } else {
  191. if (str != NULL)
  192. tprintf_append_mbs(&buf, str, attributes);
  193. }
  194. break;
  195. case '#':
  196. Scp = (uid == 0 || euid == 0) ? PRCHROOT : PRCH;
  197. if (Scp != '\0')
  198. Strbuf_append1(&buf, attributes | Scp);
  199. break;
  200. case '!':
  201. case 'h':
  202. switch (what) {
  203. case FMT_HISTORY:
  204. cz = fmthist('h', info);
  205. break;
  206. case FMT_SCHED:
  207. cz = xasprintf("%d", *(int *)info);
  208. break;
  209. default:
  210. cz = xasprintf("%d", eventno + 1);
  211. break;
  212. }
  213. tprintf_append_mbs(&buf, cz, attributes);
  214. xfree(cz);
  215. break;
  216. case 'T': /* 24 hour format */
  217. case '@':
  218. case 't': /* 12 hour am/pm format */
  219. case 'p': /* With seconds */
  220. case 'P':
  221. {
  222. char ampm = 'a';
  223. int hr = t->tm_hour;
  224. /* addition by Hans J. Albertsson */
  225. /* and another adapted from Justin Bur */
  226. if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) {
  227. if (hr >= 12) {
  228. if (hr > 12)
  229. hr -= 12;
  230. ampm = 'p';
  231. }
  232. else if (hr == 0)
  233. hr = 12;
  234. } /* else do a 24 hour clock */
  235. /* "DING!" stuff by Hans also */
  236. if (t->tm_min || print_prompt_did_ding ||
  237. what != FMT_PROMPT || adrof(STRnoding)) {
  238. if (t->tm_min)
  239. print_prompt_did_ding = 0;
  240. /*
  241. * Pad hour to 2 characters if padhour is set,
  242. * by ADAM David Alan Martin
  243. */
  244. p = Itoa(hr, adrof(STRpadhour) ? 2 : 0, attributes);
  245. Strbuf_append(&buf, p);
  246. xfree(p);
  247. Strbuf_append1(&buf, attributes | ':');
  248. p = Itoa(t->tm_min, 2, attributes);
  249. Strbuf_append(&buf, p);
  250. xfree(p);
  251. if (*cp == 'p' || *cp == 'P') {
  252. Strbuf_append1(&buf, attributes | ':');
  253. p = Itoa(t->tm_sec, 2, attributes);
  254. Strbuf_append(&buf, p);
  255. xfree(p);
  256. }
  257. if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) {
  258. Strbuf_append1(&buf, attributes | ampm);
  259. Strbuf_append1(&buf, attributes | 'm');
  260. }
  261. }
  262. else { /* we need to ding */
  263. size_t i;
  264. for (i = 0; STRDING[i] != 0; i++)
  265. Strbuf_append1(&buf, attributes | STRDING[i]);
  266. print_prompt_did_ding = 1;
  267. }
  268. }
  269. break;
  270. case 'M':
  271. #ifndef HAVENOUTMP
  272. if (what == FMT_WHO)
  273. cz = who_info(info, 'M');
  274. else
  275. #endif /* HAVENOUTMP */
  276. cz = getenv("HOST");
  277. /*
  278. * Bug pointed out by Laurent Dami <dami@cui.unige.ch>: don't
  279. * derefrence that NULL (if HOST is not set)...
  280. */
  281. if (cz != NULL)
  282. tprintf_append_mbs(&buf, cz, attributes);
  283. if (what == FMT_WHO)
  284. xfree(cz);
  285. break;
  286. case 'm': {
  287. char *scz = NULL;
  288. #ifndef HAVENOUTMP
  289. if (what == FMT_WHO)
  290. scz = cz = who_info(info, 'm');
  291. else
  292. #endif /* HAVENOUTMP */
  293. cz = getenv("HOST");
  294. if (cz != NULL)
  295. while (*cz != 0 && (what == FMT_WHO || *cz != '.')) {
  296. Char wc;
  297. cz += one_mbtowc(&wc, cz, MB_LEN_MAX);
  298. Strbuf_append1(&buf, wc | attributes);
  299. }
  300. if (scz)
  301. xfree(scz);
  302. break;
  303. }
  304. /* lukem: new directory prompt code */
  305. case '~':
  306. case '/':
  307. case '.':
  308. case 'c':
  309. case 'C':
  310. Scp = *cp;
  311. if (Scp == 'c') /* store format type (c == .) */
  312. Scp = '.';
  313. if ((z = varval(STRcwd)) == STRNULL)
  314. break; /* no cwd, so don't do anything */
  315. /* show ~ whenever possible - a la dirs */
  316. if (Scp == '~' || Scp == '.' ) {
  317. static Char *olddir = NULL;
  318. if (tlength == 0 || olddir != z) {
  319. olddir = z; /* have we changed dir? */
  320. olduser = getusername(&olddir);
  321. }
  322. if (olduser)
  323. z = olddir;
  324. }
  325. updirs = pdirs = 0;
  326. /* option to determine fixed # of dirs from path */
  327. if (Scp == '.' || Scp == 'C') {
  328. int skip;
  329. #ifdef WINNT_NATIVE
  330. Char *oldz = z;
  331. if (z[1] == ':') {
  332. Strbuf_append1(&buf, attributes | *z++);
  333. Strbuf_append1(&buf, attributes | *z++);
  334. }
  335. if (*z == '/' && z[1] == '/') {
  336. Strbuf_append1(&buf, attributes | *z++);
  337. Strbuf_append1(&buf, attributes | *z++);
  338. do {
  339. Strbuf_append1(&buf, attributes | *z++);
  340. } while(*z != '/');
  341. }
  342. #endif /* WINNT_NATIVE */
  343. q = z;
  344. while (*z) /* calc # of /'s */
  345. if (*z++ == '/')
  346. updirs++;
  347. #ifdef WINNT_NATIVE
  348. /*
  349. * for format type c, prompt will be following...
  350. * c:/path => c:/path
  351. * c:/path/to => c:to
  352. * //machine/share => //machine/share
  353. * //machine/share/folder => //machine:folder
  354. */
  355. if (oldz[0] == '/' && oldz[1] == '/' && updirs > 1)
  356. Strbuf_append1(&buf, attributes | ':');
  357. #endif /* WINNT_NATIVE */
  358. if ((Scp == 'C' && *q != '/'))
  359. updirs++;
  360. if (cp[1] == '0') { /* print <x> or ... */
  361. pdirs = 1;
  362. cp++;
  363. }
  364. if (cp[1] >= '1' && cp[1] <= '9') { /* calc # to skip */
  365. skip = cp[1] - '0';
  366. cp++;
  367. }
  368. else
  369. skip = 1;
  370. updirs -= skip;
  371. while (skip-- > 0) {
  372. while ((z > q) && (*z != '/'))
  373. z--; /* back up */
  374. if (skip && z > q)
  375. z--;
  376. }
  377. if (*z == '/' && z != q)
  378. z++;
  379. } /* . || C */
  380. /* print ~[user] */
  381. if ((olduser) && ((Scp == '~') ||
  382. (Scp == '.' && (pdirs || (!pdirs && updirs <= 0))) )) {
  383. Strbuf_append1(&buf, attributes | '~');
  384. for (q = olduser; *q; q++)
  385. Strbuf_append1(&buf, attributes | *q);
  386. }
  387. /* RWM - tell you how many dirs we've ignored */
  388. /* and add '/' at front of this */
  389. if (updirs > 0 && pdirs) {
  390. if (adrof(STRellipsis)) {
  391. Strbuf_append1(&buf, attributes | '.');
  392. Strbuf_append1(&buf, attributes | '.');
  393. Strbuf_append1(&buf, attributes | '.');
  394. } else {
  395. Strbuf_append1(&buf, attributes | '/');
  396. Strbuf_append1(&buf, attributes | '<');
  397. if (updirs > 9) {
  398. Strbuf_append1(&buf, attributes | '9');
  399. Strbuf_append1(&buf, attributes | '+');
  400. } else
  401. Strbuf_append1(&buf, attributes | ('0' + updirs));
  402. Strbuf_append1(&buf, attributes | '>');
  403. }
  404. }
  405. while (*z)
  406. Strbuf_append1(&buf, attributes | *z++);
  407. break;
  408. /* lukem: end of new directory prompt code */
  409. case 'n':
  410. #ifndef HAVENOUTMP
  411. if (what == FMT_WHO) {
  412. cz = who_info(info, 'n');
  413. tprintf_append_mbs(&buf, cz, attributes);
  414. xfree(cz);
  415. }
  416. else
  417. #endif /* HAVENOUTMP */
  418. {
  419. if ((z = varval(STRuser)) != STRNULL)
  420. while (*z)
  421. Strbuf_append1(&buf, attributes | *z++);
  422. }
  423. break;
  424. case 'N':
  425. if ((z = varval(STReuser)) != STRNULL)
  426. while (*z)
  427. Strbuf_append1(&buf, attributes | *z++);
  428. break;
  429. case 'l':
  430. #ifndef HAVENOUTMP
  431. if (what == FMT_WHO) {
  432. cz = who_info(info, 'l');
  433. tprintf_append_mbs(&buf, cz, attributes);
  434. xfree(cz);
  435. }
  436. else
  437. #endif /* HAVENOUTMP */
  438. {
  439. if ((z = varval(STRtty)) != STRNULL)
  440. while (*z)
  441. Strbuf_append1(&buf, attributes | *z++);
  442. }
  443. break;
  444. case 'd':
  445. tprintf_append_mbs(&buf, day_list[t->tm_wday], attributes);
  446. break;
  447. case 'D':
  448. p = Itoa(t->tm_mday, 2, attributes);
  449. Strbuf_append(&buf, p);
  450. xfree(p);
  451. break;
  452. case 'w':
  453. tprintf_append_mbs(&buf, month_list[t->tm_mon], attributes);
  454. break;
  455. case 'W':
  456. p = Itoa(t->tm_mon + 1, 2, attributes);
  457. Strbuf_append(&buf, p);
  458. xfree(p);
  459. break;
  460. case 'y':
  461. p = Itoa(t->tm_year % 100, 2, attributes);
  462. Strbuf_append(&buf, p);
  463. xfree(p);
  464. break;
  465. case 'Y':
  466. p = Itoa(t->tm_year + 1900, 4, attributes);
  467. Strbuf_append(&buf, p);
  468. xfree(p);
  469. break;
  470. case 'S': /* start standout */
  471. attributes |= STANDOUT;
  472. break;
  473. case 'B': /* start bold */
  474. attributes |= BOLD;
  475. break;
  476. case 'U': /* start underline */
  477. attributes |= UNDER;
  478. break;
  479. case 's': /* end standout */
  480. attributes &= ~STANDOUT;
  481. break;
  482. case 'b': /* end bold */
  483. attributes &= ~BOLD;
  484. break;
  485. case 'u': /* end underline */
  486. attributes &= ~UNDER;
  487. break;
  488. case 'L':
  489. ClearToBottom();
  490. break;
  491. case 'j':
  492. {
  493. int njobs = -1;
  494. struct process *pp;
  495. for (pp = proclist.p_next; pp; pp = pp->p_next)
  496. njobs++;
  497. if (njobs == -1)
  498. njobs++;
  499. p = Itoa(njobs, 1, attributes);
  500. Strbuf_append(&buf, p);
  501. xfree(p);
  502. break;
  503. }
  504. case '?':
  505. if ((z = varval(STRstatus)) != STRNULL)
  506. while (*z)
  507. Strbuf_append1(&buf, attributes | *z++);
  508. break;
  509. case '$':
  510. expdollar(&buf, &cp, attributes);
  511. /* cp should point the last char of current % sequence */
  512. cp--;
  513. break;
  514. case '%':
  515. Strbuf_append1(&buf, attributes | '%');
  516. break;
  517. case '{': /* literal characters start */
  518. #if LITERAL == 0
  519. /*
  520. * No literal capability, so skip all chars in the literal
  521. * string
  522. */
  523. while (*cp != '\0' && (cp[-1] != '%' || *cp != '}'))
  524. cp++;
  525. #endif /* LITERAL == 0 */
  526. attributes |= LITERAL;
  527. break;
  528. case '}': /* literal characters end */
  529. attributes &= ~LITERAL;
  530. break;
  531. default:
  532. #ifndef HAVENOUTMP
  533. if (*cp == 'a' && what == FMT_WHO) {
  534. cz = who_info(info, 'a');
  535. tprintf_append_mbs(&buf, cz, attributes);
  536. xfree(cz);
  537. }
  538. else
  539. #endif /* HAVENOUTMP */
  540. {
  541. Strbuf_append1(&buf, attributes | '%');
  542. Strbuf_append1(&buf, attributes | *cp);
  543. }
  544. break;
  545. }
  546. }
  547. else if (*cp == '\\' || *cp == '^')
  548. Strbuf_append1(&buf, attributes | parseescape(&cp));
  549. else if (*cp == HIST) { /* EGS: handle '!'s in prompts */
  550. if (what == FMT_HISTORY)
  551. cz = fmthist('h', info);
  552. else
  553. cz = xasprintf("%d", eventno + 1);
  554. tprintf_append_mbs(&buf, cz, attributes);
  555. xfree(cz);
  556. }
  557. else
  558. Strbuf_append1(&buf, attributes | *cp); /* normal character */
  559. }
  560. cleanup_ignore(&buf);
  561. cleanup_until(&buf);
  562. return Strbuf_finish(&buf);
  563. }
  564. int
  565. expdollar(struct Strbuf *buf, const Char **srcp, Char attr)
  566. {
  567. struct varent *vp;
  568. const Char *src = *srcp;
  569. Char *var, *val;
  570. size_t i;
  571. int curly = 0;
  572. /* found a variable, expand it */
  573. var = xmalloc((Strlen(src) + 1) * sizeof (*var));
  574. for (i = 0; ; i++) {
  575. var[i] = *++src & TRIM;
  576. if (i == 0 && var[i] == '{') {
  577. curly = 1;
  578. var[i] = *++src & TRIM;
  579. }
  580. if (!alnum(var[i]) && var[i] != '_') {
  581. var[i] = '\0';
  582. break;
  583. }
  584. }
  585. if (curly && (*src & TRIM) == '}')
  586. src++;
  587. vp = adrof(var);
  588. if (vp && vp->vec) {
  589. for (i = 0; vp->vec[i] != NULL; i++) {
  590. for (val = vp->vec[i]; *val; val++)
  591. if (*val != '\n' && *val != '\r')
  592. Strbuf_append1(buf, *val | attr);
  593. if (vp->vec[i+1])
  594. Strbuf_append1(buf, ' ' | attr);
  595. }
  596. }
  597. else {
  598. val = (!vp) ? tgetenv(var) : NULL;
  599. if (val) {
  600. for (; *val; val++)
  601. if (*val != '\n' && *val != '\r')
  602. Strbuf_append1(buf, *val | attr);
  603. } else {
  604. *srcp = src;
  605. xfree(var);
  606. return 0;
  607. }
  608. }
  609. *srcp = src;
  610. xfree(var);
  611. return 1;
  612. }