home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume25 / pdksh / part04 < prev    next >
Text File  |  1991-11-12  |  56KB  |  2,637 lines

  1. Newsgroups: comp.sources.misc
  2. From: sjg@zen.void.oz.au (Simon J. Gerraty)
  3. Subject:  v25i050:  pdksh - Public Domain Korn Shell, v4, Part04/09
  4. Message-ID: <1991Nov13.031114.15918@sparky.imd.sterling.com>
  5. X-Md4-Signature: 39dc3d95402c3f04617242cea892a466
  6. Date: Wed, 13 Nov 1991 03:11:14 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: sjg@zen.void.oz.au (Simon J. Gerraty)
  10. Posting-number: Volume 25, Issue 50
  11. Archive-name: pdksh/part04
  12. Environment: UNIX
  13.  
  14. #! /bin/sh
  15. # into a shell via "sh file" or similar.  To overwrite existing files,
  16. # type "sh file -c".
  17. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  18. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  19. # Contents:  Changes.mlj sh/c_ksh.c sh/eval.c sh/lex.c sh/syn.c
  20. # Wrapped by kent@sparky on Tue Nov 12 20:44:32 1991
  21. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  22. echo If this archive is complete, you will see the following message:
  23. echo '          "shar: End of archive 4 (of 9)."'
  24. if test -f 'Changes.mlj' -a "${1}" != "-c" ; then 
  25.   echo shar: Will not clobber existing file \"'Changes.mlj'\"
  26. else
  27.   echo shar: Extracting \"'Changes.mlj'\" \(1777 characters\)
  28.   sed "s/^X//" >'Changes.mlj' <<'END_OF_FILE'
  29. XI got the pd-ksh from John MacMillan after he indicated that he
  30. Xhad a version of it that had vi editing (I'd seen various versions
  31. Xwith emacs-editing, but none with vi).
  32. X
  33. XIt had a few bugs and areas which were not quite complete.  I fixed
  34. X(or at least tried) to fix several; there are still some things
  35. Xwhich I plan on doing (or at least looking into).
  36. X
  37. XBugs fixed (or at least abated):
  38. X
  39. X    vi-mode changes:
  40. X    - Changed memcpy() to memmove(), which fixed the trashing of
  41. X      the end of the edit buffer while inserting in the middle
  42. X      of a line or with use of '#'
  43. X    - using 'r' replacing the current character with ^@
  44. X    - typing ctrl-c resulting in next command being garbled
  45. X    - lack of support for '-' and '+' (pretty trivial)
  46. X    - finish adding support for '*' (not entirely sure I'm freeing
  47. X      malloc'ed memory correctly here, but I haven't had any problems)
  48. X    - treats '_' as end of a word
  49. X
  50. X    general changes:
  51. X    - reporting "not found" when a file actually doesn't have
  52. X      the appropriate execute bit set (now says "cannot execute"
  53. X      or "not found", as appropriate)
  54. X
  55. X
  56. XStill to do:
  57. X
  58. X    vi changes:
  59. X    - fix ctrl-r (I've come up with a hack, but it involves
  60. X      redrawing the screen a lot when it isn't necessary; I
  61. X      really wouldn't consider this a fix)
  62. X    - add support for 'v'
  63. X
  64. X    general changes:
  65. X    - seems to be a memory leak when executing shells in the
  66. X      current shell; repeatedly executing ". /etc/profile"
  67. X      increased the size of the program as reported in the
  68. X      "SZ" field of "ps -l"
  69. X    - don't give a file its complete pathname in argv[0]; only
  70. X      its filename (religious issue?)
  71. X    - history recall should start at the previous command, not
  72. X      the current one (typing "r r" causes an infinite loop)
  73. END_OF_FILE
  74.   if test 1777 -ne `wc -c <'Changes.mlj'`; then
  75.     echo shar: \"'Changes.mlj'\" unpacked with wrong size!
  76.   fi
  77.   # end of 'Changes.mlj'
  78. fi
  79. if test -f 'sh/c_ksh.c' -a "${1}" != "-c" ; then 
  80.   echo shar: Will not clobber existing file \"'sh/c_ksh.c'\"
  81. else
  82.   echo shar: Extracting \"'sh/c_ksh.c'\" \(11631 characters\)
  83.   sed "s/^X//" >'sh/c_ksh.c' <<'END_OF_FILE'
  84. X/*
  85. X * built-in Korn commands: c_*
  86. X */
  87. X
  88. Xstatic char *RCSid = "$Id: c_ksh.c,v 3.4 89/03/27 15:47:16 egisin Exp $";
  89. X
  90. X#include <stddef.h>
  91. X#include <stdio.h>
  92. X#include <string.h>
  93. X#include <errno.h>
  94. X#include <setjmp.h>
  95. X#include "sh.h"
  96. X#include "table.h"
  97. X
  98. Xint
  99. Xc_hash(wp)
  100. X    register char **wp;
  101. X{
  102. X    register int i;
  103. X    register struct tbl *tp, **p;
  104. X
  105. X    wp++;
  106. X    if (*wp == NULL) {
  107. X        for (p = tsort(&commands); (tp = *p++) != NULL; )
  108. X            if ((tp->flag&ISSET))
  109. X                printf("%s\n", tp->val.s);
  110. X        return 0;
  111. X    }
  112. X
  113. X    if (strcmp(*wp, "-r") == 0)
  114. X        flushcom(1);
  115. X    while (*wp != NULL)
  116. X        findcom(*wp++, 1);
  117. X    return 0;
  118. X}
  119. X
  120. Xint
  121. Xc_cd(wp)
  122. X    register char **wp;
  123. X{
  124. X    char path [PATH];
  125. X    char newd [PATH];
  126. X    register char *cp;
  127. X    register char *dir;
  128. X    register char *cdpath;
  129. X    register char *rep;
  130. X    register char *pwd = NULL, *oldpwd = NULL;
  131. X    register int done = 0;
  132. X    register int prt = 0;
  133. X    register struct tbl *v_pwd = NULL, *v_oldpwd = NULL;
  134. X    extern Void cleanpath();
  135. X
  136. X    if ((dir = wp[1]) == NULL && (dir = strval(global("HOME"))) == NULL)
  137. X        errorf("no home directory");
  138. X
  139. X    v_pwd = global("PWD");
  140. X    if ((pwd = strval(v_pwd)) == null) {
  141. X        setstr(v_pwd, getcwd(path, (size_t)PATH));
  142. X        pwd = strval(v_pwd);
  143. X    }
  144. X
  145. X    if (wp[1] != NULL && (rep = wp[2]) != NULL) {
  146. X        /*
  147. X         * Two arg version: cd pat rep
  148. X         */
  149. X        if (strlen(pwd) - strlen(dir) + strlen(rep) >= PATH)
  150. X            errorf("substitution too long\n");
  151. X        cp = strstr(pwd, dir);
  152. X        if (cp == NULL)
  153. X            errorf("substitution failed\n");
  154. X        strncpy(path, pwd, cp - pwd);        /* first part */
  155. X        strcpy(path + (cp - pwd), rep);        /* replacement */
  156. X        strcat(path, cp + strlen(dir));     /* last part */
  157. X        dir = strsave(path, ATEMP);
  158. X        prt = 1;
  159. X    } else if (dir[0] == '-' && dir[1] == '\0') {
  160. X        /*
  161. X         * Change to previous dir: cd -
  162. X         */
  163. X        dir = strval(v_oldpwd = global("OLDPWD"));
  164. X        prt = 1;
  165. X    }
  166. X    if (dir[0] == '/' || (dir[0] == '.' && (dir[1] == '/' ||
  167. X        (dir[1] == '.' && dir[2] == '/')))) {
  168. X        /*
  169. X         * dir is an explicitly named path, so no CDPATH search
  170. X         */
  171. X        cleanpath(pwd, dir, newd);
  172. X        if (chdir(newd) < 0)
  173. X            errorf("%s: bad directory\n", newd);
  174. X        else if (prt)
  175. X            shellf("%s\n", newd);
  176. X        flushcom(0);
  177. X    } else {
  178. X        /*
  179. X         * search CDPATH for dir
  180. X         */
  181. X        cdpath = strval(global("CDPATH"));
  182. X        while ( !done && cdpath != NULL ) {
  183. X            cp = path;
  184. X            while (*cdpath && *cdpath != ':')
  185. X                *cp++ = *cdpath++;
  186. X            if (*cdpath == '\0')
  187. X                cdpath = NULL;
  188. X            else
  189. X                cdpath++;
  190. X            if (prt = (cp > path)) {
  191. X                *cp++ = '/';
  192. X                (void) strcpy( cp, dir );
  193. X                cp = path;
  194. X            } else
  195. X                cp = dir;
  196. X
  197. X            cleanpath(pwd, cp, newd);
  198. X            if (chdir(newd) == 0)
  199. X                done = 1;
  200. X        } while (!done && cdpath != NULL);
  201. X        if (!done)
  202. X            errorf("%s: bad directory\n", dir);
  203. X        if (prt)
  204. X            shellf("%s\n", newd);
  205. X        flushcom(0);
  206. X    }
  207. X
  208. X    /*
  209. X     * Keep track of OLDPWD and PWD
  210. X     */
  211. X    oldpwd = pwd;
  212. X    pwd = newd;
  213. X    if (!v_oldpwd)
  214. X        v_oldpwd = global("OLDPWD");
  215. X    if (oldpwd && *oldpwd)
  216. X        setstr(v_oldpwd, oldpwd);
  217. X    else
  218. X        unset(v_oldpwd);
  219. X    if (*pwd)
  220. X        setstr(v_pwd, pwd);
  221. X    else
  222. X        unset(v_pwd);
  223. X
  224. X    return 0;
  225. X}
  226. X
  227. Xint
  228. Xc_print(wp)
  229. X    register char **wp;
  230. X{
  231. X    int nl = 1;
  232. X    int expand = 1;
  233. X    FILE *f = stdout;
  234. X
  235. X    for (wp++; *wp != NULL && **wp == '-'; wp++) {
  236. X        register char *s = *wp + 1;
  237. X        if (*s == '\0') {
  238. X            wp++;
  239. X            break;
  240. X        }
  241. X        while (*s) switch (*s++) {
  242. X          case 'n':
  243. X            nl = 0;
  244. X            break;
  245. X          case 'e':
  246. X            expand = 1;
  247. X            break;
  248. X          case 'r':
  249. X            expand = 0;
  250. X            break;
  251. X          case 'u':
  252. X            if (!digit(*s) || (f = shf[*s++-'0']) == NULL)
  253. X                errorf("bad -u argument\n");
  254. X            break;
  255. X        }
  256. X    }
  257. X
  258. X    while (*wp != NULL) {
  259. X        register char *s = *wp;
  260. X        register int c;
  261. X        while ((c = *s++) != '\0')
  262. X            if (expand && c == '\\') {
  263. X                switch ((c = *s++)) {
  264. X                case 'b': c = '\b'; break;
  265. X                case 'c': nl = 0; continue; /* AT&T brain damage */
  266. X                case 'f': c = '\f'; break;
  267. X                case 'n': c = '\n'; break;
  268. X                case 'r': c = '\r'; break;
  269. X                case 't': c = '\t'; break;
  270. X                case 'v': c = 0x0B; break;
  271. X                case '0': case '1': case '2': case '3':
  272. X                case '4': case '5': case '6': case '7':
  273. X                    c = c - '0';
  274. X                    if (*s >= '0' && *s <= '7')
  275. X                        c = 8*c + *s++ - '0';
  276. X                    if (*s >= '0' && *s <= '7')
  277. X                        c = 8*c + *s++ - '0';
  278. X                    break;
  279. X                case '\\': break;
  280. X                default:
  281. X                    putc('\\', f);
  282. X                }
  283. X                putc(c, f);
  284. X            } else
  285. X                putc(c, f);
  286. X        if (*++wp != NULL)
  287. X            putc(' ', f);
  288. X    }
  289. X    if (nl)
  290. X        putc('\n', f);
  291. X    return 0;
  292. X}
  293. X
  294. X/* todo: handle case where id is both lexical and command */
  295. Xint
  296. Xc_whence(wp)
  297. X    register char **wp;
  298. X{
  299. X    register struct tbl *tp;
  300. X    char *id;
  301. X    int vflag = 0;
  302. X    int ret = 0;
  303. X
  304. X    for (wp++; (id = *wp) != NULL && *id == '-'; wp++)
  305. X        if (id[1] == 'v')
  306. X            vflag = 1;
  307. X
  308. X    while ((id = *wp++) != NULL) {
  309. X        tp = tsearch(&lexicals, id, hash(id));
  310. X        if (tp == NULL)
  311. X            tp = findcom(id, 0);
  312. X        if (vflag)
  313. X            switch ((tp == NULL) ? CNONE : tp->type) {
  314. X              case CNONE:
  315. X                printf("%s is unknown\n", id);
  316. X                ret = 1;
  317. X                break;
  318. X              case CSHELL:
  319. X                printf("%s is a shell builtin\n", id);
  320. X                break;
  321. X              case CFUNC:
  322. X                printf("%s is a function\n", id);
  323. X                fptreef(stdout, "function %s %T\n", id, tp->val.t);
  324. X                break;
  325. X              case CEXEC:
  326. X                printf("%s is %s\n", id,
  327. X                       (tp->flag&ISSET) ? tp->val.s : "unknown");
  328. X                if (!(tp->flag&ISSET))
  329. X                    ret = 1;
  330. X                break;
  331. X              case CALIAS:
  332. X                printf("%s is the alias '%s'\n", id, tp->val.s);
  333. X                break;
  334. X              case CKEYWD:
  335. X                printf("%s is a shell keyword\n", id);
  336. X                break;
  337. X              default:
  338. X                printf("%s is *GOK*\n", id);
  339. X                break;
  340. X            }
  341. X        else
  342. X            switch ((tp == NULL) ? CNONE : tp->type) {
  343. X              case CNONE:
  344. X                printf("\n");
  345. X                ret = 1;
  346. X                break;
  347. X              case CSHELL:
  348. X                printf("builtin %s\n", id);
  349. X                break;
  350. X              case CFUNC:
  351. X                printf("%s\n", id);
  352. X                break;
  353. X              case CEXEC:
  354. X                printf("%s\n", (tp->flag&ISSET) ? tp->val.s : "");
  355. X                if (!(tp->flag&ISSET))
  356. X                    ret = 1;
  357. X                break;
  358. X              case CALIAS:
  359. X                printf("%s\n", tp->val.s);
  360. X                break;
  361. X              case CKEYWD:
  362. X                printf("%s\n", id);
  363. X                break;
  364. X              default:
  365. X                printf("*GOK*\n");
  366. X                break;
  367. X            }
  368. X    }
  369. X    return ret;
  370. X}
  371. X
  372. X/* typeset, export, and readonly */
  373. Xint
  374. Xc_typeset(wp)
  375. X    register char **wp;
  376. X{
  377. X    register char *id;
  378. X    struct block *l = e.loc;
  379. X    register struct tbl *vp, **p;
  380. X    int fset = 0, fclr = 0;
  381. X    int thing = 0, func = 0, local = 0;
  382. X
  383. X    switch (**wp) {
  384. X      case 'e':        /* export */
  385. X        fset |= EXPORT;
  386. X        break;
  387. X      case 'r':        /* readonly */
  388. X        fset |= RDONLY;
  389. X        break;
  390. X      case 't':        /* typeset */
  391. X        local = 1;
  392. X        break;
  393. X    }
  394. X
  395. X    for (wp++; (id = *wp) != NULL && (*id == '-' || *id == '+'); wp++) {
  396. X        int flag = 0;
  397. X        thing = *id;
  398. X        while (*++id != '\0') switch (*id) {
  399. X          case 'f':
  400. X            flag |= FUNCT;
  401. X            func = 1;
  402. X            break;
  403. X          case 'i':
  404. X            flag |= INTEGER;
  405. X            break;
  406. X          case 'r':
  407. X            flag |= RDONLY;
  408. X            break;
  409. X          case 'x':
  410. X            flag |= EXPORT;
  411. X            break;
  412. X          case 't':
  413. X            flag |= TRACE;
  414. X            break;
  415. X          default:
  416. X            errorf("unknown flag -%c\n", *id);
  417. X        }
  418. X        if (flag != 0) { /* + or - with options */
  419. X            if (thing == '-')
  420. X                fset |= flag;
  421. X            else
  422. X                fclr |= flag;
  423. X            thing = 0;
  424. X        }
  425. X    }
  426. X
  427. X    /* list variables and attributes */
  428. X    if (*wp == NULL) {
  429. X        for (l = e.loc; l != NULL; l = l->next) {
  430. X            for (p = tsort((func==0) ? &l->vars : &l->funs);
  431. X             (vp = *p++) != NULL; )
  432. X            if ((vp->flag&ISSET))
  433. X                if (thing == 0 && fclr == 0 && fset == 0) {
  434. X                printf("typeset ");
  435. X                if ((vp->flag&INTEGER))
  436. X                    printf("-i ");
  437. X                if ((vp->flag&EXPORT))
  438. X                    printf("-x ");
  439. X                if ((vp->flag&RDONLY))
  440. X                    printf("-r ");
  441. X                if ((vp->flag&TRACE)) 
  442. X                    printf("-t ");
  443. X                printf("%s\n", vp->name);
  444. X                } else
  445. X                if (thing == '+' ||
  446. X                fclr && (vp->flag&fclr) == fclr) {
  447. X                printf("%s\n", vp->name);
  448. X                } else
  449. X                if (thing == '-' ||
  450. X                fset && (vp->flag&fset) == fset) {
  451. X                if (fset&FUNCT)
  452. X                    printf("function %s\n", vp->name);
  453. X                else
  454. X                    printf("%s=%s\n", vp->name, strval(vp));
  455. X                }
  456. X        }
  457. X        return (0);
  458. X    }
  459. X
  460. X    if (local)
  461. X        fset |= LOCAL;
  462. X    for (; *wp != NULL; wp++)
  463. X#if 0
  464. X        if (func) {
  465. X        } else
  466. X#endif
  467. X        if (typeset(*wp, fset, fclr) == NULL)
  468. X            errorf("%s: not identifier\n", *wp);
  469. X    return 0;
  470. X}
  471. X    
  472. Xint
  473. Xc_alias(wp)
  474. X    register char **wp;
  475. X{
  476. X    register struct table *t = &lexicals;
  477. X    register struct tbl *ap, **p;
  478. X    register int i;
  479. X    int rv = 0;
  480. X
  481. X    if (*++wp != NULL && strcmp(*wp, "-d") == 0) {
  482. X        t = &homedirs;
  483. X        wp++;
  484. X    }
  485. X
  486. X    if (*wp == NULL)
  487. X        for (p = tsort(t); (ap = *p++) != NULL; )
  488. X            if (ap->type == CALIAS && (ap->flag&DEFINED))
  489. X                printf("%s='%s'\n", ap->name, ap->val.s);
  490. X
  491. X    for (; *wp != NULL; wp++) {
  492. X        register char *id = *wp;
  493. X        register char *val = strchr(id, '=');
  494. X
  495. X        if (val == NULL) {
  496. X            ap = tsearch(t, id, hash(id));
  497. X            if (ap != NULL && ap->type == CALIAS && (ap->flag&DEFINED))
  498. X                printf("%s='%s'\n", ap->name, ap->val.s);
  499. X            else
  500. X                rv = 1;
  501. X        } else {
  502. X            *val++ = '\0';
  503. X            ap = tenter(t, id, hash(id));
  504. X            if (ap->type == CKEYWD)
  505. X                errorf("cannot alias keyword\n");
  506. X            if ((ap->flag&ALLOC)) {
  507. X                afree((Void*)ap->val.s, APERM);
  508. X                ap->flag &= ~(ALLOC|ISSET);
  509. X            }
  510. X            ap->type = CALIAS;
  511. X            ap->val.s = strsave(val, APERM);
  512. X            ap->flag |= DEFINED|ALLOC|ISSET;
  513. X        }
  514. X    }
  515. X    return rv;
  516. X}
  517. X
  518. Xint
  519. Xc_unalias(wp)
  520. X    register char **wp;
  521. X{
  522. X    register struct table *t = &lexicals;
  523. X    register struct tbl *ap;
  524. X
  525. X    if (*++wp != NULL && strcmp(*wp, "-d") == 0) {
  526. X        t = &homedirs;
  527. X        wp++;
  528. X    }
  529. X
  530. X    for (; *wp != NULL; wp++) {
  531. X        ap = tsearch(t, *wp, hash(*wp));
  532. X        if (ap == NULL || ap->type != CALIAS)
  533. X            continue;
  534. X        if ((ap->flag&ALLOC))
  535. X            afree((Void*)ap->val.s, APERM);
  536. X        ap->flag &= ~(DEFINED|ISSET|ALLOC);
  537. X    }
  538. X    return 0;
  539. X}
  540. X
  541. Xint
  542. Xc_let(wp)
  543. X    char **wp;
  544. X{
  545. X    int rv = 1;
  546. X
  547. X    for (wp++; *wp; wp++)
  548. X        rv = evaluate(*wp) == 0;
  549. X    return rv;
  550. X}
  551. X
  552. Xint
  553. Xc_jobs(wp)
  554. X    char **wp;
  555. X{
  556. X    j_jobs();
  557. X    return 0;
  558. X}
  559. X
  560. X#ifdef JOBS
  561. Xint
  562. Xc_fgbg(wp)
  563. X    register char **wp;
  564. X{
  565. X    int bg = strcmp(*wp, "bg") == 0;
  566. X
  567. X    if (!flag[FMONITOR])
  568. X        errorf("Job control not enabled\n");
  569. X    wp++;
  570. X    j_resume(j_lookup((*wp == NULL) ? "%" : *wp), bg);
  571. X    return 0;
  572. X}
  573. X#endif
  574. X
  575. Xint
  576. Xc_kill(wp)
  577. X    register char **wp;
  578. X{
  579. X    register char *cp;
  580. X    int sig = 15;        /* SIGTERM */
  581. X    int rv = 0;
  582. X    int n;
  583. X    int gotsig = FALSE;
  584. X
  585. X    if (*++wp == NULL)
  586. X        errorf("Usage: kill [-l] [-signal] {pid|job} ...\n");
  587. X    if (strcmp(*wp, "-l") == 0) {
  588. X        register struct trap *p = sigtraps;
  589. X        for (sig = 0; sig < SIGNALS; sig++, p++)
  590. X            if (p->signal)
  591. X                printf("%2d %8s %s\n", p->signal, p->name, p->mess);
  592. X        return 0;
  593. X    }
  594. X
  595. X    for (; (cp = *wp) != NULL; wp++)
  596. X        if (*cp == '-' && gotsig == FALSE && *(wp+1) != NULL) {
  597. X            struct trap *p;
  598. X            gotsig = FALSE;
  599. X            if (digit(*(cp+1))) {
  600. X                if ((n = atoi(cp+1)) < SIGNALS) {
  601. X                    sig = n;
  602. X                    gotsig = TRUE;
  603. X                } else if (kill(n, sig) < 0) {
  604. X                    shellf("%s: %s\n", cp, strerror(errno));
  605. X                    rv++;
  606. X                }
  607. X            } else {
  608. X                p = gettrap(cp+1);
  609. X                if (p == NULL)
  610. X                    errorf("bad signal %s\n", cp+1);
  611. X                sig = p->signal;
  612. X                gotsig = TRUE;
  613. X            }
  614. X        } else {
  615. X            gotsig = FALSE;
  616. X            if (digit(*cp) || (*cp == '-' && digit(*(cp+1)))) {
  617. X                if (kill(atoi(cp), sig) < 0) {
  618. X                    shellf("%s: %s\n", cp, strerror(errno));
  619. X                    rv++;
  620. X                }
  621. X            } else
  622. X            if (*cp == '%')
  623. X                j_kill(j_lookup(cp), sig);
  624. X            else
  625. X                errorf("invalid argument\n");
  626. X        }
  627. X    return rv;
  628. X}
  629. X
  630. Xint
  631. Xc_bind(wp)
  632. X    register char **wp;
  633. X{
  634. X    int macro = 0;
  635. X    register char *cp;
  636. X
  637. X    for (wp++; (cp = *wp) != NULL && *cp == '-'; wp++)
  638. X        if (cp[1] == 'm')
  639. X            macro = 1;
  640. X
  641. X    if (*wp == NULL)    /* list all */
  642. X        x_bind((char*)NULL, (char*)NULL, 0);
  643. X
  644. X    for (; *wp != NULL; wp++) {
  645. X        cp = strchr(*wp, '=');
  646. X        if (cp != NULL)
  647. X            *cp++ = 0;
  648. X        x_bind(*wp, cp, macro);
  649. X    }
  650. X
  651. X    return 0;
  652. X}
  653. X
  654. Xextern    c_fc();
  655. Xextern    c_getopts();
  656. X
  657. XConst struct builtin kshbuiltins [] = {
  658. X    {"cd", c_cd},
  659. X    {"print", c_print},
  660. X    {"getopts", c_getopts},
  661. X    {"=typeset", c_typeset},
  662. X    {"=export", c_typeset},
  663. X    {"=readonly", c_typeset},
  664. X    {"whence", c_whence},
  665. X    {"=alias", c_alias},
  666. X    {"unalias", c_unalias},
  667. X    {"hash", c_hash},
  668. X    {"let", c_let},
  669. X    {"fc", c_fc},
  670. X    {"jobs", c_jobs},
  671. X    {"kill", c_kill},
  672. X#ifdef JOBS
  673. X    {"fg", c_fgbg},
  674. X    {"bg", c_fgbg},
  675. X#endif
  676. X#ifdef EMACS
  677. X    {"bind", c_bind},
  678. X#endif
  679. X    {NULL, NULL}
  680. X};
  681. X
  682. END_OF_FILE
  683.   if test 11631 -ne `wc -c <'sh/c_ksh.c'`; then
  684.     echo shar: \"'sh/c_ksh.c'\" unpacked with wrong size!
  685.   fi
  686.   # end of 'sh/c_ksh.c'
  687. fi
  688. if test -f 'sh/eval.c' -a "${1}" != "-c" ; then 
  689.   echo shar: Will not clobber existing file \"'sh/eval.c'\"
  690. else
  691.   echo shar: Extracting \"'sh/eval.c'\" \(14245 characters\)
  692.   sed "s/^X//" >'sh/eval.c' <<'END_OF_FILE'
  693. X/*
  694. X * Expansion - quoting, separation, substitution, globbing
  695. X */
  696. X
  697. Xstatic char *RCSid = "$Id: eval.c,v 3.4 89/03/27 15:49:55 egisin Exp $";
  698. X
  699. X#include <stddef.h>
  700. X#include <stdio.h>
  701. X#include <string.h>
  702. X#include <errno.h>
  703. X#include <setjmp.h>
  704. X#include <unistd.h>
  705. X#include <sys/types.h>
  706. X#include <dirent.h>
  707. X#include <pwd.h>
  708. X#include "sh.h"
  709. X#include "lex.h"
  710. X#include "tree.h"
  711. X#include "table.h"
  712. X#include "expand.h"
  713. X
  714. X/*
  715. X * string expansion
  716. X *
  717. X * first pass: quoting, IFS separation, ${} and $() substitution.
  718. X * second pass: filename expansion (*?[]~).
  719. X */
  720. X
  721. X/* expansion generator state */
  722. Xtypedef struct Expand {
  723. X    /* int  type; */    /* see expand() */
  724. X    char   *str;        /* string */
  725. X    union {
  726. X        char  **strv;    /* string[] */
  727. X        FILE   *file;    /* file */
  728. X    } u;            /* source */
  729. X    short    split;        /* split "$@"*/
  730. X} Expand;
  731. X
  732. X#define    XBASE    0        /* scanning original */
  733. X#define    XSUB    1        /* expanding ${} string */
  734. X#define    XARGSEP    2        /* ifs0 between "$@" */
  735. X#define    XARG    3        /* expanding $*, $@ */
  736. X#define    XCOM    4        /* expanding $() */
  737. X
  738. Xstatic    void    expand ARGS((char *, XPtrV *, int));
  739. Xstatic    int    comsub ARGS((Expand *, char *comm));
  740. Xstatic    int    varsub ARGS((Expand *, char *name, int stype));
  741. Xstatic    void    glob ARGS((char *cp, XPtrV *wp));
  742. Xstatic    void    globit ARGS((char *ds, char *dp, char *sp, XPtrV *wp, int check));
  743. Xstatic    char   *tilde ARGS((char *wp));
  744. Xstatic    char   *trimsub ARGS((char *str, char *pat, int how));
  745. X
  746. Xint    ifs0 = ' ';        /* todo: first char of $IFS */
  747. X
  748. X/* compile and expand word */
  749. Xchar *
  750. Xsubstitute(cp, f)
  751. X    char Const *cp;
  752. X    int f;
  753. X{
  754. X    struct source *s, *sold;
  755. X
  756. X    sold = source;
  757. X    s = pushs(SWSTR);
  758. X    s->str = (char *) cp;
  759. X    source = s;
  760. X    if (yylex(ONEWORD) != LWORD)
  761. X        errorf("eval:substitute error\n");
  762. X    source = sold;
  763. X    return evalstr(yylval.cp, f);
  764. X}
  765. X
  766. X/*
  767. X * expand arg-list
  768. X */
  769. Xchar **
  770. Xeval(ap, f)
  771. X    register char **ap;
  772. X{
  773. X    XPtrV w;
  774. X
  775. X    if (*ap == NULL)
  776. X        return ap;
  777. X    XPinit(w, 32);
  778. X    XPput(w, NULL);        /* space for shell name */
  779. X#ifdef    SHARPBANG
  780. X    XPput(w, NULL);        /* and space for one arg */
  781. X#endif
  782. X    while (*ap != NULL)
  783. X        expand(*ap++, &w, f);
  784. X    XPput(w, NULL);
  785. X#ifdef    SHARPBANG
  786. X    return (char **) XPclose(w) + 2;
  787. X#else
  788. X    return (char **) XPclose(w) + 1;
  789. X#endif
  790. X}
  791. X
  792. X/*
  793. X * expand string
  794. X */
  795. Xchar *
  796. Xevalstr(cp, f)
  797. X    register char *cp;
  798. X    int f;
  799. X{
  800. X    XPtrV w;
  801. X
  802. X    XPinit(w, 1);
  803. X    expand(cp, &w, f);
  804. X    cp = (XPsize(w) == 0) ? "" : (char*) *XPptrv(w);
  805. X    XPfree(w);
  806. X    return cp;
  807. X}
  808. X
  809. X/* for nested substitution: ${var:=$var2} */
  810. Xtypedef struct SubType {
  811. X    short    type;        /* [=+-?%#] action after expanded word */
  812. X    short    base;        /* begin position of expanded word */
  813. X    char   *name;        /* name for ${var=word} */
  814. X} SubType;
  815. X
  816. Xstatic void
  817. Xexpand(cp, wp, f)
  818. X    char *cp;        /* input word */
  819. X    register XPtrV *wp;    /* output words */
  820. X    int f;            /* DO* flags */
  821. X{
  822. X    register int c;
  823. X    register int type = XBASE; /* expansion type */
  824. X    register int quote = 0;    /* quoted */
  825. X    int quotestack[11];    /* Keep this bigger than the subtype stack */
  826. X    register int *qst = quotestack + 11;    /* This too, of course */
  827. X    XString ds;        /* destination string */
  828. X    register char *dp, *sp;    /* dest., source */
  829. X    int fdo, word, combase;    /* second pass flags; have word */
  830. X    Expand x;        /* expansion variables */
  831. X    SubType subtype [10];    /* substitution type stack */
  832. X    register SubType *st = subtype + 10;
  833. X    int newlines;        /* For trailing newlines in COMSUB */
  834. X
  835. X    if (cp == NULL)
  836. X        errorf("eval:expand(NULL)\n");
  837. X    if (flag[FNOGLOB])
  838. X        f &= ~ DOGLOB;
  839. X
  840. X    Xinit(ds, dp, 128);    /* init dest. string */
  841. X    type = XBASE;
  842. X    sp = cp;
  843. X    fdo = 0;
  844. X    word = !(f&DOBLANK);
  845. X
  846. X    while (1) {
  847. X        Xcheck(ds, dp);
  848. X
  849. X        switch (type) {
  850. X          case XBASE:    /* original prefixed string */
  851. X            c = *sp++;
  852. X            switch (c) {
  853. X              case EOS:
  854. X                c = 0;
  855. X                break;
  856. X              case CHAR:
  857. X                c = *sp++;
  858. X                break;
  859. X              case QCHAR:
  860. X                quote |= 2; /* temporary quote */
  861. X                c = *sp++;
  862. X                break;
  863. X              case OQUOTE:
  864. X                word = quote = 1;
  865. X                continue;
  866. X              case CQUOTE:
  867. X                quote = 0;
  868. X                continue;
  869. X              case COMSUB:
  870. X                type = comsub(&x, sp);
  871. X                sp = strchr(sp, 0) + 1;
  872. X                combase = Xsavepos(ds, dp);
  873. X                newlines = 0;
  874. X                continue;
  875. X              case OSUBST: /* ${var{:}[=+-?]word} */
  876. X                cp = sp;         /* variable */
  877. X                sp = strchr(sp, 0) + 1;    /* skip variable */
  878. X                c = (*sp == CSUBST) ? 0 : *sp++;
  879. X                if ((c&0x7F) == '#' || (c&0x7F) == '%') {
  880. X                    if (flag[FNOUNSET] &&
  881. X                        strval(global(cp)) == null)
  882. X                        errorf("%s: unset variable\n", cp);
  883. X                    f |= DOPAT;
  884. X                    type = XBASE;
  885. X                    *--qst = quote;
  886. X                    quote = 0;
  887. X                } else
  888. X                    type = varsub(&x, cp, c);
  889. X                if (type == XBASE) {    /* expand? */
  890. X                    if (st == subtype)
  891. X                        errorf("ridiculous ${} nesting\n");
  892. X                    --st;
  893. X                    st->type = c;
  894. X                    st->base = Xsavepos(ds, dp);
  895. X                    st->name = cp;
  896. X                } else
  897. X                    sp = wdscan(sp, CSUBST); /* skip word */
  898. X                continue;
  899. X              case CSUBST: /* only get here if expanding word */
  900. X                *dp = 0;
  901. X                if (f&DOGLOB)
  902. X                    f &= ~DOPAT;
  903. X                switch (st->type&0x7F) {
  904. X                  case '#':
  905. X                  case '%':
  906. X                    *dp = 0;
  907. X                    dp = Xrestpos(ds, dp, st->base);
  908. X                    quote = *qst++;
  909. X                    x.str = trimsub(strval(global(st->name)),
  910. X                        dp, st->type);
  911. X                    type = XSUB;
  912. X                    continue;
  913. X                  case '=':
  914. X#if 0
  915. X                    if ((x.u.vp->flag&RDONLY))
  916. X                        errorf("cannot set readonly %s\n", cp);
  917. X#endif
  918. X                    setstr(global(st->name), Xrestpos(ds, dp, st->base));
  919. X                    break;
  920. X                  case '?':
  921. X                    if (dp == Xrestpos(ds, dp, st->base))
  922. X                        errorf("missing value for %s\n", cp);
  923. X                    else
  924. X                        errorf("%s\n", Xrestpos(ds, dp, st->base));
  925. X                }
  926. X                st++;
  927. X                type = XBASE;
  928. X                continue;
  929. X            }
  930. X            break;
  931. X
  932. X          case XSUB:
  933. X            if ((c = *x.str++) == 0) {
  934. X                type = XBASE;
  935. X                continue;
  936. X            }
  937. X            break;
  938. X
  939. X          case XARGSEP:
  940. X            type = XARG;
  941. X            quote = 1;
  942. X          case XARG:
  943. X            if ((c = *x.str++) == 0) {
  944. X                if ((x.str = *x.u.strv++) == NULL) {
  945. X                    type = XBASE;
  946. X                    continue;
  947. X                } else if (quote && x.split) {
  948. X                    /* terminate word for "$@" */
  949. X                    type = XARGSEP;
  950. X                    quote = 0;
  951. X                }
  952. X                c = ifs0;
  953. X            }
  954. X            break;
  955. X
  956. X          case XCOM:
  957. X            if (newlines) {        /* Spit out saved nl's */
  958. X                c = '\n';
  959. X                --newlines;
  960. X            } else {
  961. X                while ((c = getc(x.u.file)) == '\n')
  962. X                    newlines++;    /* Save newlines */
  963. X                if (newlines && c != EOF) {
  964. X                    ungetc(c, x.u.file);
  965. X                    c = '\n';
  966. X                    --newlines;
  967. X                }
  968. X            }
  969. X            if (c == EOF) {
  970. X                cp = Xrestpos(ds, sp, combase);
  971. X                newlines = 0;
  972. X                fclose(x.u.file);
  973. X                if (x.split)
  974. X                    waitlast();
  975. X                type = XBASE;
  976. X                continue;
  977. X            }
  978. X            break;
  979. X        }
  980. X
  981. X        /* check for end of word or IFS separation */
  982. X        if (c == 0 || !quote && (f&DOBLANK) && ctype(c, C_IFS)) {
  983. X            if (word) {
  984. X                *dp++ = 0;
  985. X                cp = Xclose(ds, dp);
  986. X                if (fdo&DOTILDE)
  987. X                    cp = tilde(cp);
  988. X                if (fdo&DOGLOB)
  989. X                    glob(cp, wp);
  990. X                else
  991. X                    {XPput(*wp, cp);}
  992. X                fdo = word = 0;
  993. X                if (c != 0)
  994. X                    Xinit(ds, dp, 128);
  995. X            } else
  996. X                ; /* ignore IFS */
  997. X            if (c == 0)
  998. X                return;
  999. X        } else {
  1000. X            /* mark any special second pass chars */
  1001. X            if (!quote)
  1002. X                switch (c) {
  1003. X                  case '*':
  1004. X                  case '?':
  1005. X                  case '[':
  1006. X                    if (f&(DOPAT|DOGLOB)) {
  1007. X                        fdo |= (f&DOGLOB);
  1008. X                        *dp++ = MAGIC;
  1009. X                    }
  1010. X                    break;
  1011. X                  case NOT:
  1012. X                    if ((f&(DOPAT|DOGLOB)) &&
  1013. X                        dp[-1] == '[' && dp[-2] == MAGIC) {
  1014. X                        *dp++ = MAGIC;
  1015. X                    }
  1016. X                    break;
  1017. X                  case '~':
  1018. X                    if ((f&DOTILDE) && dp == Xstring(ds, dp) ||
  1019. X                        !(f&DOBLANK) && 
  1020. X                        (dp[-1] == '=' || dp[-1] == ':')) {
  1021. X                        fdo |= DOTILDE;
  1022. X                        *dp++ = MAGIC;
  1023. X                    }
  1024. X                    break;
  1025. X                }
  1026. X            else
  1027. X                quote &= ~2; /* undo temporary */
  1028. X
  1029. X            word = 1;
  1030. X            *dp++ = c; /* save output char */
  1031. X        }
  1032. X    }
  1033. X}
  1034. X
  1035. X/*
  1036. X * Prepare to generate the string returned by ${} substitution.
  1037. X */
  1038. Xstatic int
  1039. Xvarsub(xp, sp, stype)
  1040. X    register Expand *xp;
  1041. X    register char *sp;
  1042. X    int stype;
  1043. X{
  1044. X    register int c;
  1045. X    int type;
  1046. X
  1047. X    /* ${#var}, string length or argc */
  1048. X    if (sp[0] == '#' && (c = sp[1]) != 0) {
  1049. X        c = (c == '*' || c == '@') ? e.loc->argc :
  1050. X            strlen(strval(global(sp+1)));
  1051. X        xp->str = strsave(ulton((unsigned long)c, 10), ATEMP);
  1052. X        return XSUB;
  1053. X    }
  1054. X
  1055. X    c = sp[0];
  1056. X    if (c == '*' || c == '@') {
  1057. X        if (e.loc->argc == 0) {
  1058. X            xp->str = null;
  1059. X            type = XSUB;
  1060. X        } else {
  1061. X            xp->u.strv = e.loc->argv + 1;
  1062. X            xp->str = *xp->u.strv++;
  1063. X            xp->split = c == '@'; /* $@ */
  1064. X            type = XARG;
  1065. X        }
  1066. X    } else {
  1067. X        xp->str = strval(global(sp));
  1068. X        type = XSUB;
  1069. X    }
  1070. X
  1071. X    c = stype&0x7F;
  1072. X    /* test the compiler's code generator */
  1073. X    if (c == '%' || c == '#' ||
  1074. X        (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
  1075. X         c == '=' || c == '-' || c == '?' : c == '+'))
  1076. X        type = XBASE;    /* expand word instead of variable value */
  1077. X    if (type != XBASE && flag[FNOUNSET] && xp->str == null && c != '+')
  1078. X        errorf("%s: unset variable\n", sp);
  1079. X    return type;
  1080. X}
  1081. X
  1082. X/*
  1083. X * Run the command in $(...) and read its output.
  1084. X */
  1085. Xstatic int
  1086. Xcomsub(xp, cp)
  1087. X    register Expand *xp;
  1088. X    char *cp;
  1089. X{
  1090. X    Source *s;
  1091. X    register struct op *t;
  1092. X    FILE *fi;
  1093. X
  1094. X    s = pushs(SSTRING);
  1095. X    s->str = cp;
  1096. X    t = compile(s);
  1097. X
  1098. X    if (t != NULL && t->type == TCOM && /* $(<file) */
  1099. X        *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
  1100. X        register struct ioword *io = *t->ioact;
  1101. X
  1102. X        if ((io->flag&IOTYPE) != IOREAD)
  1103. X            errorf("funny $() command\n");
  1104. X        fi = fopen(evalstr(io->name, DOTILDE), "r");
  1105. X        if (fi != NULL)
  1106. X            fileno(fi) = savefd(fileno(fi));
  1107. X        xp->split = 0;    /* no waitlast() */
  1108. X    } else {
  1109. X        int ofd1, pv[2];
  1110. X        openpipe(pv);
  1111. X        fi = fdopen(pv[0], "r");
  1112. X        ofd1 = savefd(1);
  1113. X        dup2(pv[1], 1);
  1114. X        close(pv[1]);
  1115. X#if 0
  1116. X        exchild(t, XXCOM|XPIPEO);
  1117. X#else
  1118. X        execute(t, XFORK|XXCOM|XPIPEO);
  1119. X#endif
  1120. X        dup2(ofd1, 1);
  1121. X        close(ofd1);
  1122. X        xp->split = 1;    /* waitlast() */
  1123. X    }    
  1124. X
  1125. X    if (fi == NULL)
  1126. X        errorf("cannot open $() input\n");
  1127. X    setvbuf(fi, (char *)NULL, _IOFBF, BUFSIZ);
  1128. X    xp->u.file = fi;
  1129. X    return XCOM;
  1130. X}
  1131. X
  1132. X/*
  1133. X * perform #pattern and %pattern substitution in ${}
  1134. X */
  1135. X
  1136. Xstatic char *
  1137. Xtrimsub(str, pat, how)
  1138. X    register char *str;
  1139. X    char *pat;
  1140. X    int how;
  1141. X{
  1142. X    register char *end = strchr(str, 0);
  1143. X    register char *p, c, *match;
  1144. X
  1145. X    switch (how&0xff) {    /* UCHAR_MAX maybe? */
  1146. X    case '#':        /* shortest at begin */
  1147. X        for (p = str; p <= end; p++) {
  1148. X            c = *p; *p = '\0';
  1149. X            if (gmatch(str, pat)) {
  1150. X                *p = c;
  1151. X                return p;
  1152. X            }
  1153. X            *p = c;
  1154. X        }
  1155. X        break;
  1156. X    case '#'|0x80:        /* longest match at begin */
  1157. X        for (p = end; p >= str; p--) {
  1158. X            c = *p; *p = '\0';
  1159. X            if (gmatch(str, pat)) {
  1160. X                *p = c;
  1161. X                return p;
  1162. X            }
  1163. X            *p = c;
  1164. X        }
  1165. X        break;
  1166. X    case '%':        /* shortest match at end */
  1167. X        for (p = end; p >= str; p--) {
  1168. X            if (gmatch(p, pat)) {
  1169. X                c = *p; *p = '\0';
  1170. X                match = strsave( str, APERM );    /* APERM? */
  1171. X                *p = c;
  1172. X                return match;
  1173. X            }
  1174. X        }
  1175. X        break;
  1176. X    case '%'|0x80:    /* longest match at end */
  1177. X        for (p = str; p <= end; p++) {
  1178. X            if (gmatch(p, pat)) {
  1179. X                c = *p; *p = '\0';
  1180. X                match = strsave( str, ATEMP );    /* APERM? */
  1181. X                *p = c;
  1182. X                return match;
  1183. X            }
  1184. X        }
  1185. X        break;
  1186. X    }
  1187. X
  1188. X    return str;        /* no match, return string */
  1189. X}
  1190. X
  1191. X/*
  1192. X * glob
  1193. X * Name derived from V6's /etc/glob, the program that expanded filenames.
  1194. X */
  1195. X
  1196. Xstatic    char   *debunk();
  1197. X
  1198. Xstatic void 
  1199. Xglob(cp, wp)
  1200. X    char *cp;
  1201. X    register XPtrV *wp;
  1202. X{
  1203. X    char path [PATH];
  1204. X    register char *sp = cp;
  1205. X    int oldsize;
  1206. X
  1207. X    oldsize = XPsize(*wp);
  1208. X    globit(path, path, sp, wp, 0);
  1209. X
  1210. X    if (XPsize(*wp) == oldsize)
  1211. X        {XPput(*wp, debunk(cp));}
  1212. X    else
  1213. X        qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), xstrcmp);
  1214. X}
  1215. X
  1216. Xstatic void
  1217. Xglobit(ds, dp, sp, wp, check)
  1218. X    char *ds;        /* dest path */
  1219. X    char *dp;        /* dest end */
  1220. X    char *sp;        /* source path */
  1221. X    register XPtrV *wp;    /* output list */
  1222. X    int check;        /* check dest existence */
  1223. X{
  1224. X    register char *np;    /* next source component */
  1225. X    register char *tsp, *tdp;
  1226. X
  1227. X    if (sp == NULL) {    /* end of source path */
  1228. X        if (check && eaccess(ds, 0) < 0)
  1229. X            return;
  1230. X        XPput(*wp, strsave(ds, ATEMP));
  1231. X        return;
  1232. X    }
  1233. X
  1234. X    if (dp > ds)
  1235. X        *dp++ = '/';
  1236. X    while (*sp == '/')
  1237. X        *dp++ = *sp++;
  1238. X    np = strchr(sp, '/');
  1239. X    if (np != NULL)
  1240. X        *np++ = 0;
  1241. X
  1242. X    *dp = 0;
  1243. X    if (strchr(sp, MAGIC) == NULL) { /* contains no pattern? */
  1244. X        tdp = dp; tsp = sp;
  1245. X        while ((*tdp++ = *tsp++) != 0)
  1246. X            ;
  1247. X        --tdp;
  1248. X        globit(ds, tdp, np, wp, check);
  1249. X    } else {
  1250. X        DIR *dirp;
  1251. X        struct dirent *d;
  1252. X
  1253. X        /* ToDo:
  1254. X         * should not attemp to open() special files: /dev/ttyd0/*
  1255. X         * opendir should do this check, but Doug Gwyn's does not.
  1256. X         */
  1257. X        dirp = opendir((*ds == 0) ? "." : ds);
  1258. X        if (dirp == NULL)
  1259. X            goto Nodir;
  1260. X        while ((d = readdir(dirp)) != NULL) {
  1261. X            tsp = d->d_name;
  1262. X            if (tsp[0] == '.' &&
  1263. X                (tsp[1] == 0 || tsp[1] == '.' && tsp[2] == 0))
  1264. X                continue; /* always ignore . and .. */
  1265. X            if (*tsp == '.' && *sp != '.' || !gmatch(tsp, sp))
  1266. X                continue;
  1267. X
  1268. X            tdp = dp;
  1269. X            while ((*tdp++ = *tsp++) != 0)
  1270. X                ;
  1271. X            --tdp;
  1272. X            globit(ds, tdp, np, wp, np != NULL);
  1273. X        }
  1274. X        closedir(dirp);
  1275. X      Nodir:;
  1276. X    }
  1277. X
  1278. X    if (np != NULL)
  1279. X        *--np = '/';
  1280. X}
  1281. X
  1282. X/* remove MAGIC from string */
  1283. Xstatic char *
  1284. Xdebunk(cp)
  1285. X    char *cp;
  1286. X{
  1287. X    register char *dp, *sp;
  1288. X
  1289. X    for (dp = sp = cp; *sp != 0; sp++)
  1290. X        if (*sp != MAGIC)
  1291. X            *dp++ = *sp;
  1292. X    *dp = 0;
  1293. X    return cp;
  1294. X}
  1295. X
  1296. X/*
  1297. X * tilde expansion
  1298. X *
  1299. X * based on a version by Arnold Robbins
  1300. X */
  1301. X
  1302. Xstatic char *homedir();
  1303. X
  1304. Xstatic char *
  1305. Xtilde(acp)
  1306. X    char *acp;
  1307. X{
  1308. X    register int c;
  1309. X    char path [PATH+1];
  1310. X    register char *cp = acp, *wp = path, *dp;
  1311. X    char userid [16+1];
  1312. X
  1313. X  Again:
  1314. X    while (1) {
  1315. X        if ((c = *cp++) == 0) {
  1316. X            *wp = 0;
  1317. X            afree((Void*)acp, ATEMP);
  1318. X            return strsave(path, ATEMP);
  1319. X        } else if (c == MAGIC && *cp == '~')
  1320. X            break;
  1321. X        else
  1322. X            *wp++ = c;
  1323. X    }
  1324. X
  1325. X    dp = NULL;    /* no output substitution */
  1326. X    if (cp[1] == 0 || cp[1] == '/' || cp[1] == ':') /* ~ or ~/ */
  1327. X        dp = strval(global("HOME")), cp += 1;
  1328. X    else if (cp[1] == '+' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0))
  1329. X        dp = strval(global("PWD")), cp += 2;
  1330. X    else if (cp[1] == '-' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0))
  1331. X        dp = strval(global("OLDPWD")), cp += 2;
  1332. X    else if (letter(cp[1])) {
  1333. X        char *save = cp;
  1334. X        for (dp = userid, cp++; letnum(*cp) && dp < userid+16; )
  1335. X            *dp++ = *cp++;
  1336. X        *dp = 0;
  1337. X        dp = homedir(userid);
  1338. X        if (dp == NULL)
  1339. X            cp = save;
  1340. X    }
  1341. X    /* substitute */
  1342. X    if (dp != NULL)
  1343. X        while (*dp != 0)
  1344. X            *wp++ = *dp++;
  1345. X    goto Again;
  1346. X}
  1347. X
  1348. X/*
  1349. X * map userid to user's home directory.
  1350. X * todo: implement a cache with the "homedirs" table.
  1351. X * note that 4.3's getpw adds more than 6K to the shell,
  1352. X * and the YP version probably adds much more.
  1353. X * we might consider our own version of getpwnam() to keep the size down.
  1354. X */
  1355. X
  1356. Xstatic char *
  1357. Xhomedir(name)
  1358. X    char *name;
  1359. X{
  1360. X    register struct tbl *ap;
  1361. X    register struct passwd *pw;
  1362. X    extern struct passwd *getpwnam();
  1363. X
  1364. X    ap = tsearch(&homedirs, name, hash(name));
  1365. X    if ((ap != NULL && (ap->flag&ISSET)))
  1366. X        return ap->val.s;
  1367. X    pw = getpwnam(name);
  1368. X    if (pw == NULL)
  1369. X        return NULL;
  1370. X    return pw->pw_dir;
  1371. X}
  1372. END_OF_FILE
  1373.   if test 14245 -ne `wc -c <'sh/eval.c'`; then
  1374.     echo shar: \"'sh/eval.c'\" unpacked with wrong size!
  1375.   fi
  1376.   # end of 'sh/eval.c'
  1377. fi
  1378. if test -f 'sh/lex.c' -a "${1}" != "-c" ; then 
  1379.   echo shar: Will not clobber existing file \"'sh/lex.c'\"
  1380. else
  1381.   echo shar: Extracting \"'sh/lex.c'\" \(12416 characters\)
  1382.   sed "s/^X//" >'sh/lex.c' <<'END_OF_FILE'
  1383. X/*
  1384. X * lexical analysis and source input
  1385. X */
  1386. X
  1387. X#ifndef lint
  1388. Xstatic char *RCSid = "$Id: lex.c,v 3.6 89/03/27 15:51:20 egisin Exp $";
  1389. Xstatic char *sccs_id = "@(#)lex.c    1.3 91/11/09 15:35:19 (sjg)";
  1390. X#endif
  1391. X
  1392. X#include <stddef.h>
  1393. X#include <stdio.h>
  1394. X#include <string.h>
  1395. X#include <errno.h>
  1396. X#include <setjmp.h>
  1397. X#include <unistd.h>
  1398. X#include <assert.h>
  1399. X#include "sh.h"
  1400. X#include "lex.h"
  1401. X#include "tree.h"
  1402. X#include "table.h"
  1403. X#include "expand.h"
  1404. X
  1405. X    int    ttyfd = -1;        /* tty fd for edit and jobs */
  1406. X    char   *history[HISTORY];    /* saved commands */
  1407. X    char  **histptr = history - 1;    /* last history item */
  1408. X    int    histpush;        /* number of pushed fc commands */
  1409. X
  1410. X/* we set s->str to NULLSTR instead of "", so that ungetsc() works */
  1411. Xstatic    char    nullstr [] = {0, 0};
  1412. X#define    NULLSTR    (nullstr + 1)
  1413. X
  1414. Xstatic    int    expanding_alias;
  1415. Xstatic    int    alias;
  1416. Xstatic    int    getsc_ ARGS((void));
  1417. X
  1418. X/* optimized getsc_() */
  1419. X#define    getsc()    ((*source->str != 0) ? *source->str++ : getsc_())
  1420. X#define    ungetsc() (source->str--)
  1421. X
  1422. X/*
  1423. X * Lexical analyzer
  1424. X *
  1425. X * tokens are not regular expressions, they are LL(1).
  1426. X * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
  1427. X * hence the state stack.
  1428. X */
  1429. X
  1430. Xint
  1431. Xyylex(cf)
  1432. X    int cf;
  1433. X{
  1434. X    register int c, state;
  1435. X    char states [64], *statep = states;
  1436. X    XString ws;        /* expandable output word */
  1437. X    register char *wp;    /* output word pointer */
  1438. X    register char *sp, *dp;
  1439. X    int istate;
  1440. X    int c2;
  1441. X    static int rec_alias_cnt = 0;
  1442. X    static struct tbl *rec_alias_table[20];
  1443. X
  1444. X
  1445. X    if (expanding_alias) {
  1446. X        expanding_alias = 0;
  1447. X        while (rec_alias_cnt-- > 0)
  1448. X            rec_alias_table[rec_alias_cnt]->flag &= ~EXPALIAS;
  1449. X        rec_alias_cnt = 0;
  1450. X    }
  1451. X  Again:
  1452. X    Xinit(ws, wp, 64);
  1453. X
  1454. X    if (cf&ONEWORD)
  1455. X        istate = SWORD;
  1456. X    else if (cf&LETEXPR)
  1457. X        istate = SDPAREN;
  1458. X    else {            /* normal lexing */
  1459. X        istate = SBASE;
  1460. X        while ((c = getsc()) == ' ' || c == '\t')
  1461. X            ;
  1462. X        if (c == '#')
  1463. X            while ((c = getsc()) != 0 && c != '\n')
  1464. X                ;
  1465. X        ungetsc();
  1466. X    }
  1467. X    if (alias) {            /* trailing ' ' in alias definition */
  1468. X        alias = 0;
  1469. X        cf |= ALIAS;
  1470. X    }
  1471. X
  1472. X    /* collect non-special or quoted characters to form word */
  1473. X    for (*statep = state = istate;
  1474. X         !((c = getsc()) == 0 || state == SBASE && ctype(c, C_LEX1)); ) {
  1475. X        Xcheck(ws, wp);
  1476. X        switch (state) {
  1477. X          case SBASE:
  1478. X          Sbase:
  1479. X            switch (c) {
  1480. X              case '\\':
  1481. X                c = getsc();
  1482. X                if (c != '\n')
  1483. X                    *wp++ = QCHAR, *wp++ = c;
  1484. X                else
  1485. X                    if (wp == Xstring(ws, wp))
  1486. X                        goto Again;
  1487. X                break;
  1488. X              case '\'':
  1489. X                *++statep = state = SSQUOTE;
  1490. X                *wp++ = OQUOTE;
  1491. X                break;
  1492. X              case '"':
  1493. X                *++statep = state = SDQUOTE;
  1494. X                *wp++ = OQUOTE;
  1495. X                break;
  1496. X              default:
  1497. X                goto Subst;
  1498. X            }
  1499. X            break;
  1500. X
  1501. X          Subst:
  1502. X            switch (c) {
  1503. X              case '\\':
  1504. X                c = getsc();
  1505. X                switch (c) {
  1506. X                  case '\n':
  1507. X                    break;
  1508. X                  case '"': case '\\':
  1509. X                  case '$': case '`':
  1510. X                    *wp++ = QCHAR, *wp++ = c;
  1511. X                    break;
  1512. X                  default:
  1513. X                    Xcheck(ws, wp);
  1514. X                    *wp++ = CHAR, *wp++ = '\\';
  1515. X                    *wp++ = CHAR, *wp++ = c;
  1516. X                    break;
  1517. X                }
  1518. X                break;
  1519. X              case '$':
  1520. X                c = getsc();
  1521. X                if (c == '(') {
  1522. X                    *++statep = state = SPAREN;
  1523. X                    *wp++ = COMSUB;
  1524. X                } else
  1525. X                if (c == '{') {
  1526. X                    *++statep = state = SBRACE;
  1527. X                    *wp++ = OSUBST;
  1528. X                    c = getsc();
  1529. X                    do {
  1530. X                        Xcheck(ws, wp);
  1531. X                        *wp++ = c;
  1532. X                        c = getsc();
  1533. X                    } while (ctype(c, C_ALPHA|C_DIGIT));
  1534. X                    *wp++ = 0;
  1535. X                    /* todo: more compile-time checking */
  1536. X                    if (c == '}')
  1537. X                        ungetsc();
  1538. X                    else if (c == '#' || c == '%') {
  1539. X                        /* Korn pattern trimming */
  1540. X                        if (getsc() == c)
  1541. X                            c |= 0x80;
  1542. X                        else
  1543. X                            ungetsc();
  1544. X                        *wp++ = c;
  1545. X                    } else if (c == ':')
  1546. X                        *wp++ = 0x80|getsc();
  1547. X                    else
  1548. X                        *wp++ = c;
  1549. X                } else if (ctype(c, C_ALPHA)) {
  1550. X                    *wp++ = OSUBST;
  1551. X                    do {
  1552. X                        Xcheck(ws, wp);
  1553. X                        *wp++ = c;
  1554. X                        c = getsc();
  1555. X                    } while (ctype(c, C_ALPHA|C_DIGIT));
  1556. X                    *wp++ = 0;
  1557. X                    *wp++ = CSUBST;
  1558. X                    ungetsc();
  1559. X                } else if (ctype(c, C_DIGIT|C_VAR1)) {
  1560. X                    Xcheck(ws, wp);
  1561. X                    *wp++ = OSUBST;
  1562. X                    *wp++ = c;
  1563. X                    *wp++ = 0;
  1564. X                    *wp++ = CSUBST;
  1565. X                } else {
  1566. X                    *wp++ = CHAR, *wp++ = '$';
  1567. X                    ungetsc();
  1568. X                }
  1569. X                break;
  1570. X              case '`':
  1571. X                *++statep = state = SBQUOTE;
  1572. X                *wp++ = COMSUB;
  1573. X                break;
  1574. X              default:
  1575. X                *wp++ = CHAR, *wp++ = c;
  1576. X            }
  1577. X            break;
  1578. X
  1579. X          case SSQUOTE:
  1580. X            if (c == '\'') {
  1581. X                state = *--statep;
  1582. X                *wp++ = CQUOTE;
  1583. X            } else
  1584. X                *wp++ = QCHAR, *wp++ = c;
  1585. X            break;
  1586. X
  1587. X          case SDQUOTE:
  1588. X            if (c == '"') {
  1589. X                state = *--statep;
  1590. X                *wp++ = CQUOTE;
  1591. X            } else
  1592. X                goto Subst;
  1593. X            break;
  1594. X
  1595. X          case SPAREN:
  1596. X            if (c == '(')
  1597. X                *++statep = state;
  1598. X            else if (c == ')')
  1599. X                state = *--statep;
  1600. X            if (state == SPAREN)
  1601. X                *wp++ = c;
  1602. X            else
  1603. X                *wp++ = 0; /* end of COMSUB */
  1604. X            break;
  1605. X
  1606. X          case SBRACE:
  1607. X            if (c == '}') {
  1608. X                state = *--statep;
  1609. X                *wp++ = CSUBST;
  1610. X            } else
  1611. X                goto Sbase;
  1612. X            break;
  1613. X
  1614. X          case SBQUOTE:
  1615. X            if (c == '`') {
  1616. X                *wp++ = 0;
  1617. X                state = *--statep;
  1618. X            } else if (c == '\\') {
  1619. X                switch (c = getsc()) {
  1620. X                  case '\n':
  1621. X                    break;
  1622. X                  case '\\':
  1623. X                  case '$': case '`':
  1624. X                    *wp++ = c;
  1625. X                    break;
  1626. X                  default:
  1627. X                    *wp++ = '\\';
  1628. X                    *wp++ = c;
  1629. X                    break;
  1630. X                }
  1631. X            } else
  1632. X                *wp++ = c;
  1633. X            break;
  1634. X
  1635. X          case SWORD:    /* ONEWORD */
  1636. X            goto Subst;
  1637. X
  1638. X          case SDPAREN:    /* LETEXPR */
  1639. X            if (c == ')') {
  1640. X                if (getsc() == ')') {
  1641. X                    c = 0;
  1642. X                    goto Done;
  1643. X                } else
  1644. X                    ungetsc();
  1645. X            }
  1646. X            goto Subst;
  1647. X        }
  1648. X    }
  1649. XDone:
  1650. X    Xcheck(ws, wp);
  1651. X    if (state != istate)
  1652. X        yyerror("no closing quote");
  1653. X
  1654. X    if (c == '<' || c == '>') {
  1655. X        char *cp = Xstring(ws, wp);
  1656. X        if (wp > cp && cp[0] == CHAR && digit(cp[1])) {
  1657. X            wp = cp; /* throw away word */
  1658. X            c2/*unit*/ = cp[1] - '0';
  1659. X        } else
  1660. X            c2/*unit*/ = c == '>'; /* 0 for <, 1 for > */
  1661. X    }
  1662. X
  1663. X    if (wp == Xstring(ws, wp) && state == SBASE) {
  1664. X        Xfree(ws, sp);    /* free word */
  1665. X        /* no word, process LEX1 character */
  1666. X        switch (c) {
  1667. X          default:
  1668. X            return c;
  1669. X
  1670. X          case '|':
  1671. X          case '&':
  1672. X          case ';':
  1673. X            if (getsc() == c)
  1674. X                c = (c == ';') ? BREAK :
  1675. X                    (c == '|') ? LOGOR :
  1676. X                    (c == '&') ? LOGAND :
  1677. X                    YYERRCODE;
  1678. X            else
  1679. X                ungetsc();
  1680. X            return c;
  1681. X
  1682. X          case '>':
  1683. X          case '<': {
  1684. X            register struct ioword *iop;
  1685. X
  1686. X            iop = (struct ioword *) alloc(sizeof(*iop), ATEMP);
  1687. X            iop->unit = c2/*unit*/;
  1688. X
  1689. X            c2 = getsc();
  1690. X            if (c2 == '>' || c2 == '<') {
  1691. X                iop->flag = c != c2 ? IORDWR : c == '>' ? IOCAT : IOHERE;
  1692. X                c2 = getsc();
  1693. X            } else
  1694. X                iop->flag = c == '>' ? IOWRITE : IOREAD;
  1695. X
  1696. X            if (iop->flag == IOHERE)
  1697. X                if (c2 == '-')
  1698. X                    iop->flag |= IOSKIP;
  1699. X                else
  1700. X                    ungetsc();
  1701. X            else
  1702. X                if (c2 == '&')
  1703. X                    iop->flag = IODUP;
  1704. X                else if (c2 == '!' && iop->flag == IOWRITE)
  1705. X                    iop->flag |= IOCLOB;
  1706. X                else
  1707. X                    ungetsc();
  1708. X            yylval.iop = iop;
  1709. X            return REDIR;
  1710. X            }
  1711. X          case '\n':
  1712. X            gethere();
  1713. X            if (cf & CONTIN)
  1714. X                goto Again;
  1715. X            return c;
  1716. X
  1717. X          case '(':
  1718. X            c2 = getsc();
  1719. X            if (c2 == ')')
  1720. X                c = MPAREN;
  1721. X            else if (c2 == '(')
  1722. X                c = MDPAREN;
  1723. X            else
  1724. X                ungetsc();
  1725. X          case ')':
  1726. X            return c;
  1727. X        }
  1728. X    }
  1729. X
  1730. X    *wp++ = EOS;        /* terminate word */
  1731. X    yylval.cp = Xclose(ws, wp);
  1732. X    if (state == SWORD || state == SDPAREN)    /* ONEWORD? */
  1733. X        return LWORD;
  1734. X    ungetsc();        /* unget terminator */
  1735. X
  1736. X    /* copy word to unprefixed string ident */
  1737. X    for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
  1738. X        *dp++ = *sp++;
  1739. X    /* Make sure the ident array stays '\0' padded */
  1740. X    while (dp <= ident+IDENT)
  1741. X        *dp++ = '\0';
  1742. X#if 0
  1743. X    if (*ident == '~' || (dp = strchr(ident, '=')) != NULL && dp[1] == '~')
  1744. X        "Tilde expansion";
  1745. X#endif
  1746. X    if (c != EOS)
  1747. X        *ident = 0;    /* word is not unquoted */
  1748. X
  1749. X    if (*ident != 0 && (cf&(KEYWORD|ALIAS))) {
  1750. X        register struct tbl *p;
  1751. X
  1752. X        p = tsearch(&lexicals, ident, hash(ident));
  1753. X        if (p != NULL && (p->flag&ISSET))
  1754. X            if (p->type == CKEYWD && (cf&KEYWORD)) {
  1755. X                afree(yylval.cp, ATEMP);
  1756. X                return p->val.i;
  1757. X            } else if (p->type == CALIAS && (cf&ALIAS) &&
  1758. X                   !(p->flag&EXPALIAS)) {
  1759. X                register Source *s;
  1760. X
  1761. X                if (rec_alias_cnt == sizeof(rec_alias_table)/sizeof(rec_alias_table[0]))
  1762. X                    yyerror("excessive recusrsive aliasing");
  1763. X                else
  1764. X                    rec_alias_table[rec_alias_cnt++] = p;
  1765. X                p->flag |= EXPALIAS;
  1766. X                /* check for recursive aliasing */
  1767. X                for (s = source; s->type == SALIAS; s = s->next)
  1768. X                    if (s->u.tblp == p)
  1769. X                        return LWORD;
  1770. X                afree(yylval.cp, ATEMP);
  1771. X
  1772. X                /* push alias expansion */
  1773. X                s = pushs(SALIAS);
  1774. X                s->str = p->val.s;
  1775. X                s->u.tblp = p;
  1776. X                s->next = source;
  1777. X                source = s;
  1778. X                goto Again;
  1779. X            } 
  1780. X    }
  1781. X
  1782. X    return LWORD;
  1783. X}
  1784. X
  1785. Xstatic void readhere();
  1786. X
  1787. Xgethere()
  1788. X{
  1789. X    register struct ioword **p;
  1790. X
  1791. X    for (p = heres; p < herep; p++)
  1792. X        readhere(*p);
  1793. X    herep = heres;
  1794. X}
  1795. X
  1796. X/*
  1797. X * read "<<word" text into temp file
  1798. X * todo: set up E_ERR to fclose(f) on unwind
  1799. X */
  1800. X
  1801. Xstatic void
  1802. Xreadhere(iop)
  1803. X    register struct ioword *iop;
  1804. X{
  1805. X    register FILE *f;
  1806. X    struct temp *h;
  1807. X    register int c;
  1808. X    char *eof;
  1809. X    register char *cp;
  1810. X    char line [LINE+1];
  1811. X
  1812. X    eof = evalstr(iop->name, 0);
  1813. X
  1814. X    h = maketemp(ATEMP);
  1815. X    h->next = e.temps; e.temps = h;
  1816. X    iop->name = h->name;
  1817. X    f = fopen(h->name, "w");
  1818. X    if (f == NULL)
  1819. X        errorf("Cannot create temporary file\n");
  1820. X    setvbuf(f, (char *)NULL, _IOFBF, BUFSIZ);
  1821. X
  1822. X    for (;;) {
  1823. X        cp = line;
  1824. X        while ((c = getsc()) != '\n') {
  1825. X            if (c == 0)
  1826. X                errorf("here document `%s' unclosed\n", eof);
  1827. X            if (cp >= line+LINE)
  1828. X                break;
  1829. X            *cp++ = c;
  1830. X        }
  1831. X        ungetsc();
  1832. X        *cp = 0;
  1833. X        for (cp = line; iop->flag&IOSKIP && *cp == '\t'; cp++)
  1834. X            ;
  1835. X        if (strcmp(eof, cp) == 0 || c == 0)
  1836. X            break;
  1837. X        while ((c = *cp++) != '\0')
  1838. X            putc(c, f);
  1839. X        while ((c = getsc()) != '\n') {
  1840. X            if (c == 0)
  1841. X                errorf("here document `%s' unclosed\n", eof);
  1842. X            putc(c, f);
  1843. X        }
  1844. X        putc(c, f);
  1845. X    }
  1846. X    fclose(f);
  1847. X}
  1848. X
  1849. Xvoid
  1850. Xyyerror(msg)
  1851. X    Const char *msg;
  1852. X{
  1853. X    yynerrs++;
  1854. X    while (source->type == SALIAS) /* pop aliases */
  1855. X        source = source->next;
  1856. X    if (source->file != NULL)
  1857. X        shellf("%s[%d]: ", source->file, source->line);
  1858. X    source->str = NULLSTR;    /* zap pending input */
  1859. X    errorf("%s\n", msg);
  1860. X}
  1861. X
  1862. X/*
  1863. X * input for yylex with alias expansion
  1864. X */
  1865. X
  1866. XSource *
  1867. Xpushs(type)
  1868. X    int type;
  1869. X{
  1870. X    register Source *s;
  1871. X
  1872. X    s = (Source *) alloc(sizeof(Source), ATEMP);
  1873. X    s->type = type;
  1874. X    s->str = NULLSTR;
  1875. X    s->line = 0;
  1876. X    s->file = NULL;
  1877. X    s->echo = 0;
  1878. X    s->next = NULL;
  1879. X    return s;
  1880. X}
  1881. X
  1882. Xstatic int
  1883. Xgetsc_()
  1884. X{
  1885. X    register Source *s = source;
  1886. X    register int c;
  1887. X    extern void    mprint();
  1888. X
  1889. X    while ((c = *s->str++) == 0) {
  1890. X        s->str = NULL;        /* return 0 for EOF by default */
  1891. X        switch (s->type) {
  1892. X          case SEOF:
  1893. X            s->str = NULLSTR;
  1894. X            return 0;
  1895. X
  1896. X          case STTY:
  1897. X            if (histpush < 0) {    /* commands pushed by dofc */
  1898. X                s->type = SHIST;
  1899. X                s->str = NULLSTR;
  1900. X                continue;
  1901. X            }
  1902. X            s->line++;
  1903. X            s->str = line;
  1904. X            line[0] = '\0';
  1905. X            mprint();
  1906. X            pprompt(prompt);
  1907. X            flushshf(1);    flushshf(2);
  1908. X#ifdef EDIT
  1909. X#ifdef EMACS
  1910. X            if (flag[FEMACS])
  1911. X                c = x_read(ttyfd, line, LINE);
  1912. X            else
  1913. X#endif
  1914. X#ifdef VI
  1915. X            if (flag[FVI])
  1916. X                c = x_read(ttyfd, line, LINE);
  1917. X            else
  1918. X#endif
  1919. X#endif
  1920. X                c = read(ttyfd, line, LINE);
  1921. X            if (c < 0)    /* read error */
  1922. X                c = 0;
  1923. X            line[c] = '\0';
  1924. X            prompt = strval(global("PS2"));
  1925. X            if (c == 0) { /* EOF */
  1926. X                s->str = NULL;
  1927. X                s->line--;
  1928. X            } else {
  1929. X                c = 0;
  1930. X                while(line[c] && ctype(line[c], C_IFS))
  1931. X                    c++;
  1932. X                if (!line[c]) {
  1933. X                    s->str = &line[c - 1];
  1934. X                    s->line--;
  1935. X                } else {
  1936. X                    s->str = &line[c];
  1937. X                    histsave(s->str);
  1938. X                }
  1939. X            }
  1940. X            break;
  1941. X
  1942. X          case SHIST:
  1943. X            if (histpush == 0) {
  1944. X                s->type = STTY;
  1945. X                s->str = NULLSTR;
  1946. X                continue;
  1947. X            }
  1948. X            s->line++;
  1949. X            s->str = histptr[++histpush];
  1950. X#if 0
  1951. X            pprompt("!< ");    /* todo: PS9 */
  1952. X#endif
  1953. X            shellf("%s\n", s->str);
  1954. X            strcpy(line, s->str);
  1955. X            s->str = strchr(line, 0);
  1956. X            *s->str++ = '\n';
  1957. X            *s->str = 0;
  1958. X            s->str = line;
  1959. X            break;
  1960. X
  1961. X          case SFILE:
  1962. X            s->line++;
  1963. X            s->str = fgets(line, LINE, s->u.file);
  1964. X            if (s->str == NULL)
  1965. X                if (s->u.file != stdin)
  1966. X                    fclose(s->u.file);
  1967. X            break;
  1968. X
  1969. X          case SWSTR:
  1970. X            break;
  1971. X
  1972. X          case SSTRING:
  1973. X            s->str = "\n";
  1974. X            s->type = SEOF;
  1975. X            break;
  1976. X
  1977. X          case SWORDS:
  1978. X            s->str = *s->u.strv++;
  1979. X            s->type = SWORDSEP;
  1980. X            break;
  1981. X
  1982. X          case SWORDSEP:
  1983. X            if (*s->u.strv == NULL) {
  1984. X                s->str = "\n";
  1985. X                s->type = SEOF;
  1986. X            } else {
  1987. X                s->str = " ";
  1988. X                s->type = SWORDS;
  1989. X            }
  1990. X            break;
  1991. X
  1992. X          case SALIAS:
  1993. X            s->str = s->u.tblp->val.s;
  1994. X            if (s->str[0] != 0) {
  1995. X                c = strchr(s->str, 0)[-1];
  1996. X                if (c == ' ' || c == '\t')
  1997. X                    alias = 1;    /* trailing ' ' */
  1998. X            }
  1999. X            source = s = s->next;
  2000. X            expanding_alias = 1;
  2001. X            continue;
  2002. X        }
  2003. X        if (s->str == NULL) {
  2004. X            s->type = SEOF;
  2005. X            s->str = NULLSTR;
  2006. X            return 0;
  2007. X        }
  2008. X        if (s->echo)
  2009. X            fputs(s->str, shlout);
  2010. X    }
  2011. X    return c;
  2012. X}
  2013. X
  2014. Xpprompt(cp)
  2015. X    register char *cp;
  2016. X{
  2017. X    while (*cp != 0)
  2018. X        if (*cp != '!')
  2019. X            putc(*cp++, shlout);
  2020. X        else
  2021. X            if (*++cp == '!')
  2022. X                putc(*cp++, shlout);
  2023. X            else
  2024. X                shellf("%d", source->line);
  2025. X    fflush(shlout);
  2026. X}
  2027. END_OF_FILE
  2028.   if test 12416 -ne `wc -c <'sh/lex.c'`; then
  2029.     echo shar: \"'sh/lex.c'\" unpacked with wrong size!
  2030.   fi
  2031.   # end of 'sh/lex.c'
  2032. fi
  2033. if test -f 'sh/syn.c' -a "${1}" != "-c" ; then 
  2034.   echo shar: Will not clobber existing file \"'sh/syn.c'\"
  2035. else
  2036.   echo shar: Extracting \"'sh/syn.c'\" \(9735 characters\)
  2037.   sed "s/^X//" >'sh/syn.c' <<'END_OF_FILE'
  2038. X/*
  2039. X * shell parser (C version)
  2040. X */
  2041. X
  2042. Xstatic char *RCSid = "$Id: syn.c,v 3.3 89/03/27 15:51:51 egisin Exp $";
  2043. X
  2044. X#include <stddef.h>
  2045. X#include <stdio.h>
  2046. X#include <string.h>
  2047. X#include <errno.h>
  2048. X#include <setjmp.h>
  2049. X#include "sh.h"
  2050. X#include "lex.h"
  2051. X#include "tree.h"
  2052. X#include "table.h"
  2053. X#include "expand.h"
  2054. X
  2055. Xstatic    void    zzerr();
  2056. Xstatic    struct    op *block(), *newtp();
  2057. Xstatic    struct    op *pipeline(), *andor(), *command();
  2058. Xstatic    struct    op *nested(), *c_list();
  2059. Xstatic    struct    op *dogroup(), *thenpart(), *casepart(), *caselist();
  2060. Xstatic    struct    op *elsepart();
  2061. Xstatic    char  **wordlist();
  2062. Xstatic    void    musthave();
  2063. Xstatic    struct ioword *synio(), *io();
  2064. X
  2065. Xstatic    struct    op    *outtree; /* yyparse output */
  2066. X
  2067. Xstatic    int    reject;        /* token(cf) gets symbol again */
  2068. Xstatic    int    symbol;        /* yylex value */
  2069. X
  2070. X#define    REJECT    (reject = 1)
  2071. X#define    ACCEPT    (reject = 0)
  2072. X#define    token(cf) \
  2073. X    ((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
  2074. X#define    tpeek(cf) \
  2075. X    ((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
  2076. X
  2077. Xint
  2078. Xyyparse()
  2079. X{
  2080. X    ACCEPT;
  2081. X    yynerrs = 0;
  2082. X    if ((tpeek(KEYWORD|ALIAS)) == 0) { /* EOF */
  2083. X        outtree = newtp(TEOF);
  2084. X        return 0;
  2085. X    }
  2086. X    outtree = c_list();
  2087. X    musthave('\n', 0);
  2088. X    return (yynerrs != 0);
  2089. X}
  2090. X
  2091. Xstatic struct op *
  2092. Xpipeline(cf)
  2093. X    int cf;
  2094. X{
  2095. X    register struct op *t, *p, *tl = NULL;
  2096. X    register int c;
  2097. X
  2098. X    t = command(cf);
  2099. X    if (t != NULL) {
  2100. X        while ((c = token(0)) == '|') {
  2101. X            if ((p = command(CONTIN)) == NULL)
  2102. X                SYNTAXERR;
  2103. X            if (tl == NULL)
  2104. X                t = tl = block(TPIPE, t, p, NOWORDS);
  2105. X            else
  2106. X                tl = tl->right = block(TPIPE, tl->right, p, NOWORDS);
  2107. X            /*t = block(TPIPE, t, p, NOWORDS);*/
  2108. X        }
  2109. X        REJECT;
  2110. X    }
  2111. X    return (t);
  2112. X}
  2113. X
  2114. Xstatic struct op *
  2115. Xandor()
  2116. X{
  2117. X    register struct op *t, *p;
  2118. X    register int c;
  2119. X
  2120. X    t = pipeline(0);
  2121. X    if (t != NULL) {
  2122. X        while ((c = token(0)) == LOGAND || c == LOGOR) {
  2123. X            if ((p = pipeline(CONTIN)) == NULL)
  2124. X                SYNTAXERR;
  2125. X            t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
  2126. X        }
  2127. X        REJECT;
  2128. X    }
  2129. X    return (t);
  2130. X}
  2131. X
  2132. Xstatic struct op *
  2133. Xc_list()
  2134. X{
  2135. X    register struct op *t, *p, *tl = NULL;
  2136. X    register int c;
  2137. X
  2138. X    t = andor();
  2139. X    if (t != NULL) {
  2140. X        while ((c = token(0)) == ';' || c == '&' ||
  2141. X               (multiline || source->type == SSTRING
  2142. X                || source->type == SALIAS) && c == '\n') {
  2143. X            if (c == '&') {
  2144. X                if (tl)
  2145. X                    tl->right = block(TASYNC, tl->right, NOBLOCK, NOWORDS);
  2146. X                else
  2147. X                    t = block(TASYNC, t, NOBLOCK, NOWORDS);
  2148. X            }
  2149. X            if ((p = andor()) == NULL)
  2150. X                return (t);
  2151. X            if (tl == NULL)
  2152. X                t = tl = block(TLIST, t, p, NOWORDS);
  2153. X            else
  2154. X                tl = tl->right = block(TLIST, tl->right, p, NOWORDS);
  2155. X        }
  2156. X        REJECT;
  2157. X    }
  2158. X    return (t);
  2159. X}
  2160. X
  2161. Xstatic struct ioword *
  2162. Xsynio(cf)
  2163. X    int cf;
  2164. X{
  2165. X    register struct ioword *iop;
  2166. X
  2167. X    if (tpeek(cf) != REDIR)
  2168. X        return NULL;
  2169. X    ACCEPT;
  2170. X    iop = yylval.iop;
  2171. X    musthave(LWORD, 0);
  2172. X    iop->name = yylval.cp;
  2173. X    if ((iop->flag&IOTYPE) == IOHERE) {
  2174. X        if (*ident != 0) /* unquoted */
  2175. X            iop->flag |= IOEVAL;
  2176. X        if (herep >= &heres[HERES])
  2177. X            errorf("too many <<'s\n");
  2178. X        *herep++ = iop;
  2179. X    }
  2180. X    return iop;
  2181. X}
  2182. X
  2183. Xstatic void
  2184. Xmusthave(c, cf)
  2185. X    int c, cf;
  2186. X{
  2187. X    if ((token(cf)) != c)
  2188. X        SYNTAXERR;
  2189. X}
  2190. X
  2191. Xstatic struct op *
  2192. Xnested(type, mark)
  2193. X    int type, mark;
  2194. X{
  2195. X    register struct op *t;
  2196. X
  2197. X    multiline++;
  2198. X    t = c_list();
  2199. X    musthave(mark, KEYWORD);
  2200. X    multiline--;
  2201. X    return (block(type, t, NOBLOCK, NOWORDS));
  2202. X}
  2203. X
  2204. Xstatic struct op *
  2205. Xcommand(cf)
  2206. X    int cf;
  2207. X{
  2208. X    register struct op *t;
  2209. X    register int c, iopn = 0;
  2210. X    struct ioword *iop, **iops;
  2211. X    XPtrV args, vars;
  2212. X
  2213. X    iops = (struct ioword **) alloc(sizeofN(struct ioword *, NUFILE+1), ATEMP);
  2214. X    XPinit(args, 16);
  2215. X    XPinit(vars, 16);
  2216. X
  2217. X    if (multiline)
  2218. X        cf = CONTIN;
  2219. X    cf |= KEYWORD|ALIAS;
  2220. X
  2221. X    while ((iop = synio(cf)) != NULL) {
  2222. X        if (iopn >= NUFILE)
  2223. X            yyerror("too many redirections");
  2224. X        iops[iopn++] = iop;
  2225. X        cf &=~ CONTIN;
  2226. X    }
  2227. X
  2228. X    switch (c = token(cf)) {
  2229. X      case 0:
  2230. X        yyerror("unexpected EOF");
  2231. X        return NULL;
  2232. X
  2233. X      default:
  2234. X        REJECT;
  2235. X        if (iopn == 0)
  2236. X            return NULL; /* empty line */
  2237. X        t = newtp(TCOM);
  2238. X        break;
  2239. X
  2240. X      case LWORD:
  2241. X      case MDPAREN:
  2242. X        REJECT;
  2243. X        t = newtp(TCOM);
  2244. X        if (c == MDPAREN) {
  2245. X            ACCEPT;
  2246. X            XPput(args,"let");
  2247. X            musthave(LWORD,LETEXPR);
  2248. X            XPput(args,yylval.cp);
  2249. X        }
  2250. X        while (1)
  2251. X            switch (tpeek(0)) {
  2252. X              case REDIR:
  2253. X                if (iopn >= NUFILE)
  2254. X                    yyerror("too many redirections");
  2255. X                iops[iopn++] = synio(0);
  2256. X                break;
  2257. X
  2258. X              case LWORD:
  2259. X                ACCEPT;
  2260. X                if ((XPsize(args) == 0 || flag[FKEYWORD])
  2261. X                    && strchr(ident+1, '='))
  2262. X                    {XPput(vars, yylval.cp);}
  2263. X                else
  2264. X                    {XPput(args, yylval.cp);}
  2265. X                break;
  2266. X
  2267. X              case MPAREN:
  2268. X                ACCEPT;
  2269. X                if (XPsize(args) != 1)
  2270. X                    SYNTAXERR;
  2271. X                if (*ident == 0)
  2272. X                    yyerror("invalid function name\n");
  2273. X                t = newtp(TFUNCT);
  2274. X                t->str = strsave(ident, ATEMP);
  2275. X                musthave('{', CONTIN|KEYWORD);
  2276. X                t->left = nested(TBRACE, '}');
  2277. X                return t;
  2278. X
  2279. X              default:
  2280. X                goto Leave;
  2281. X            }
  2282. X      Leave:
  2283. X        break;
  2284. X
  2285. X      case '(':
  2286. X        t = nested(TPAREN, ')');
  2287. X        break;
  2288. X
  2289. X      case '{':
  2290. X        t = nested(TBRACE, '}');
  2291. X        break;
  2292. X
  2293. X      case FOR:
  2294. X        t = newtp(TFOR);
  2295. X        musthave(LWORD, 0);
  2296. X        t->str = strsave(ident, ATEMP);
  2297. X        multiline++;
  2298. X        t->vars = wordlist();
  2299. X        t->left = dogroup(0);
  2300. X        multiline--;
  2301. X        break;
  2302. X
  2303. X      case WHILE:
  2304. X      case UNTIL:
  2305. X        multiline++;
  2306. X        t = newtp((c == WHILE) ? TWHILE: TUNTIL);
  2307. X        t->left = c_list();
  2308. X        t->right = dogroup(1);
  2309. X        multiline--;
  2310. X        break;
  2311. X
  2312. X      case CASE:
  2313. X        t = newtp(TCASE);
  2314. X        musthave(LWORD, 0);
  2315. X        t->str = yylval.cp;
  2316. X        multiline++;
  2317. X        musthave(IN, KEYWORD|CONTIN);
  2318. X        t->left = caselist();
  2319. X        musthave(ESAC, KEYWORD);
  2320. X        multiline--;
  2321. X        break;
  2322. X
  2323. X      case IF:
  2324. X        multiline++;
  2325. X        t = newtp(TIF);
  2326. X        t->left = c_list();
  2327. X        t->right = thenpart();
  2328. X        musthave(FI, KEYWORD);
  2329. X        multiline--;
  2330. X        break;
  2331. X
  2332. X      case TIME:
  2333. X        t = pipeline(CONTIN);
  2334. X        t = block(TTIME, t, NOBLOCK, NOWORDS);
  2335. X        break;
  2336. X
  2337. X      case FUNCTION:
  2338. X        t = newtp(TFUNCT);
  2339. X        musthave(LWORD, 0);
  2340. X        t->str = strsave(ident, ATEMP);
  2341. X        musthave('{', CONTIN|KEYWORD);
  2342. X        t->left = nested(TBRACE, '}');
  2343. X        break;
  2344. X
  2345. X#if 0
  2346. X      case MDPAREN:
  2347. X        t = newtp(TCOM);
  2348. X        XPput(args, "let");
  2349. X        musthave(LWORD, LETEXPR);
  2350. X        XPput(args, yylval.cp);
  2351. X        while (tpeek(0) == REDIR) {
  2352. X            if (iopn >= NUFILE)
  2353. X                yyerror("too many redirections");
  2354. X            iops[iopn++] = synio(0);
  2355. X        }
  2356. X        break;
  2357. X#endif
  2358. X    }
  2359. X
  2360. X    while ((iop = synio(0)) != NULL) {
  2361. X        if (iopn >= NUFILE)
  2362. X            yyerror("too many redirections");
  2363. X        iops[iopn++] = iop;
  2364. X    }
  2365. X
  2366. X    if (iopn == 0) {
  2367. X        afree((Void*) iops, ATEMP);
  2368. X        t->ioact = NULL;
  2369. X    } else {
  2370. X        iops[iopn++] = NULL;
  2371. X        aresize((Void*) iops, sizeofN(struct ioword *, iopn), ATEMP);
  2372. X        t->ioact = iops;
  2373. X    }
  2374. X
  2375. X    if (t->type == TCOM) {
  2376. X        XPput(args, NULL);
  2377. X        t->args = (char **) XPclose(args);
  2378. X        XPput(vars, NULL);
  2379. X        t->vars = (char **) XPclose(vars);
  2380. X    } else {
  2381. X        XPfree(args);
  2382. X        XPfree(vars);
  2383. X    }
  2384. X
  2385. X    return t;
  2386. X}
  2387. X
  2388. Xstatic struct op *
  2389. Xdogroup(onlydone)
  2390. X    int onlydone;
  2391. X{
  2392. X    register int c;
  2393. X    register struct op *list;
  2394. X
  2395. X    c = token(CONTIN|KEYWORD);
  2396. X    if (c == DONE && onlydone)
  2397. X        return NULL;
  2398. X    if (c != DO)
  2399. X        SYNTAXERR;
  2400. X    list = c_list();
  2401. X    musthave(DONE, KEYWORD);
  2402. X    return list;
  2403. X}
  2404. X
  2405. Xstatic struct op *
  2406. Xthenpart()
  2407. X{
  2408. X    register int c;
  2409. X    register struct op *t;
  2410. X
  2411. X    if ((c = token(0)) != THEN) {
  2412. X        REJECT;
  2413. X        return NULL;
  2414. X    }
  2415. X    t = newtp(0);
  2416. X    t->left = c_list();
  2417. X    if (t->left == NULL)
  2418. X        SYNTAXERR;
  2419. X    t->right = elsepart();
  2420. X    return (t);
  2421. X}
  2422. X
  2423. Xstatic struct op *
  2424. Xelsepart()
  2425. X{
  2426. X    register int c;
  2427. X    register struct op *t;
  2428. X
  2429. X    switch (c = token(0)) {
  2430. X      case ELSE:
  2431. X        if ((t = c_list()) == NULL)
  2432. X            SYNTAXERR;
  2433. X        return (t);
  2434. X
  2435. X      case ELIF:
  2436. X        t = newtp(TELIF);
  2437. X        t->left = c_list();
  2438. X        t->right = thenpart();
  2439. X        return (t);
  2440. X
  2441. X      default:
  2442. X        REJECT;
  2443. X        return NULL;
  2444. X    }
  2445. X}
  2446. X
  2447. Xstatic struct op *
  2448. Xcaselist()
  2449. X{
  2450. X    register struct op *t, *tl;
  2451. X
  2452. X    t = tl = NULL;
  2453. X    while ((tpeek(CONTIN|KEYWORD)) != ESAC) {
  2454. X        struct op *tc = casepart();
  2455. X        if (tl == NULL)
  2456. X            t = tl = tc, tl->right = NULL;
  2457. X        else
  2458. X            tl->right = tc, tl = tc;
  2459. X    }
  2460. X    return (t);
  2461. X}
  2462. X
  2463. Xstatic struct op *
  2464. Xcasepart()
  2465. X{
  2466. X    register struct op *t;
  2467. X    register int c, cf;
  2468. X    XPtrV ptns;
  2469. X
  2470. X    XPinit(ptns, 16);
  2471. X    t = newtp(TPAT);
  2472. X    cf = CONTIN|KEYWORD;
  2473. X    c = token(cf);
  2474. X    if (c != '(')
  2475. X        REJECT;
  2476. X    else
  2477. X        cf = 0;
  2478. X    do {
  2479. X        musthave(LWORD, cf);
  2480. X        XPput(ptns, yylval.cp);
  2481. X        cf = 0;
  2482. X    } while ((c = token(0)) == '|');
  2483. X    REJECT;
  2484. X    XPput(ptns, NULL);
  2485. X    t->vars = (char **) XPclose(ptns);
  2486. X    musthave(')', 0);
  2487. X
  2488. X    t->left = c_list();
  2489. X    if ((tpeek(CONTIN|KEYWORD)) != ESAC)
  2490. X        musthave(BREAK, CONTIN|KEYWORD);
  2491. X    return (t);
  2492. X}
  2493. X
  2494. Xstatic char **
  2495. Xwordlist()
  2496. X{
  2497. X    register int c;
  2498. X    XPtrV args;
  2499. X
  2500. X    XPinit(args, 16);
  2501. X    if ((c = token(CONTIN|KEYWORD)) != IN) {
  2502. X        REJECT;
  2503. X        return NULL;
  2504. X    }
  2505. X    while ((c = token(0)) == LWORD)
  2506. X        XPput(args, yylval.cp);
  2507. X    if (c != '\n' && c != ';')
  2508. X        SYNTAXERR;
  2509. X    if (XPsize(args) == 0) {
  2510. X        XPfree(args);
  2511. X        return NULL;
  2512. X    } else {
  2513. X        XPput(args, NULL);
  2514. X        return (char **) XPclose(args);
  2515. X    }
  2516. X}
  2517. X
  2518. X/*
  2519. X * supporting functions
  2520. X */
  2521. X
  2522. Xstatic struct op *
  2523. Xblock(type, t1, t2, wp)
  2524. X    struct op *t1, *t2;
  2525. X    char **wp;
  2526. X{
  2527. X    register struct op *t;
  2528. X
  2529. X    t = newtp(type);
  2530. X    t->left = t1;
  2531. X    t->right = t2;
  2532. X    t->vars = wp;
  2533. X    return (t);
  2534. X}
  2535. X
  2536. XConst    struct res {
  2537. X    char    *name;
  2538. X    int    val;
  2539. X} restab[] = {
  2540. X    "for",        FOR,
  2541. X    "case",        CASE,
  2542. X    "esac",        ESAC,
  2543. X    "while",    WHILE,
  2544. X    "do",        DO,
  2545. X    "done",        DONE,
  2546. X    "if",        IF,
  2547. X    "in",        IN,
  2548. X    "then",        THEN,
  2549. X    "else",        ELSE,
  2550. X    "elif",        ELIF,
  2551. X    "until",    UNTIL,
  2552. X    "fi",        FI,
  2553. X    "function",    FUNCTION,
  2554. X    "time",        TIME,
  2555. X    "{",        '{',
  2556. X    "}",        '}',
  2557. X    0
  2558. X};
  2559. X
  2560. Xkeywords()
  2561. X{
  2562. X    register struct res Const *rp;
  2563. X    register struct tbl *p;
  2564. X
  2565. X    for (rp = restab; rp->name; rp++) {
  2566. X        p = tenter(&lexicals, rp->name, hash(rp->name));
  2567. X        p->flag |= DEFINED|ISSET;
  2568. X        p->type = CKEYWD;
  2569. X        p->val.i = rp->val;
  2570. X    }
  2571. X}
  2572. X
  2573. Xstatic struct op *
  2574. Xnewtp(type)
  2575. X    int type;
  2576. X{
  2577. X    register struct op *t;
  2578. X
  2579. X    t = (struct op *) alloc(sizeof(*t), ATEMP);
  2580. X    t->type = type;
  2581. X    t->args = t->vars = NULL;
  2582. X    t->ioact = NULL;
  2583. X    t->left = t->right = NULL;
  2584. X    t->str = NULL;
  2585. X    return (t);
  2586. X}
  2587. X
  2588. Xstatic void
  2589. Xzzerr()
  2590. X{
  2591. X    yyerror("syntax error");
  2592. X}
  2593. X
  2594. Xstruct op *
  2595. Xcompile(s)
  2596. X    Source *s;
  2597. X{
  2598. X    yynerrs = 0;
  2599. X    multiline = 0;
  2600. X    herep = heres;
  2601. X    source = s;
  2602. X    if (yyparse())
  2603. X        unwind();
  2604. X    if (s->type == STTY || s->type == SFILE || s->type == SHIST)
  2605. X        s->str = null;    /* line is not preserved */
  2606. X    return outtree;
  2607. X}
  2608. X
  2609. END_OF_FILE
  2610.   if test 9735 -ne `wc -c <'sh/syn.c'`; then
  2611.     echo shar: \"'sh/syn.c'\" unpacked with wrong size!
  2612.   fi
  2613.   # end of 'sh/syn.c'
  2614. fi
  2615. echo shar: End of archive 4 \(of 9\).
  2616. cp /dev/null ark4isdone
  2617. MISSING=""
  2618. for I in 1 2 3 4 5 6 7 8 9 ; do
  2619.     if test ! -f ark${I}isdone ; then
  2620.     MISSING="${MISSING} ${I}"
  2621.     fi
  2622. done
  2623. if test "${MISSING}" = "" ; then
  2624.     echo You have unpacked all 9 archives.
  2625.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2626. else
  2627.     echo You still must unpack the following archives:
  2628.     echo "        " ${MISSING}
  2629. fi
  2630. exit 0
  2631. exit 0 # Just in case...
  2632. -- 
  2633. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  2634. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  2635. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  2636. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  2637.