home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume18 / mush / part10 < prev    next >
Internet Message Format  |  1991-04-22  |  51KB

  1. From: argv@zipcode.com (Dan Heller)
  2. Newsgroups: comp.sources.misc
  3. Subject: v18i067:  mush - Mail User's Shell, Part10/22
  4. Message-ID: <1991Apr22.000314.18736@sparky.IMD.Sterling.COM>
  5. Date: 22 Apr 91 00:03:14 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: d75d5d29 b8aec007 9f0e321b 2ecdbf05
  8.  
  9. Submitted-by: Dan Heller <argv@zipcode.com>
  10. Posting-number: Volume 18, Issue 67
  11. Archive-name: mush/part10
  12. Supersedes: mush: Volume 12, Issue 28-47
  13.  
  14. #!/bin/sh
  15. # do not concatenate these parts, unpack them in order with /bin/sh
  16. # file hdrs.c continued
  17. #
  18. if test ! -r _shar_seq_.tmp; then
  19.     echo 'Please unpack part 1 first!'
  20.     exit 1
  21. fi
  22. (read Scheck
  23.  if test "$Scheck" != 10; then
  24.     echo Please unpack part "$Scheck" next!
  25.     exit 1
  26.  else
  27.     exit 0
  28.  fi
  29. ) < _shar_seq_.tmp || exit 1
  30. if test ! -f _shar_wnt_.tmp; then
  31.     echo 'x - still skipping hdrs.c'
  32. else
  33. echo 'x - continuing file hdrs.c'
  34. sed 's/^X//' << 'SHAR_EOF' >> 'hdrs.c' &&
  35. X        *b++ = ',', *b++ = ' ';
  36. X        p[lim] = '\0'; /* prevent overflow */
  37. X        (void) strcpy(b, p);
  38. X    }
  39. X    }
  40. X    fix_up_addr(buf);
  41. X    /* p2 used to save boolean value of $metoo */
  42. X    if (!(p2 = do_set(set_options, "metoo"))) {
  43. X    /* Save the original name/addr in case it is the only one */
  44. X    (void) get_name_n_addr(buf, name, addr);
  45. X    take_me_off(buf);
  46. X    }
  47. X    for (p = buf; *p == ',' || isspace(*p); p++)
  48. X    ;
  49. X    if (!*p)
  50. X    if (p2) /* take_me_off() was not done */
  51. X        (void) strcpy(buf, login);
  52. X    else
  53. X        (void) sprintf(buf, "%s <%s>", name, addr);
  54. X    return buf;
  55. }
  56. X
  57. char *
  58. subject_to(n, buf)
  59. register char *buf;
  60. {
  61. X    register char *p;
  62. X    buf[0] = 0; /* make sure it's already null terminated */
  63. X    if (!(p = header_field(n, "subject")))
  64. X    return NULL;
  65. X    if (lcase_strncmp(p, "Re:", 3))
  66. X    (void) strcpy(buf, "Re: ");
  67. X    return strcat(buf, p);
  68. }
  69. X
  70. char *
  71. cc_to(n, buf)
  72. register char *buf;
  73. {
  74. X    register char *p;
  75. X    buf[0] = 0; /* make sure it's already null terminated */
  76. X    if (!(p = header_field(n, "cc")))
  77. X    return NULL;
  78. X    fix_up_addr(p);
  79. X    if (!do_set(set_options, "metoo"))
  80. X    take_me_off(p);
  81. X    return strcpy(buf, p);
  82. }
  83. SHAR_EOF
  84. echo 'File hdrs.c is complete' &&
  85. chmod 0644 hdrs.c ||
  86. echo 'restore of hdrs.c failed'
  87. Wc_c="`wc -c < 'hdrs.c'`"
  88. test 22622 -eq "$Wc_c" ||
  89.     echo 'hdrs.c: original size 22622, current size' "$Wc_c"
  90. rm -f _shar_wnt_.tmp
  91. fi
  92. # ============= init.c ==============
  93. if test -f 'init.c' -a X"$1" != X"-c"; then
  94.     echo 'x - skipping init.c (File already exists)'
  95.     rm -f _shar_wnt_.tmp
  96. else
  97. > _shar_wnt_.tmp
  98. echo 'x - extracting init.c (Text)'
  99. sed 's/^X//' << 'SHAR_EOF' > 'init.c' &&
  100. /* init.c    (c) copyright 1986 (Dan Heller) */
  101. X
  102. /* init.c -- functions and whatnot that initialize everything */
  103. #include "mush.h"
  104. #include <pwd.h>
  105. X
  106. #ifdef BSD
  107. #include <netdb.h>
  108. #endif /* BSD */
  109. X
  110. #if defined(SYSV) && !defined(HPUX)
  111. #include <sys/utsname.h>
  112. #endif /* SYSV && !HPUX */
  113. X
  114. void
  115. init()
  116. {
  117. X    char         *home, *realname, *argv[4];
  118. X    extern char        *getlogin();
  119. X    char        buf[MAXPATHLEN];
  120. #if defined(SYSV) && !defined(HPUX)
  121. X    extern struct passwd *getpwuid();  /* sys-v forgot this in pwd.h! */
  122. X    struct utsname ourhost;
  123. #else
  124. X    char ourhost[128];
  125. #endif /* SYSV && !HPUX */
  126. X    register char     *p;
  127. X    struct passwd     *entry;
  128. X    int            cnt;
  129. #if defined(BSD) || defined(HPUX)
  130. X    struct hostent     *hp;
  131. #endif /* BSD || HPUX */
  132. X
  133. X    home = getenv("HOME");
  134. X    if (realname = getenv("NAME")) {
  135. X    (void) strcpy(buf, realname);
  136. X    }
  137. X    argv[1] = "=";
  138. X    argv[3] = NULL;
  139. X
  140. X    if (!(entry = getpwuid(getuid())))
  141. X    if (p = getlogin())
  142. X        strdup(login, p);
  143. X    else {
  144. X        login = "unknown";
  145. X        print("I don't know you, but that's ok.\n");
  146. X    }
  147. X    else {
  148. X    strdup(login, entry->pw_name);
  149. X    if (!home || !*home)
  150. X        home = entry->pw_dir;
  151. X    if (!realname && (realname = entry->pw_gecos)) {
  152. X        if (p = index(realname, ','))
  153. X        *p = 0;
  154. X        for (p = buf; *realname; realname++)
  155. X        if (*realname == '&')
  156. X            *p++ = upper(*login), p += Strcpy(p, login+1);
  157. X        else
  158. X            *p++ = *realname;
  159. X        *p = 0;
  160. X    }
  161. X    endpwent();
  162. X    }
  163. X    if (!home || !*home || Access(home, W_OK)) {
  164. X    if (home && *home)
  165. X        error(home);
  166. X    else
  167. X        print("No home!? ");
  168. X    print_more("Using \"%s\" as home.\n", ALTERNATE_HOME);
  169. X    } else {
  170. X    argv[0] = "home";
  171. X    argv[2] = home;
  172. X    (void) add_option(&set_options, argv);
  173. X    }
  174. X    if (realname && *realname) {
  175. X    /* realname has already been copied to buf */
  176. X    argv[0] = "realname";
  177. X    argv[2] = buf;
  178. X    (void) add_option(&set_options, argv);
  179. X    }
  180. X    crt = 24;
  181. X    screen = 18;
  182. X    wrapcolumn = 0; /* Default is no wrap */
  183. X    escape = DEF_ESCAPE;
  184. X    prompt = DEF_PROMPT;
  185. X
  186. #if defined(BSD) || defined(HPUX)
  187. X    (void) gethostname(ourhost, sizeof ourhost);
  188. X    if (!(hp = gethostbyname(ourhost))) {
  189. X    if (ourname = (char **)calloc((unsigned)2, sizeof (char *)))
  190. X        strdup(ourname[0], ourhost);
  191. X    } else {
  192. X    int n = 0;
  193. X    cnt = 2; /* 1 for ourhost and 1 for NULL terminator */
  194. X    for (p = hp->h_name; p && *p; p = hp->h_aliases[n++])
  195. X        if (strcmp(ourhost, p)) /* if host name is different */
  196. X        cnt++;
  197. X    if (ourname = (char **)calloc((unsigned)cnt, sizeof (char *))) {
  198. X        ourname[--cnt] = NULL;
  199. X        for (p = hp->h_name; p && *p && n >= 0; p = hp->h_aliases[--n])
  200. X        if (strcmp(ourhost, p)) /* if host name is different */
  201. X            ourname[--cnt] = savestr(p);
  202. X        strdup(ourname[0], ourhost); /* cnt better be 0! */
  203. X    }
  204. X    }
  205. #endif /* BSD || HPUX */
  206. #if defined(SYSV) && !defined(HPUX)
  207. X    if (ourname = (char **)calloc((unsigned)2, sizeof (char *))) {
  208. X    if ((uname (&ourhost) >= 0) && (*ourhost.nodename))
  209. X        ourname[0] = savestr(ourhost.nodename);
  210. X    else {
  211. X        /* Try to use uuname -l to get host's name if uname didn't work */
  212. X        char buff[50];
  213. X        char *p;
  214. X        FILE *F;
  215. X
  216. X        if (F = popen("exec uuname -l", "r")) {
  217. X        if ((fgets(buff, sizeof buff, F) == buff) &&
  218. X            (p = strchr(buff, '\n'))) {
  219. X            *p = '\0';        /* eliminate newline */
  220. X            ourname[0] = savestr (buff);
  221. X        }
  222. X        (void)pclose(F);
  223. X        }
  224. X    }
  225. X    }
  226. #endif /* SYSV && !HPUX */
  227. X    if (ourname && ourname[0]) {
  228. X    for (p = buf, cnt = 0; ourname[cnt]; cnt++) {
  229. X        if (cnt)
  230. X        *p++ = ' ';
  231. X        p += Strcpy(p, ourname[cnt]);
  232. X    }
  233. X    argv[0] = "hostname";
  234. X    argv[2] = buf;
  235. X    (void) add_option(&set_options, argv);
  236. X    }
  237. X
  238. X    init_bindings();
  239. }
  240. X
  241. /*
  242. X * Source a file, or just the default file.  Since sourcing files
  243. X * means reading possible aliases, don't expand the ! as history
  244. X * by setting the IGN_BANG flag.  Since a command in the sourced file
  245. X * may call source on another file, this routine may be called from
  246. X * within itself.  Continue to ignore ! chars by setting save_bang (local).
  247. X *
  248. X * Try opening the file passed to us.  If not given, check for the correct
  249. X * .rc file which is found in the user's home dir.
  250. X *
  251. X * return -1 for filesystem errors, -2 for attempting to read a directory.
  252. X */
  253. source(argc, argv)
  254. char **argv;
  255. {
  256. X    register char *p;
  257. X    FILE      *fp;
  258. X    char       file[MAXPATHLEN];
  259. X    u_long      save_bang = ison(glob_flags, IGN_BANG);
  260. X    int          line_no = 0;
  261. X
  262. X    if (argc && *++argv && !strcmp(*argv, "-?"))
  263. X    return help(0, "source", cmd_help);
  264. X    if (argc && *argv)
  265. X    (void) strcpy(file, *argv);
  266. X    else if ((p = getenv("MUSHRC")) && *p || (p = getenv("MAILRC")) && *p)
  267. X    (void) strcpy(file, p);
  268. X    else {
  269. X    char *home = do_set(set_options, "home");
  270. X    if (!home || !*home)
  271. X        home = ALTERNATE_HOME;
  272. X    if (Access(sprintf(file, "%s/%s", home, MAILRC), R_OK)
  273. X        && Access(sprintf(file, "%s/%s", home, ALTERNATE_RC), R_OK))
  274. X        if (argc || argv)
  275. X        (void) strcpy(file, DEFAULT_RC);
  276. X        else
  277. X        return -1;
  278. X    }
  279. X
  280. X    argc = 0; /* don't ignore ENOENT */
  281. X    p = getpath(file, &argc);
  282. X    /* Try the ALT_DEF_RC if DEFAULT_RC fails */
  283. X    if (argc && !strcmp(file, DEFAULT_RC)) {
  284. X    argc = 0; /* don't ignore ENOENT */
  285. X    (void) strcpy(file, ALT_DEF_RC);
  286. X    p = getpath(file, &argc);
  287. X    }
  288. X    if (argc) {
  289. X    /* Don't print error messages for missing default files */
  290. X    if (strcmp(file, ALT_DEF_RC))
  291. X        if (argc == -1) {
  292. X        print("%s: %s\n", file, p);
  293. X        return -1;
  294. X        } else {
  295. X        print("%s is a directory.\n", file);
  296. X        return -2;
  297. X        }
  298. X    return -1;
  299. X    }
  300. X    if (!(fp = fopen(p, "r"))) {
  301. X    if (errno != ENOENT)
  302. X        error("Can't open %s", p);
  303. X    return -1;
  304. X    }
  305. X    turnon(glob_flags, IGN_BANG); /* ignore ! when reading record files */
  306. X    (void) strcpy(file, p);
  307. X    (void) src_parse(file, fp, 0, 0, &line_no);
  308. X    /* if we entered the routine ignoring !, leave it that way. */
  309. X    if (!save_bang)
  310. X    turnoff(glob_flags, IGN_BANG);
  311. X    /* Sourcing might change things, so abort pipes/macros */
  312. X    return 0 - (in_pipe() || in_macro());
  313. }
  314. X
  315. /*
  316. X * Do the actual file parsing for source().  The first argument should
  317. X * be the name of the file referenced by the second argument.  The third
  318. X * argument is used for handling nested if_else_endif expressions.  The
  319. X * fourth argument is used to keep track of the recursion depth, and the
  320. X * last argument keeps track of the line number in the current file.
  321. X *
  322. X * This function calls itself recursively.  It also calls do_command(),
  323. X * which may in turn call source() recursively.
  324. X *
  325. X * If-then-else nesting algorithm:
  326. X *  On any "if" (whether parsing or not), increment if_else
  327. X *  On true "if" when parsing, evaluate by recursion
  328. X *  On false "if" when parsing, set find_else equal to if_else
  329. X *  On any "if" when not parsing, set find_endif equal to if_else
  330. X *  On "else", invert parsing only when find_else equals if_else
  331. X *  When "if" was false and there is nesting, recur for "else"
  332. X *  Skip nested "if...endif" when find_else or find_endif true
  333. X *  On "endif" or when recursion returns, decrement if_else
  334. X *  On "endif", test both find_endif and find_else against if_else:
  335. X *   when either matches, reset that one;
  336. X *   when the lesser (less nested) matches, resume parsing
  337. X *  On "endif", when if_else hits 0, continue (depth 0) or return
  338. X */
  339. src_parse(file, fp, if_else, depth, line_no)
  340. char    *file;
  341. FILE    *fp;
  342. int      if_else, depth, *line_no;
  343. {
  344. X    register char *p, *p2, **newargv;
  345. X    static int    exited;
  346. X    int       parsing = 1, cont_line = 0;
  347. X    int          find_else = 0, find_endif = 0;
  348. X    char       line[BUFSIZ];
  349. X    int          argc;
  350. X
  351. X    exited = 0;
  352. X
  353. X    while (p = fgets(&line[cont_line], BUFSIZ - cont_line, fp)) {
  354. X    (*line_no)++;
  355. X    if (*(p2 = no_newln(p)) == '\\') {
  356. X        *p2++ = ' ';
  357. X        cont_line = p2 - line;
  358. X        continue;
  359. X    } else
  360. X        cont_line = 0;
  361. X    /* don't consider comments (#) in lines. check if # is within quotes */
  362. X    if (p = any(line, "\"'#\\")) {
  363. X        register int balanced = 1;
  364. X        do {
  365. X        if (*p == '\\' && p[1])
  366. X            p = any(p+2, "\"'#\\");
  367. X        else if (*p != '#') {
  368. X            /* first find matching quote */
  369. X            register char *quote = index(p+1, *p);
  370. X            if (!quote) {
  371. X            print("%s: line %d: unbalanced %c.\n",
  372. X                file, *line_no, *p);
  373. X            balanced = 0;
  374. X            } else
  375. X            p = any(quote+1, "\"'#\\");
  376. X        }
  377. X        } while (p && *p != '#' && balanced);
  378. X        if (!balanced)
  379. X        continue;
  380. X        if (p && *p == '#')
  381. X        *p = 0; /* found a Comment: null terminate line at comment */
  382. X    }
  383. X    if (!*line || !parsing && !(newargv = mk_argv(line, &argc, 0))
  384. X    || parsing && !(newargv = make_command(line, TRPL_NULL, &argc))) {
  385. X        if (!strncmp(line, "if", 2))
  386. X        find_else = ++if_else, parsing = FALSE;
  387. X        continue;
  388. X    }
  389. X    if (!strcmp(newargv[0], "endif")) {
  390. X        if (!if_else)
  391. X        print("%s: line %d: endif with no \"if\".\n", file, *line_no);
  392. X        else {
  393. X        /* If looking for an else or endif, reset parsing */
  394. X        if (find_endif && find_endif == if_else) {
  395. X            if (find_endif <= find_else || !find_else)
  396. X            parsing = 1, find_else = 0;
  397. X            find_endif = 0;
  398. X        }
  399. X        /* Note: find_else never < find_endif */
  400. X        if (find_else && find_else == if_else)
  401. X            parsing = !parsing, find_else = 0;
  402. X        /* Decrement if_else and check depth */
  403. X        if (--if_else == 0)
  404. X            /* Resume parsing if at the top */
  405. X            if (depth == 0)
  406. X            parsing = 1;
  407. X            /* Return if not at the top */
  408. X            else
  409. X            return 1;
  410. X        }
  411. X        goto bad;
  412. X    } else if (!strcmp(newargv[0], "else")) {
  413. X        if (!if_else)
  414. X        print("%s: line %d: if-less \"else\".\n", file, *line_no);
  415. X        /* If inside an else, ignore nested else;
  416. X         *  otherwise, recur when if_else > 1 */
  417. X        else if (!find_else && !find_endif && !parsing) {
  418. X        parsing = src_parse(file, fp, 1, depth + 1, line_no);
  419. X        --if_else;
  420. X        } else if (find_else == if_else || if_else == 1) {
  421. X        find_else = 0;
  422. X        parsing = !parsing;
  423. X        if (!parsing)
  424. X            find_endif = if_else;
  425. X        }
  426. X        goto bad;
  427. X    } else if (!strcmp(newargv[0], "if")) {
  428. X        /* if statements are of the form:
  429. X         *     if expr
  430. X         *     if !expr  or  if ! expr
  431. X         *     if expr == expr   or   if expr != expr
  432. X         */
  433. X        int equals = TRUE, pattern = FALSE;
  434. X        register char *lhs = newargv[1], *rhs = NULL;
  435. X
  436. X        if_else++;
  437. X        /* If parsing, set parsing to 0 until
  438. X         *  evaluating the "if" proves otherwise.
  439. X         * If not parsing, skip to the "endif".
  440. X         */
  441. X        if (parsing)
  442. X        parsing = 0;
  443. X        else {
  444. X        if (!find_endif)
  445. X            find_endif = if_else;
  446. X        goto bad;
  447. X        }
  448. X        if (!lhs || !*lhs) {
  449. X        print("%s: line %d: if what?\n", file, *line_no);
  450. X        goto bad;
  451. X        }
  452. X        /* "lhs" is the left hand side of the equation
  453. X         * In this instance, we're doing case 2 above (check for negation).
  454. X         */
  455. X        if (*lhs == '!') {
  456. X        if (!*++lhs && !(lhs = newargv[2])) {
  457. X            print("%s: line %d: syntax error: \"if ! <what?>\"\n",
  458. X            file, *line_no);
  459. X            goto bad;
  460. X        }
  461. X        equals = FALSE;
  462. X        }
  463. X        if (*lhs == '-' && (lhs[1] == 'e' || lhs[1] == 'z') && !lhs[2]) {
  464. X        char *path;
  465. X        int n = 1; /* ignore ENOENT, I'll handle it here */
  466. X        struct stat statb;
  467. X
  468. X        /* check for existence or zero-length folders/files */
  469. X        if (argc > 3 + (!equals)) {
  470. X            print("%s: line %d: if %s \"filename\"\n",
  471. X            file, *line_no, lhs);
  472. X            goto bad;
  473. X        }
  474. X        path = getpath(newargv[argc-1], &n);
  475. X        parsing = !equals ^ (n == -1 || n == 1 && lhs[1] == 'e' ||
  476. X            !stat(path, &statb) && (lhs[1] == 'e' || !statb.st_size));
  477. X        } else {
  478. X        if (equals && argc > 2) {
  479. X            if (argc != 4) {
  480. X            print("%s: %d: argument count error: %d args.\n",
  481. X                file, *line_no, argc);
  482. X            goto bad;
  483. X            }
  484. X            /* now check newargv[2] for == or != or =~ or !~ */
  485. X            if (!strcmp(newargv[2], "!=") ||
  486. X                (pattern = !strcmp(newargv[2], "!~")))
  487. X            equals = !equals;
  488. X            else if (!strcmp(newargv[2], "=~"))
  489. X            pattern = TRUE;
  490. X            else if (strcmp(newargv[2], "==")) {
  491. X            print("%s: %d: use `==' or `!=' only.\n",
  492. X                file, *line_no);
  493. X            goto bad;
  494. X            }
  495. X            rhs = newargv[3];
  496. X        }
  497. X        if (rhs) {
  498. X            /* Some fun tricks with booleans here.
  499. X             * Extra ! ops make sure all == are on 0 or 1;
  500. X             * aside from that, we want (glob == equals)
  501. X             * or (!strcmp == equals).  Make sense?  
  502. X             */
  503. X            if (pattern && !glob(lhs,rhs) == !equals)
  504. X            parsing = 1;
  505. X            else if (!pattern && !strcmp(lhs, rhs) == !!equals)
  506. X            parsing = 1;
  507. X        } else if (isdigit(*lhs))
  508. X            parsing = !!(atoi(lhs) ? equals : !equals);
  509. X        else if (!strcmp(lhs, "redirect") && (!isatty(0) != !equals)
  510. X              /* (ison(glob_flags, REDIRECT) && equals ||
  511. X               isoff(glob_flags, REDIRECT) && !equals) */
  512. X            || !strcmp(lhs, "is_shell") && (!is_shell == !equals)
  513. X            || !strcmp(lhs, "is_sending") &&
  514. X              (ison(glob_flags, IS_SENDING) && equals ||
  515. X               isoff(glob_flags, IS_SENDING) && !equals)
  516. X            || !strcmp(lhs, "hdrs_only") &&
  517. X              (hdrs_only && equals || !hdrs_only && !equals)
  518. X            || !strcmp(lhs, "istool") &&
  519. X              (istool && equals || !istool && !equals)
  520. X            || !strcmp(lhs, "iscurses") &&
  521. X              ((iscurses || ison(glob_flags, PRE_CURSES)) && equals
  522. X              || (isoff(glob_flags, PRE_CURSES) &&
  523. X                  !iscurses && !equals)))
  524. X            parsing = 1;
  525. X        }
  526. X        if (parsing) {
  527. X        parsing = src_parse(file, fp, 1, depth + 1, line_no);
  528. X        --if_else;
  529. X        }
  530. X        else
  531. X        find_else = if_else; /* Look for a matching else */
  532. bad:
  533. X        free_vec(newargv);
  534. X        continue;
  535. X    }
  536. X    if (parsing && argc > 0)
  537. X        if (!strcmp(newargv[0], "exit")) {
  538. X        if_else = find_else = find_endif = 0;
  539. X        exited = 1;
  540. X        break;
  541. X        } else {
  542. X        (void) do_command(argc, newargv, msg_list);
  543. X        exited = 0;
  544. X        }
  545. X    else
  546. X        free_vec(newargv);
  547. X    }
  548. X    if (if_else && !exited)
  549. X    print("%s: missing endif\n", file);
  550. X    if (depth == 0)
  551. X    (void) fclose(fp);
  552. X    else
  553. X    (void) fseek(fp, 0L, 2); /* Skip ahead to the end */
  554. X    return 0;
  555. }
  556. SHAR_EOF
  557. chmod 0644 init.c ||
  558. echo 'restore of init.c failed'
  559. Wc_c="`wc -c < 'init.c'`"
  560. test 13558 -eq "$Wc_c" ||
  561.     echo 'init.c: original size 13558, current size' "$Wc_c"
  562. rm -f _shar_wnt_.tmp
  563. fi
  564. # ============= lock.c ==============
  565. if test -f 'lock.c' -a X"$1" != X"-c"; then
  566.     echo 'x - skipping lock.c (File already exists)'
  567.     rm -f _shar_wnt_.tmp
  568. else
  569. > _shar_wnt_.tmp
  570. echo 'x - extracting lock.c (Text)'
  571. sed 's/^X//' << 'SHAR_EOF' > 'lock.c' &&
  572. /*
  573. X * lock.c -- deal with file locking on various architectures and UNIXs.
  574. X * dot_lock() creates a file with the same name as the parameter passed
  575. X * with the appendage ".lock" -- this is to be compatible with certain
  576. X * systems that don't use flock or lockf or whatever they have available
  577. X * that they don't use.
  578. X */
  579. X
  580. #ifdef USG
  581. #include <unistd.h>
  582. #endif /* USG */
  583. #include "mush.h"
  584. #if defined(SYSV) && !defined(USG)
  585. #include <sys/locking.h>
  586. #endif /* SYSV && !USG */
  587. X
  588. #ifdef DOT_LOCK
  589. extern int sgid;
  590. #ifdef BSD
  591. extern int rgid;
  592. #endif /* BSD */
  593. X
  594. dot_lock(filename)
  595. char *filename;
  596. {
  597. X    char buf[MAXPATHLEN];
  598. X    int lockfd, cnt = 0;
  599. X    SIGRET (*oldint)(), (*oldquit)();
  600. X
  601. #ifdef SYSV
  602. X    /* Only the spoolfile needs to be dot_locked -- other files are
  603. X     * handled by lock_fopen, below.  To avoid collisions with 14-char
  604. X     * file name limits, we allow dot_locking ONLY of the spoolfile.
  605. X     */
  606. X    if (strcmp(spoolfile, filename) != 0)
  607. X    return 0;
  608. #endif
  609. #ifdef BSD
  610. X    setregid(rgid, sgid);
  611. #else /* BSD */
  612. X    setgid(sgid);
  613. #endif /* BSD */
  614. #ifdef M_XENIX
  615. X    (void) sprintf(buf, "/tmp/%.10s.mlk", login);
  616. #else /* M_XENIX */
  617. X    (void) sprintf(buf, "%s.lock", filename);
  618. #endif /* M_XENIX */
  619. X    on_intr();
  620. X    while ((lockfd = open(buf, O_CREAT|O_WRONLY|O_EXCL, 0444)) == -1) {
  621. X    if (errno != EEXIST) {
  622. X        error("unable to lock %s", filename);
  623. X        break;
  624. X    }
  625. X    if (cnt++ == 0)
  626. X        print("%s already locked, waiting", filename);
  627. X    else
  628. X        print_more(".");
  629. X    sleep(1);
  630. X    if (ison(glob_flags, WAS_INTR)) {
  631. X        print_more("\nAborted.\n");
  632. X        break;
  633. X    }
  634. X    }
  635. X    off_intr();
  636. X    if (lockfd != -1) {
  637. X    if (cnt)
  638. X        print("done.\n");
  639. X    (void) close(lockfd);
  640. X    }
  641. #ifdef BSD
  642. X    setregid(sgid, rgid);
  643. #else
  644. X    setgid(getgid());
  645. #endif /* BSD */
  646. X    return lockfd == -1? -1 : 0;
  647. }
  648. #endif /* DOT_LOCK */
  649. X
  650. #ifdef SYSV
  651. X
  652. /*
  653. X * Define some BSD names for the SYSV world
  654. X */
  655. #ifdef USG
  656. #define LOCK_SH F_RDLCK
  657. #define LOCK_EX F_WRLCK
  658. #define LOCK_UN F_UNLCK
  659. #else /* USG */
  660. #define LOCK_SH LK_LOCK
  661. #define LOCK_EX LK_LOCK
  662. #define LOCK_UN LK_UNLCK
  663. #endif /* USG */
  664. #define LOCK_NB 0    /* Always non-blocking in this case */
  665. X
  666. #ifdef EWOULDBLOCK
  667. #undef EWOULDBLOCK
  668. #endif /* EWOULDBLOCK */
  669. #ifdef M_UNIX
  670. #define EWOULDBLOCK    EACCESS    /* SCO bug that may eventually be fixed */
  671. #else /* !M_UNIX */
  672. #define EWOULDBLOCK    EAGAIN
  673. #endif /* M_UNIX */
  674. X
  675. #ifndef F_SETLKW
  676. #define F_SETLKW F_SETLK
  677. #endif /* F_SETLKW */
  678. X
  679. flock(fd, op)
  680. int fd, op;
  681. {
  682. #ifndef USG
  683. X    (void) locking(fd, op, 0); /* old xenix (sys III) */
  684. X    return 0;
  685. #else
  686. X    struct flock l;
  687. X
  688. X    l.l_len = 0L;
  689. X    l.l_start = 0L;
  690. X    l.l_whence = 1;
  691. X    l.l_type = op;
  692. X
  693. X    return fcntl(fd, F_SETLKW, &l);
  694. #endif /* USG */
  695. }
  696. X
  697. #endif /* SYSV */
  698. X
  699. static struct options *exclude_list;
  700. X
  701. /* Quick'n'dirty test to avoid opening the same file multiple times.
  702. X * Fails if we aren't passed full paths or if the file is known by
  703. X * more than one name, but you can't have everything.
  704. X */
  705. static FILE *
  706. exclusive_fopen(filename, mode)
  707. char *filename, *mode;
  708. {
  709. X    struct options *tmp;
  710. X    FILE *fp;
  711. X    
  712. X    for (tmp = exclude_list; tmp; tmp = tmp->next)
  713. X    if (strcmp(tmp->option, filename) == 0) {
  714. X        errno = EWOULDBLOCK;
  715. X        return NULL_FILE;
  716. X    }
  717. X    if (!(fp = mask_fopen(filename, mode)))
  718. X    return NULL_FILE;
  719. X    if (tmp = (struct options *)malloc(sizeof(struct options))) {
  720. X    tmp->option = savestr(filename);
  721. X    tmp->value = (char *)fp;
  722. X    /*
  723. X     * NOTE: The LCKDFLDIR code below depends on this stackwise
  724. X     * insertion to be able to close/reopen the file pointer.
  725. X     * These routines therefore cannot cleanly be used outside
  726. X     * of lock_fopen() and close_lock(), which handle LCKDFLDIR.
  727. X     */
  728. X    tmp->next = exclude_list;
  729. X    exclude_list = tmp;
  730. X    return fp;
  731. X    } else
  732. X    (void) fclose(fp);
  733. X    return NULL_FILE;
  734. }
  735. X
  736. static int
  737. exclusive_fclose(fileptr)
  738. FILE *fileptr;
  739. {
  740. X    struct options *tmp1, *tmp2;
  741. X    int n = 0;
  742. X    
  743. X    for (tmp1 = tmp2 = exclude_list; tmp1; tmp2 = tmp1, tmp1 = tmp1->next)
  744. X    if ((FILE *)(tmp1->value) == fileptr) {
  745. X        if (tmp1 == tmp2)
  746. X        exclude_list = tmp1->next;
  747. X        else
  748. X        tmp2->next = tmp1->next;
  749. X        xfree(tmp1->option);
  750. #ifndef LCKDFLDIR
  751. X        /* LCKDFLDIR needs lk_fclose(), so let caller do it */
  752. X        n = fclose(fileptr);
  753. #endif /* !LCKDFLDIR */
  754. X        xfree(tmp1);
  755. X        break;
  756. X    }
  757. X    return n;
  758. }
  759. X
  760. FILE *
  761. lock_fopen(filename, mode)
  762. char *filename;
  763. char *mode;
  764. {
  765. X    FILE *mail_fp = NULL_FILE;
  766. X    struct options exclude;
  767. X    int fd, lk;
  768. X    int cnt = 0;
  769. X    SIGRET (*oldint)(), (*oldquit)();
  770. #ifdef LCKDFLDIR
  771. X    extern FILE *lk_fopen();
  772. #endif /* !LCKDFLDIR */
  773. X
  774. X    if (debug && do_set(set_options, "deadlock")) {
  775. X    (void) un_set(&set_options, "deadlock");
  776. X    return NULL_FILE;
  777. X    }
  778. X
  779. #ifdef DOT_LOCK
  780. X    if (dot_lock(filename) == 0)
  781. #endif /* DOT_LOCK */
  782. X    mail_fp = exclusive_fopen(filename, mode);
  783. X    if (!mail_fp)
  784. X    return NULL_FILE;
  785. X    fd = fileno(mail_fp);
  786. X
  787. X    if (mode[0] != 'r' || mode[1] == '+')
  788. X    lk = LOCK_EX | LOCK_NB;
  789. X    else
  790. X    lk = LOCK_SH | LOCK_NB;
  791. X
  792. X    on_intr();
  793. #ifdef LCKDFLDIR
  794. X    (void) fclose(mail_fp);
  795. X    while (isoff(glob_flags, WAS_INTR))
  796. X    if (mail_fp = lk_fopen(filename, mode, NULL, NULL, 0)) {
  797. X        /* See note in exclusive_fopen() above */
  798. X        exclude_list->value = (char *)mail_fp;
  799. X        break;
  800. X    } else /* uses the open brace below the #endif LCKDFLDIR */
  801. #else /* !LCKDFLDIR */
  802. X    while (isoff(glob_flags, WAS_INTR) && flock(fd, lk))
  803. #endif /* LCKDFLDIR */
  804. X    {
  805. #ifdef LCKDFLDIR
  806. X    if (Access(filename, any(mode, "aw+") ? W_OK : R_OK) == 0)
  807. #else /* !LCKDFLDIR */
  808. X    if (errno == EWOULDBLOCK)
  809. #endif /* LCKDFLDIR */
  810. X    {
  811. X        if (isoff(glob_flags, REDIRECT))
  812. X        if (!cnt++)
  813. X            print("\nwaiting to lock");
  814. X        else
  815. X            print(".");
  816. X    } else {
  817. X        error("Unable to lock \"%s\"", filename);
  818. X        exclusive_fclose(mail_fp);
  819. X        off_intr();
  820. X        return NULL_FILE;
  821. X    }
  822. X    (void) fflush(stdout);
  823. X    sleep(1);
  824. X    }
  825. X    if (cnt)
  826. X    print("\n");
  827. X    cnt = (ison(glob_flags, WAS_INTR) != 0);
  828. X    off_intr();
  829. X    if (cnt) {
  830. X    exclusive_fclose(mail_fp);
  831. X    return NULL_FILE;
  832. X    }
  833. X    return mail_fp;
  834. }
  835. X
  836. /*ARGSUSED*/
  837. close_lock(filename, fp)
  838. char *filename;
  839. FILE *fp;
  840. #ifdef LCKDFLDIR
  841. {
  842. X    (void) exclusive_fclose(fp); /* Only removes the list elem */
  843. X    return lk_fclose(fp, filename, NULL, NULL);
  844. }
  845. #else /* !LCKDFLDIR */
  846. {
  847. #ifdef DOT_LOCK
  848. X    char buf[MAXPATHLEN];
  849. #endif /* DOT_LOCK */
  850. X
  851. X    fflush(fp);
  852. #ifdef DOT_LOCK
  853. #ifdef BSD
  854. X    setregid(rgid, sgid);
  855. #else
  856. X    setgid(sgid);
  857. #endif /* BSD */
  858. #ifdef SYSV
  859. X    if (strcmp(spoolfile, filename) == 0)
  860. #endif /* SYSV */
  861. #ifdef M_XENIX
  862. X    (void) unlink(sprintf(buf, "/tmp/%.10s.mlk", login));
  863. #else /* M_XENIX */
  864. X    {
  865. X    /* If the file was locked through open_file(), we may not have
  866. X     * a complete pathname to work with here.  Expand it and test
  867. X     * whether we need to unlink at all.  This should really be
  868. X     * handled by having open_file() return the name it used, but
  869. X     * that breaks too many other things at the moment.
  870. X     */
  871. X    int isdir = 0;
  872. X    char *p = getpath(sprintf(buf, "%s.lock", filename), &isdir);
  873. X    if (isdir == 0)
  874. X        (void) unlink(p);
  875. X    }
  876. #endif /* M_XENIX */
  877. #ifdef BSD
  878. X    setregid(sgid, rgid);
  879. #else
  880. X    setgid(getgid());
  881. #endif /* BSD */
  882. #endif /* DOT_LOCK */
  883. X
  884. X    (void) flock(fileno(fp), LOCK_UN);
  885. X    return exclusive_fclose(fp);
  886. }
  887. #endif /* LCKDFLDIR */
  888. SHAR_EOF
  889. chmod 0644 lock.c ||
  890. echo 'restore of lock.c failed'
  891. Wc_c="`wc -c < 'lock.c'`"
  892. test 7206 -eq "$Wc_c" ||
  893.     echo 'lock.c: original size 7206, current size' "$Wc_c"
  894. rm -f _shar_wnt_.tmp
  895. fi
  896. # ============= loop.c ==============
  897. if test -f 'loop.c' -a X"$1" != X"-c"; then
  898.     echo 'x - skipping loop.c (File already exists)'
  899.     rm -f _shar_wnt_.tmp
  900. else
  901. > _shar_wnt_.tmp
  902. echo 'x - extracting loop.c (Text)'
  903. sed 's/^X//' << 'SHAR_EOF' > 'loop.c' &&
  904. /* loop.c     (c) copyright 1986 (Dan Heller) */
  905. X
  906. /*
  907. X * Here is where the main loop for text mode exists. Also, all the
  908. X * history is kept here and all the command parsing and execution
  909. X * and alias expansion in or out of text/graphics mode is done here.
  910. X */
  911. X
  912. #include "mush.h"
  913. #include "version.h"
  914. X
  915. #ifdef BSD
  916. #include <sys/wait.h>
  917. #else
  918. #ifndef SYSV
  919. #include <wait.h>
  920. #endif /* SYSV */
  921. #endif /* BSD */
  922. X
  923. #define ever (;;)
  924. #define MAXARGS        100
  925. #define isdelimeter(c)    (index(" \t;|", c))
  926. X
  927. char *alias_expand(), *hist_expand(), *reference_hist(), *hist_from_str();
  928. char *calloc();
  929. X
  930. struct history {
  931. X    int histno;
  932. X    char **argv;
  933. X    struct history *prev;
  934. X    struct history *next;
  935. };
  936. static struct history *hist_head, *hist_tail;
  937. #define malloc(n)    (struct history *)calloc((unsigned)1,(unsigned)(n))
  938. #define NULL_HIST    (struct history *)0
  939. X
  940. static char *last_aliased;
  941. static int hist_size, print_only;
  942. X
  943. do_loop()
  944. {
  945. X    register char *p, **argv;
  946. X    char      **last_argv = DUBL_NULL, line[256];
  947. X    int         argc, c = (iscurses - 1);
  948. #ifdef CURSES
  949. X    int          save_echo_flg = FALSE;
  950. #endif /* CURSES */
  951. X
  952. X    /* catch the right signals -- see main.c for other signal catching */
  953. X    (void) signal(SIGINT, catch);
  954. X    (void) signal(SIGQUIT, catch);
  955. X    (void) signal(SIGHUP, catch);
  956. X    (void) signal(SIGTERM, catch);
  957. X    (void) signal(SIGCHLD,
  958. #ifndef SYSV
  959. X               sigchldcatcher
  960. #else /* SYSV */
  961. X               SIG_DFL
  962. #endif /* SYSV */
  963. X               );
  964. X
  965. X    turnoff(glob_flags, IGN_SIGS);
  966. X    if (hist_size == 0) /* if user didn't set history in .rc file */
  967. X    hist_size = 1;
  968. X
  969. X    for ever {
  970. X    if (setjmp(jmpbuf)) {
  971. X        Debug("jumped back to main loop (%s: %d)\n", __FILE__,__LINE__);
  972. #ifdef CURSES
  973. X        if (c > 0) { /* don't pass last command back to curses_command() */
  974. X        iscurses = TRUE;
  975. X        c = hit_return();
  976. X        }
  977. #endif /* CURSES */
  978. X    }
  979. #ifdef CURSES
  980. X    if (iscurses || c > -1) {
  981. X        /* if !iscurses, we know that we returned from a curses-based
  982. X         * call and we really ARE still in curses. Reset tty modes!
  983. X         */
  984. X        if (ison(glob_flags, ECHO_FLAG)) {
  985. X        turnoff(glob_flags, ECHO_FLAG);
  986. X        echo_off();
  987. X        save_echo_flg = TRUE;
  988. X        }
  989. X        if (!iscurses) {
  990. X        iscurses = TRUE;
  991. X        c = hit_return();
  992. X        }
  993. X        if (c < 0)
  994. X        c = 0;
  995. X        if ((c = curses_command(c)) == -1 && save_echo_flg) {
  996. X        echo_on();
  997. X        turnon(glob_flags, ECHO_FLAG);
  998. X        save_echo_flg = FALSE;
  999. X        }
  1000. X        continue;
  1001. X    }
  1002. #endif /* CURSES */
  1003. X    clear_msg_list(msg_list);
  1004. X    (void) check_new_mail();
  1005. X
  1006. X    /* print a prompt according to printf like format:
  1007. X     * (current message, deleted, unread, etc) are found in mail_status.
  1008. X     */
  1009. X    mail_status(1);
  1010. X    if (Getstr(line, sizeof(line), 0) > -1)
  1011. X        p = line;
  1012. X    else {
  1013. X        if (isatty(0) && (p = do_set(set_options, "ignoreeof"))) {
  1014. X        if (!*p)
  1015. X            continue;
  1016. X        else
  1017. X            p = strcpy(line, p); /* so processing won't destroy var */
  1018. X        } else {
  1019. X        putchar('\n');
  1020. X        (void) mush_quit(0, DUBL_NULL);
  1021. X        continue; /* quit may return if new mail arrives */
  1022. X        }
  1023. X    }
  1024. X
  1025. X    skipspaces(0);
  1026. X    if (!*p && !(p = do_set(set_options, "newline"))) {
  1027. X        (void) readmsg(0, DUBL_NULL, msg_list);
  1028. X        continue;
  1029. X    }
  1030. X    if (!*p) /* if newline is set, but no value, then continue */
  1031. X        continue;
  1032. X
  1033. X    /* upon error, argc = -1 -- still save in history so user can
  1034. X     * modify syntax error. if !argv, error is too severe.  We pass
  1035. X     * the last command typed in last_argv for history reference, and
  1036. X     * get back the current command _as typed_ (unexpanded by aliases
  1037. X     * or history) in last_argv.
  1038. X     */
  1039. X    if (!(argv = make_command(p, &last_argv, &argc)))
  1040. X        continue;
  1041. X    /* now save the old argv in a newly created history structure */
  1042. X    (void) add_history(0, last_argv); /* argc is currently ignored */
  1043. X
  1044. X    if (print_only) {
  1045. X        print_only = 0;
  1046. X        free_vec(argv);
  1047. X    } else if (argc > -1)
  1048. X        (void) do_command(argc, argv, msg_list);
  1049. X    }
  1050. }
  1051. X
  1052. /* Add a command to the history list
  1053. X */
  1054. /*ARGSUSED*/
  1055. add_history(un_used, argv)
  1056. char **argv;
  1057. {
  1058. X    struct history *new;
  1059. X
  1060. X    if (!(new = malloc(sizeof (struct history))))
  1061. X    error("can't increment history");
  1062. X    else {
  1063. X    new->histno = ++hist_no;
  1064. X    new->argv = argv;    /* this is the command _as typed_ */
  1065. X    new->next = NULL_HIST;
  1066. X    new->prev = hist_head;
  1067. X    /* if first command, the tail of the list is "new" because
  1068. X     * nothing is in the list.  If not the first command, the
  1069. X     * head of the list's "next" pointer points to the new command.
  1070. X     */
  1071. X    if (hist_head)
  1072. X        hist_head->next = new;
  1073. X    else
  1074. X        hist_tail = new;
  1075. X    hist_head = new;
  1076. X    }
  1077. X    /*
  1078. X     * truncate the history list to the size of the history.
  1079. X     * Free the outdated command (argv) and move the tail closer to front.
  1080. X     * use a while loop in case the last command reset histsize to "small"
  1081. X     */
  1082. X    while (hist_head->histno - hist_tail->histno >= hist_size) {
  1083. X    hist_tail = hist_tail->next;
  1084. X    free_vec(hist_tail->prev->argv);
  1085. X    xfree((char *) (hist_tail->prev));
  1086. X    hist_tail->prev = NULL_HIST;
  1087. X    }
  1088. }
  1089. X
  1090. /* make a command from "buf".
  1091. X * first, expand history references. make an argv from that and save
  1092. X * in last_argv (to be passed back and stored in history). After that,
  1093. X * THEN expand aliases. return that argv to be executed as a command.
  1094. X */
  1095. char **
  1096. make_command(start, last_argv, argc)
  1097. register char *start, ***last_argv;
  1098. int *argc;
  1099. {
  1100. X    register char *p, **tmp;
  1101. X    char buf[BUFSIZ];
  1102. X
  1103. X    if (!last_argv)
  1104. X    tmp = DUBL_NULL;
  1105. X    else
  1106. X    tmp = *last_argv;
  1107. X    /* first expand history -- (here's where argc gets set)
  1108. X     * pass the buffer, the history list to reference if \!* (or whatever)
  1109. X     * result in static buffer (pointed to by p) -- even if history parsing is
  1110. X     * ignored, do this to remove \'s behind !'s and verifying matching quotes
  1111. X     */
  1112. X    if (!(p = hist_expand(start, tmp, argc)) || Strcpy(buf, p) > sizeof buf)
  1113. X    return DUBL_NULL;
  1114. X    /* if history was referenced in the command, echo new command */
  1115. X    if (*argc)
  1116. X    puts(buf);
  1117. X
  1118. X    /* argc may == -1; ignore this error for now but catch it later */
  1119. X    if (!(tmp = mk_argv(buf, argc, 0)))
  1120. X    return DUBL_NULL;
  1121. X
  1122. X    /* save this as the command typed */
  1123. X    if (last_argv)
  1124. X    *last_argv = tmp;
  1125. X
  1126. X    /* expand all aliases (recursively)
  1127. X     * pass _this_ command (as typed and without aliases) to let aliases
  1128. X     * with "!*" be able to reference the command line just typed.
  1129. X     */
  1130. X    if (alias_stuff(buf, *argc, tmp) == -1)
  1131. X    return DUBL_NULL;
  1132. X
  1133. X    if (!last_argv)
  1134. X    free_vec(tmp);
  1135. X
  1136. X    /* with everything expanded, build final argv from new buffer
  1137. X     * Note that backslashes and quotes still exist. Those are removed
  1138. X     * because argument final is 1.
  1139. X     */
  1140. X    tmp = mk_argv(buf, argc, 1);
  1141. X    return tmp;
  1142. }
  1143. X
  1144. /* Return values from commands, see check_internal() */
  1145. static int last_status;            /* Changes after every command */
  1146. static char last_output[MAXMSGS];    /* Changes after SUCCESSFUL command */
  1147. X
  1148. /*
  1149. X * do the command specified by the argument vector, argv.
  1150. X * First check to see if argc < 0. If so, someone called this
  1151. X * command and they should not have! make_command() will return
  1152. X * an argv but it will set argc to -1 if there's a syntax error.
  1153. X */
  1154. do_command(argc, argv, list)
  1155. char **argv, list[];
  1156. {
  1157. X    register char *p;
  1158. X    char **tmp = argv, *next_cmd = NULL;
  1159. X    int i, status = 0;
  1160. X    long do_pipe = ison(glob_flags, DO_PIPE);
  1161. X
  1162. X    if (argc <= 0) {
  1163. X    turnoff(glob_flags, DO_PIPE);
  1164. X    return -1;
  1165. X    }
  1166. X
  1167. X    clear_msg_list(list);
  1168. X
  1169. X    for (i = 0; do_pipe >= 0 && argc; argc--) {
  1170. X    p = argv[i];
  1171. X    /* mk_argv inserts a boolean in argv[i][2] for separators */
  1172. X    if ((!strcmp(p, "|") || !strcmp(p, ";")) && p[2]) {
  1173. X        if (do_pipe = (*p == '|'))
  1174. X        turnon(glob_flags, DO_PIPE);
  1175. X        else if (next_cmd = argv[i+1])
  1176. X        argv[i+1] = NULL, argc--;
  1177. X        argv[i] = NULL;
  1178. X        if ((status = exec_argv(i, argv, list)) <= -1)
  1179. X        mac_flush();
  1180. X        else
  1181. X        list_to_str(list, last_output);
  1182. X        turnon(glob_flags, IGN_SIGS); /* prevent longjmp */
  1183. X        /* if piping, then don't call next command if this one failed. */
  1184. X        if (status <= -1 && do_pipe) {
  1185. X        print("Broken pipe.\n");
  1186. X        do_pipe = -1, turnoff(glob_flags, DO_PIPE);
  1187. X        }
  1188. X        last_status = status;
  1189. X        /* if command failed and piping, or command worked and not piping */
  1190. X        if (do_pipe <= 0)
  1191. X        status = 0, clear_msg_list(list);
  1192. X        /* else command worked and piping: set is_pipe */
  1193. X        else if (!status)
  1194. X        turnon(glob_flags, IS_PIPE), turnoff(glob_flags, DO_PIPE);
  1195. X        argv[i] = p;
  1196. X        argv += (i+1);
  1197. X        i = 0;
  1198. X        turnoff(glob_flags, IGN_SIGS);
  1199. X    } else
  1200. X        i++;
  1201. X    }
  1202. X    if (*argv && do_pipe >= 0) {
  1203. X    status = exec_argv(i, argv, list);
  1204. X    turnon(glob_flags, IGN_SIGS);
  1205. X    if (status < 0) {
  1206. X        mac_flush();
  1207. X    } else
  1208. X        list_to_str(list, last_output);
  1209. X    last_status = status;
  1210. X    }
  1211. X    Debug("freeing: "), print_argv(tmp);
  1212. X    free_vec(tmp);
  1213. X    turnoff(glob_flags, DO_PIPE), turnoff(glob_flags, IS_PIPE);
  1214. X    if (next_cmd) {
  1215. X    if (tmp = mk_argv(next_cmd, &argc, 1)) {
  1216. X        turnoff(glob_flags, IGN_SIGS);
  1217. X        status = do_command(argc, tmp, list);
  1218. X        turnon(glob_flags, IGN_SIGS);
  1219. X    } else
  1220. X        status = argc;
  1221. X    xfree(next_cmd);
  1222. X    }
  1223. X    turnoff(glob_flags, IGN_SIGS);
  1224. X    return status;
  1225. }
  1226. X
  1227. exec_argv(argc, argv, list)
  1228. register char **argv, list[];
  1229. {
  1230. X    register int n;
  1231. X
  1232. X    if (!argv || !*argv || argv[0][0] == '\\' && !argv[0][1]) {
  1233. X    if (ison(glob_flags, IS_PIPE))
  1234. X        print("Invalid null command.\n");
  1235. X    else if (ison(glob_flags, DO_PIPE)) {
  1236. X        set_msg_bit(list, current_msg);
  1237. X        return 0;
  1238. X    }
  1239. X    return -1;
  1240. X    } else if (argv[0][0] == '\\') {
  1241. X    /* Can't change *argv (breaks free_vec),
  1242. X     *  so shift to remove the backslash
  1243. X     */
  1244. X    for (n = 0; argv[0][n]; n++)
  1245. X        argv[0][n] = argv[0][n+1];
  1246. X    }
  1247. X    Debug("executing: "), print_argv(argv);
  1248. X
  1249. X    /* if interrupted during execution of a command, return -1 */
  1250. X    if (isoff(glob_flags, IGN_SIGS) && setjmp(jmpbuf)) {
  1251. X    Debug("jumped back to exec_argv (%s: %d)\n", __FILE__, __LINE__);
  1252. X    return -1;
  1253. X    }
  1254. X
  1255. X    /* standard commands */
  1256. X    for (n = 0; cmds[n].command; n++)
  1257. X    if (!strcmp(argv[0], cmds[n].command))
  1258. X        return (*cmds[n].func)(argc, argv, list);
  1259. X
  1260. X    /* ucb-Mail compatible commands */
  1261. X    for (n = 0; ucb_cmds[n].command; n++)
  1262. X    if (!strcmp(argv[0], ucb_cmds[n].command))
  1263. X        return (*ucb_cmds[n].func)(argc, argv, list);
  1264. X
  1265. X    /* for hidden, undocumented commands */
  1266. X    for (n = 0; hidden_cmds[n].command; n++)
  1267. X    if (!strcmp(argv[0], hidden_cmds[n].command))
  1268. X        return (*hidden_cmds[n].func)(argc, argv, list);
  1269. X
  1270. X    n = -1; /* default to failure */
  1271. X    if ((isdigit(**argv) || index("^.*$-`{}", **argv))
  1272. X            && (n = get_msg_list(argv, list)) != 0) {
  1273. X    if (n < 0)
  1274. X        return -1;
  1275. X    else if (isoff(glob_flags, DO_PIPE))
  1276. X        for (n = 0; n < msg_cnt; n++)
  1277. X        if (msg_bit(list, n)) {
  1278. X            display_msg((current_msg = n), (long)0);
  1279. X            unset_msg_bit(list, n);
  1280. X        }
  1281. X    return 0;
  1282. X    } else {
  1283. X    /* get_msg_list will set the current message bit if nothing parsed */
  1284. X    if (n == 0)
  1285. X        unset_msg_bit(list, current_msg);
  1286. X    if (strlen(*argv) == 1 && index("$^.", **argv)) {
  1287. X        if (!msg_cnt) {
  1288. X        print("No messages.");
  1289. X        return -1;
  1290. X        } else {
  1291. X        if (**argv != '.')
  1292. X            current_msg = (**argv == '$') ? msg_cnt-1 : 0;
  1293. X        set_msg_bit(list, current_msg);
  1294. X        display_msg(current_msg, (long)0);
  1295. X        }
  1296. X        return 0;
  1297. X    }
  1298. X    }
  1299. X
  1300. X    if (!istool && do_set(set_options, "unix")) {
  1301. X    if (ison(glob_flags, IS_PIPE)) {
  1302. X        return pipe_msg(argc, argv, list);
  1303. X    } else
  1304. X        execute(argv);  /* try to execute a unix command */
  1305. X    return -1; /* doesn't affect messages! */
  1306. X    }
  1307. X
  1308. X    print("%s: command not found.\n", *argv);
  1309. X    if (!istool)
  1310. X    print("type '?' for valid commands, or type `help'\n");
  1311. X    return -1;
  1312. }
  1313. X
  1314. /* recursively look for aliases on a command line.  aliases may
  1315. X * reference other aliases.
  1316. X */
  1317. alias_stuff(b, argc, Argv)
  1318. register char     *b, **Argv;
  1319. {
  1320. X    register char     *p, **argv = DUBL_NULL;
  1321. X    register int     n = 0, i = 0, Argc;
  1322. X    static int         loops;
  1323. X    int         dummy;
  1324. X
  1325. X    if (++loops == 20) {
  1326. X    print("Alias loop.\n");
  1327. X    return -1;
  1328. X    }
  1329. X    for (Argc = 0; Argc < argc; Argc++) {
  1330. X    register char *h = Argv[n + ++i];
  1331. X    register char *p2 = "";
  1332. X    int sep;
  1333. X
  1334. X    /* we've hit a command separator or the end of the line */
  1335. X    if (h && strcmp(h, ";") && strcmp(h, "|"))
  1336. X        continue;
  1337. X
  1338. X    /* create a new argv containing this (possible subset) of argv */
  1339. X    if (!(argv = (char **)calloc((unsigned)(i+1), sizeof (char *))))
  1340. X        continue;
  1341. X    sep = n + i;
  1342. X    while (i--)
  1343. X        strdup(argv[i], Argv[n+i]);
  1344. X
  1345. X    if ((!last_aliased || strcmp(last_aliased, argv[0]))
  1346. X            && (p = alias_expand(argv[0]))) {
  1347. X        /* if history was referenced, ignore the rest of argv
  1348. X         * else copy all of argv onto the end of the buffer.
  1349. X         */
  1350. X        if (!(p2 = hist_expand(p, argv, &dummy)))
  1351. X        break;
  1352. X        if (!dummy)
  1353. X        (void) argv_to_string(p2+strlen(p2), argv+1);
  1354. X        if (Strcpy(b, p2) > BUFSIZ) {
  1355. X        print("Not enough buffer space.\n");
  1356. X        break;
  1357. X        }
  1358. X        /* release old argv and build a new one based on new string */
  1359. X        free_vec(argv);
  1360. X        if (!(argv = mk_argv(b, &dummy, 0)))
  1361. X        break;
  1362. X        if (alias_stuff(b, dummy, argv) == -1)
  1363. X        break;
  1364. X    } else
  1365. X        b = argv_to_string(b, argv);
  1366. X    xfree(last_aliased), last_aliased = NULL;
  1367. X    free_vec(argv);
  1368. X    b += strlen(b);
  1369. X    if (h) {
  1370. X        b += strlen(sprintf(b, " %s ", h));
  1371. X        while (++Argc < argc && (h = Argv[Argc]))
  1372. X        if (Argc > sep && strcmp(h, ";"))
  1373. X            break;
  1374. X        n = Argc--;
  1375. X    }
  1376. X    i = 0;
  1377. X    }
  1378. X    xfree(last_aliased), last_aliased = NULL;
  1379. X    --loops;
  1380. X    if (Argc < argc) {
  1381. X    free_vec(argv);
  1382. X    return -1;
  1383. X    }
  1384. X    return 0;
  1385. }
  1386. X
  1387. char *
  1388. alias_expand(cmd)
  1389. register char *cmd;
  1390. {
  1391. X    register char *p;
  1392. X    register int x;
  1393. X
  1394. X    if (!(p = do_set(functions, cmd)))
  1395. X    return NULL;
  1396. X    last_aliased = savestr(cmd); /* to be freed elsewhere; don't strdup! */
  1397. X    if (isoff(glob_flags, WARNING))
  1398. X    return p;
  1399. X    for (x = 0; cmds[x].command; x++)
  1400. X    if (!strcmp(cmd, cmds[x].command)) {
  1401. X        wprint("(real command: \"%s\" aliased to \"%s\")\n", cmd, p);
  1402. X        return p;
  1403. X    }
  1404. X    for (x = 0; ucb_cmds[x].command; x++)
  1405. X    if (!strcmp(cmd, ucb_cmds[x].command)) {
  1406. X        wprint("(ucb-command: \"%s\" aliased to \"%s\")\n", cmd, p);
  1407. X        return p;
  1408. X    }
  1409. X    return p;
  1410. }
  1411. X
  1412. static int nonobang;
  1413. X
  1414. /* expand history references and separate message lists from other tokens */
  1415. char *
  1416. hist_expand(str, argv, hist_was_referenced)
  1417. register char *str, **argv;
  1418. register int *hist_was_referenced;
  1419. {
  1420. X    static char   buf[BUFSIZ];
  1421. X    register int  b = 0, inquotes = 0;
  1422. X    int       first_space = 0, ignore_bang;
  1423. X
  1424. X    ignore_bang = (ison(glob_flags, IGN_BANG) ||
  1425. X           do_set(set_options, "ignore_bang"));
  1426. X    nonobang = !!do_set(set_options, "nonobang");
  1427. X
  1428. X    if (hist_was_referenced)
  1429. X    *hist_was_referenced = 0;
  1430. X    while (*str) {
  1431. X    while (!inquotes && isspace(*str))
  1432. X        str++;
  1433. X    do  {
  1434. X        if (!*str)
  1435. X        break;
  1436. X        if (b >= sizeof(buf)-1) {
  1437. X        print("argument list too long.\n");
  1438. X        return NULL;
  1439. X        }
  1440. X        if ((buf[b] = *str++) == '\'') {
  1441. X        /* make sure there's a match! */
  1442. X        inquotes = !inquotes;
  1443. X        }
  1444. X        if (!first_space && !inquotes && index("0123456789{}*$^.", buf[b])
  1445. X                 && b && !index("0123456789{}-^. \t", buf[b-1])) {
  1446. X        buf[b+1] = buf[b];
  1447. X        buf[b++] = ' ';
  1448. X        while ((buf[++b] = *str++) && index("0123456789-,${}.", buf[b]))
  1449. X            ;
  1450. X        if (!buf[b])
  1451. X            str--;
  1452. X        first_space++;
  1453. X        }
  1454. X        /* check for (;) (|) or any other delimiter and separate it from
  1455. X         * other tokens.
  1456. X         */
  1457. X        if (!inquotes && buf[b] != '\0' && isdelimeter(buf[b]) &&
  1458. X            (b < 0 || buf[b-1] != '\\')) {
  1459. X        if (!isspace(buf[b]))
  1460. X            first_space = -1; /* resume msg-list separation */
  1461. X        if (b && !isspace(buf[b-1]))
  1462. X            buf[b+1] = buf[b], buf[b++] = ' ';
  1463. X        b++;
  1464. X        break;
  1465. X        }
  1466. X        /*
  1467. X         * If double-quotes, just copy byte by byte, char by char,
  1468. X         *  but do remove backslashes from in front of !s
  1469. X         */
  1470. X        if (!inquotes && buf[b] == '"') {
  1471. X        int B = b;
  1472. X        while ((buf[++B] = *str++) && buf[B] != '"')
  1473. X            if (*str == '!' && buf[B] == '\\')
  1474. X            buf[B] = '!', str++;
  1475. X        if (buf[B])
  1476. X            b = B;
  1477. X        else
  1478. X            str--;
  1479. X        b++;
  1480. X        continue;
  1481. X        }
  1482. X        if (buf[b] == '\\') {
  1483. X        first_space = 1;    /* don't split escaped words */
  1484. X        if ((buf[++b] = *str) == '!')
  1485. X            buf[--b] = '!';
  1486. X        ++str;
  1487. X        } else if (buf[b] == '!' && *str && *str != '\\' && !isspace(*str)
  1488. X               && !ignore_bang) {
  1489. X        char word[BUFSIZ], *s;
  1490. X        if (!(s = reference_hist(str, word, argv))) {
  1491. X            if (!nonobang)
  1492. X            return NULL;
  1493. X        } else {
  1494. X            str = s;
  1495. X            if (hist_was_referenced)
  1496. X            *hist_was_referenced = 1;
  1497. X            if (strlen(word) + b >= sizeof buf) {
  1498. X            print("argument list too long.\n");
  1499. X            return NULL;
  1500. X            }
  1501. X            b += Strcpy(&buf[b], word) - 1;
  1502. X        }
  1503. X        }
  1504. X        b++;
  1505. X    } while (*str && (!isdelimeter(*str) || str[-1] == '\\'));
  1506. X    if (!inquotes)
  1507. X        first_space++, buf[b++] = ' ';
  1508. X    }
  1509. X    buf[b] = 0;
  1510. X    return buf;
  1511. }
  1512. X
  1513. /*
  1514. X * expand references to internal variables.  This allows such things
  1515. X * as $iscurses, $hdrs_only, etc. to work correctly.
  1516. X */
  1517. char *
  1518. check_internal(str)
  1519. register char *str;
  1520. {
  1521. X    int ret_val = -1;
  1522. X    static char version[80], get_status[4];
  1523. X
  1524. X    if (!strcmp(str, "iscurses"))
  1525. X    ret_val = (iscurses || ison(glob_flags, PRE_CURSES));
  1526. X    else if (!strcmp(str, "istool"))
  1527. X    ret_val = istool;
  1528. X    else if (!strcmp(str, "hdrs_only"))
  1529. X    ret_val = (hdrs_only && *hdrs_only);
  1530. X    else if (!strcmp(str, "is_shell"))
  1531. X    ret_val = is_shell;
  1532. X    else if (!strcmp(str, "is_sending"))
  1533. X    ret_val = (ison(glob_flags, IS_SENDING) != 0);
  1534. X    else if (!strcmp(str, "redirect"))
  1535. X    ret_val = (isatty(0) != 0);
  1536. X    else if (!strcmp(str, "thisfolder"))
  1537. X    return (mailfile && *mailfile) ? mailfile : NULL;
  1538. X    else if (!strcmp(str, "status"))
  1539. X    return sprintf(get_status, "%d", last_status);
  1540. X    else if (!strcmp(str, "output"))
  1541. X    return last_output;
  1542. X    else if (!strcmp(str, "version")) {
  1543. X    /* Create the version string ONCE, then re-use it. */
  1544. X    if (!*version)
  1545. X        (void) sprintf(version, "%s (%d.%s.%d %s)",
  1546. X              MUSHNAME, RELEASE, REVISION, PATCHLEVEL, RELEASE_DATE);
  1547. X    return version;
  1548. X    }
  1549. X
  1550. X    return ret_val > 0 ? "1" : ret_val == 0? "0" : NULL;
  1551. }
  1552. X
  1553. /*
  1554. X * Parse and expand a single variable reference.  Variable references
  1555. X * begin with a '$' and thereafter look like any of:
  1556. X *    $    $$ is the pid of the current process
  1557. X *    [%x]    $[%x] expands %x as a hdr_format character ($%x is same)
  1558. X *    (%x)    $(%x) expands %x as a prompt format character
  1559. X *    name    Value of variable "name" (error if not set)
  1560. X *    v:x    Modified expansion; v is any of above, x is any of
  1561. X *            h    head of a file pathname
  1562. X *            t    tail of a file pathname
  1563. X *            l    value converted to lowercase
  1564. X *            u    value converted to uppercase
  1565. X *             q    quote against further expansion (not yet)
  1566. X *              <num>    select the <num>th space-separated field
  1567. X *    ?name    Set/unset truth value of "name"
  1568. X *    {v}    Separate v (any of above) from surrounding text
  1569. X * A variable name may include alphabetics, numbers, or underscores but
  1570. X * must begin with an alphabetic or underscore.
  1571. X */
  1572. varexp(ref)
  1573. struct expand *ref;
  1574. {
  1575. X    char *str = ref->orig, c, *p, *var, *end = NULL, *op = NULL;
  1576. X    int do_bool, do_fmt = 0, expanded = 0;
  1577. X
  1578. X    if (*str == '$') {
  1579. X    /* Allow a $ all by itself to stand */
  1580. X    if (!*++str || isspace(*str)) {
  1581. X        ref->exp = savestr("$");
  1582. X        ref->rest = str;
  1583. X        return 1;
  1584. X    }
  1585. X    /* Handle $?{name} for backwards compatibility */
  1586. X    if (do_bool = (*str == '?'))
  1587. X        str++;
  1588. X    if (*str == '{')
  1589. X        if (p = index(str + 1, '}')) {
  1590. X        var = str + 1;
  1591. X        end = p;
  1592. X        } else
  1593. X        goto bad_var;
  1594. X    else
  1595. X        var = str;
  1596. X    /* Handle $?name and ${?name} (normal cases) */
  1597. X    if (*var == '?') {
  1598. X        if (do_bool) /* backwards compatibility clash */
  1599. X        goto bad_var;
  1600. X        ++var, do_bool = 1;
  1601. X    }
  1602. X    switch (*var) {
  1603. X        case '$':
  1604. X        if (str[0] == '{' && str[2] != '}')
  1605. X            goto bad_var;
  1606. X        else {
  1607. X            char buf[16];
  1608. X            (void) sprintf(buf, "%d", getpid());
  1609. X            ref->exp = savestr(buf);
  1610. X            ref->rest = (end ? end : var) + 1;
  1611. X            return 1;
  1612. X        }
  1613. X        when '%':
  1614. X        for (p = var + 1; *p && !index(" \t\n;|\"'$", *p); p++)
  1615. X            if (*p == ':') {
  1616. X            if (!do_bool && !op) {
  1617. X                op = p;
  1618. X                do_fmt = p - var;
  1619. X            } else
  1620. X                break;
  1621. X            }
  1622. X        if (!do_fmt)
  1623. X            do_fmt = p - var;
  1624. X        end = p;
  1625. X        when '[': case '(':  /*)*/
  1626. X        p = any(var, *var == '(' ? ") \t\n" : "] \t\n");
  1627. X        if (!p || isspace(*p))
  1628. X            goto bad_var;
  1629. X        if (end && p > end)
  1630. X            goto bad_var;
  1631. X        else {
  1632. X            var++;
  1633. X            do_fmt = p - var;
  1634. X            if (*++p == ':')
  1635. X            op = p;
  1636. X            else
  1637. X            end = p;
  1638. X        }
  1639. X        /* fall through */
  1640. X        default:
  1641. X        if (!do_fmt && !isalpha(*var) && *var != '_')
  1642. X            goto bad_var;
  1643. X        if (!end)
  1644. X            end = var + strlen(var);
  1645. X        for (p = (op ? op : var + do_fmt) + 1; p < end; p++)
  1646. X            if (!do_bool && !op && *p == ':') {
  1647. X            op = p;
  1648. X            } else if (!isalnum(*p) && *p != '_') {
  1649. X            if (*str == '{') /*}*/
  1650. X                goto bad_var;
  1651. X            end = p;
  1652. X            break;
  1653. X            }
  1654. X        if (op && op > end)
  1655. X            op = NULL;
  1656. X    }
  1657. X    /* replace the end of "var" (end) with a nul,
  1658. X     * and save char in `c'.  Similarly chop at op.
  1659. X     */
  1660. X    c = *end, *end = 0;
  1661. X    if (op)
  1662. X        *op++ = 0;
  1663. X
  1664. X    if (!do_fmt && debug > 3)
  1665. X        printf("expanding (%s) ", var);
  1666. X
  1667. X    /* get the value of the variable. */
  1668. X    if (do_fmt) {
  1669. X        char c1 = var[do_fmt];
  1670. X        var[do_fmt] = 0;
  1671. X        if (debug > 3)
  1672. X        printf("expanding (%s) ", var);
  1673. X        if (/*(*/ ')' == c1)
  1674. X        p = format_prompt(current_msg, var);
  1675. X        else
  1676. X        p = format_hdr(current_msg, var, FALSE) + 9;
  1677. X        var[do_fmt] = c1;
  1678. X    } else if (!(p = check_internal(var)))
  1679. X        p = do_set(set_options, var);
  1680. X    if (do_bool) {
  1681. X        ref->exp = savestr((p && (*p || !do_fmt)) ? "1" : "0");
  1682. X        expanded = 1;
  1683. X        if (debug > 3)
  1684. X        printf("--> (%s)\n", p);
  1685. X    } else if (p) {
  1686. X        if (debug > 3)
  1687. X        printf("--> (%s)", p);
  1688. X        if (op && isdigit(*op)) {
  1689. X        int varc, ix = atoi(op) - 1;
  1690. X        char **varv = mk_argv(p, &varc, FALSE);
  1691. X        /* Ignore non-fatal errors like unmatched quotes */
  1692. X        if (varv && varc < 0)
  1693. X            for (varc = 0; varv[varc]; varc++)
  1694. X            ;
  1695. X        if (ix < 0 || varc <= ix || !varv)
  1696. X            ref->exp = savestr("");
  1697. X        else
  1698. X            ref->exp = savestr(varv[ix]);
  1699. X        expanded = 1;
  1700. X        free_vec(varv);
  1701. X        } else if (op) {
  1702. X        char *p2 = rindex(p, '/');
  1703. X        expanded = (*op == 'h' || *op == 't');
  1704. X        if (*op == 't' && p2)
  1705. X            p = p2 + 1;
  1706. X        else if (*op == 'h' && p2)
  1707. X            *p2 = 0;
  1708. X        ref->exp = savestr(p);
  1709. X        if (*op == 'h' && p2)
  1710. X            *p2 = '/';
  1711. X        else if (*op == 'l' || *op == 'u') {
  1712. X            expanded = 1;
  1713. X            for (p = ref->exp; *p; p++)
  1714. X            if (*op == 'u')
  1715. X                Upper(*p);
  1716. X            else
  1717. X                Lower(*p);
  1718. X        }
  1719. X        if (!expanded) {
  1720. X            print("Unknown colon modifier :%c.\n", *op);
  1721. X            xfree(ref->exp);
  1722. X        } else
  1723. X            if (debug > 3)
  1724. X            printf("--> (%s)\n", p);
  1725. X        } else {
  1726. X        ref->exp = savestr(p);
  1727. X        expanded = 1;
  1728. X        if (debug > 3)
  1729. X            printf("\n");
  1730. X        }
  1731. X    } else {
  1732. X        print("%s: undefined variable\n", var);
  1733. X        expanded = 0;
  1734. X    }
  1735. X    *end = c; /* replace the null with the old character */
  1736. X    if (op)
  1737. X        *--op = ':'; /* Put back the colon */
  1738. X    ref->rest = end + (*str == '{'); /* } */
  1739. X    }
  1740. X    return expanded;
  1741. bad_var:
  1742. X    print("Illegal variable name.\n");
  1743. X    return 0;
  1744. }
  1745. X
  1746. /*
  1747. X * find mush variable references and expand them to their values.
  1748. X * variables are preceded by a '$' and cannot be within single
  1749. X * quotes.  Only if expansion has been made do we copy buf back into str.
  1750. X * We expand only as far as the first unprotected `;' separator in str,
  1751. X * to get the right behavior when multiple commands are on one line.
  1752. X * RETURN 0 on failure, 1 on success.
  1753. X */
  1754. variable_expand(str)
  1755. register char *str;
  1756. {
  1757. X    register int     b = 0, inquotes = 0;
  1758. X    char             buf[BUFSIZ], *start = str;
  1759. X    int             expanded = 0;
  1760. X
  1761. X    while (*str && b < sizeof buf - 1) {
  1762. X    if (*str == '~' && (str == start || isspace(*(str-1)))) {
  1763. X        register char *p = any(str, " \t"), *tmp;
  1764. X        int x = 1;
  1765. X        if (p)
  1766. X        *p = 0;
  1767. X        tmp = getpath(str, &x);
  1768. X        /* if error, print message and return 0 */
  1769. X        if (x == -1) {
  1770. X        wprint("%s: %s\n", str, tmp);
  1771. X        return 0;
  1772. X        }
  1773. X        b += Strcpy(buf+b, tmp);
  1774. X        if (p)
  1775. X        *p = ' ', str = p;
  1776. X        else
  1777. X        str += strlen(str);
  1778. X        expanded = 1;
  1779. X    }
  1780. X    /* if single-quotes, just copy byte by byte, char by char ... */
  1781. X    if ((buf[b] = *str++) == '\'' && !inquotes) {
  1782. X        while ((buf[++b] = *str++) && buf[b] != '\'')
  1783. X        ;
  1784. X        if (!buf[b])
  1785. X        str--;
  1786. X    } else if (!inquotes && buf[b] == '\\' && *str) {
  1787. X        buf[++b] = *str++;
  1788. X        b++;
  1789. X        continue;
  1790. X    } else if (buf[b] == '"')
  1791. X        inquotes = !inquotes;
  1792. X    /* If $ is eol, continue.  Variables must start with a `$'
  1793. X     * and continue with {, _, a-z, A-Z or it is not a variable.      }
  1794. X     */
  1795. X    if (buf[b] == '$' && *str) {
  1796. X        struct expand expansion;
  1797. X        expansion.orig = str - 1;
  1798. X        if (varexp(&expansion)) {
  1799. X        b += Strcpy(&buf[b], expansion.exp);
  1800. X        xfree(expansion.exp);
  1801. X        str = expansion.rest;
  1802. X        expanded = 1;
  1803. X        } else
  1804. X        return 0;
  1805. X    } else if (!inquotes && buf[b] == ';') {
  1806. X        while (buf[++b] = *str++)
  1807. X        ;
  1808. X        b++;
  1809. X        break;
  1810. X    } else
  1811. X        b++;
  1812. X    }
  1813. X    buf[b] = 0;
  1814. X    if (expanded) /* if any expansions were done, copy back into orig buf */
  1815. X    (void) strcpy(start, buf);
  1816. X    if (debug > 3)
  1817. X    printf("expanded to: %s\n", start);
  1818. X    return 1;
  1819. }
  1820. X
  1821. /* make an argv of space delimited character strings out of string "str".
  1822. X * place in "argc" the number of args made.  If final is true, then expand
  1823. X * variables and file names and remove quotes and backslants according to
  1824. X * standard.
  1825. X */
  1826. char **
  1827. mk_argv(str, argc, final)
  1828. register char *str;
  1829. int *argc;
  1830. {
  1831. X    register char    *s = NULL, *p;
  1832. X    register int    tmp, err = 0, unq_sep = 0;
  1833. X    char        *newargv[MAXARGS], **argv, *p2, c, buf[BUFSIZ];
  1834. X
  1835. X    if (debug > 3)
  1836. X    (void) printf("Working on: %s\n",str);
  1837. SHAR_EOF
  1838. true || echo 'restore of loop.c failed'
  1839. fi
  1840. echo 'End of  part 10'
  1841. echo 'File loop.c is continued in part 11'
  1842. echo 11 > _shar_seq_.tmp
  1843. exit 0
  1844. exit 0 # Just in case...
  1845. -- 
  1846. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1847. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1848. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1849. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1850.