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

  1. From: argv@zipcode.com (Dan Heller)
  2. Newsgroups: comp.sources.misc
  3. Subject: v18i065:  mush - Mail User's Shell, Part08/22
  4. Message-ID: <1991Apr21.025028.11432@sparky.IMD.Sterling.COM>
  5. Date: 21 Apr 91 02:50:28 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: 66a5696c f93bd72d c9b78bc9 6dad1564
  8.  
  9. Submitted-by: Dan Heller <argv@zipcode.com>
  10. Posting-number: Volume 18, Issue 65
  11. Archive-name: mush/part08
  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 expr.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" != 8; 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 expr.c'
  32. else
  33. echo 'x - continuing file expr.c'
  34. sed 's/^X//' << 'SHAR_EOF' >> 'expr.c' &&
  35. X */
  36. char *
  37. eval_expr(p, new_list)
  38. register char *p, new_list[];
  39. {
  40. X    register char *p2, **argv;
  41. X    int       argc;
  42. X    u_long      save_flags = glob_flags;
  43. X
  44. X    if (!(p2 = index(++p, '`'))) {
  45. X    print("unmatched backquote (`)\n");
  46. X    return NULL;
  47. X    }
  48. X    *p2 = 0;
  49. X
  50. X    skipspaces(0);
  51. X    if (!*p) {
  52. X    print("Invalid null command\n");
  53. X    return NULL;
  54. X    }
  55. X    turnon(glob_flags, DO_PIPE);
  56. X    /* ignore sigs only because if user interrupts the do_command,
  57. X     * the longjmp will corrupt the stack and the program is hosed.
  58. X     * fix is to have layers of jmp_bufs to return to different levels.
  59. X     */
  60. X    turnon(glob_flags, IGN_SIGS);
  61. X    if (*p && (argv = make_command(p, TRPL_NULL, &argc)))
  62. X    (void) do_command(argc, argv, new_list);
  63. X    glob_flags = save_flags;
  64. X    *p2 = '`';
  65. X    return p2+1;
  66. }
  67. SHAR_EOF
  68. echo 'File expr.c is complete' &&
  69. chmod 0644 expr.c ||
  70. echo 'restore of expr.c failed'
  71. Wc_c="`wc -c < 'expr.c'`"
  72. test 4685 -eq "$Wc_c" ||
  73.     echo 'expr.c: original size 4685, current size' "$Wc_c"
  74. rm -f _shar_wnt_.tmp
  75. fi
  76. # ============= file.c ==============
  77. if test -f 'file.c' -a X"$1" != X"-c"; then
  78.     echo 'x - skipping file.c (File already exists)'
  79.     rm -f _shar_wnt_.tmp
  80. else
  81. > _shar_wnt_.tmp
  82. echo 'x - extracting file.c (Text)'
  83. sed 's/^X//' << 'SHAR_EOF' > 'file.c' &&
  84. /* file.c -- Copyright (1988) Dan Heller */
  85. X
  86. #include "mush.h"
  87. #include <pwd.h>
  88. X
  89. /* takes string 'p' and address of int (isdir).  If p uses the ~ to reference
  90. X * a home directory of some sort, then expand it.  find out what sort of
  91. X * file final path is. set isdir to 1 if a directory, 0 if not, -1 on error
  92. X * return final path. If an error occurs, return string indicating error.
  93. X * if isdir has a value of 1 when passed, it ignores "No such file or directory"
  94. X */
  95. char *
  96. getpath(p, isdir)
  97. register char *p;
  98. int *isdir;
  99. {
  100. X    static char buf[MAXPATHLEN];
  101. X    struct stat stat_buf;
  102. X
  103. X    if (p != buf) { /* Just in case */
  104. X    if (!p || !*p || !strcmp(p, "~")) {
  105. X        char *home = do_set(set_options, "home");
  106. X        if (!home || !*home)
  107. X        home = ALTERNATE_HOME;
  108. X        (void) strcpy(buf, home);  /* no arg means home */
  109. X    } else if (*p == '~') {
  110. X        if (p[1] != '/') {
  111. X        /* not our home, but someone else's
  112. X         * look for ~user or ~user/subpath
  113. X         * if '/' exists, separate into tmp="user" p="subpath"
  114. X         */
  115. X        struct passwd *ent, *getpwnam();
  116. X        char *p2 = p+1;
  117. X        if (p = index(p2, '/'))
  118. X            *p++ = 0;
  119. X        if (!(ent = getpwnam(p2))) {
  120. X            *isdir = -1;
  121. X            return sprintf(buf, "no such user: %s", p2);
  122. X        }
  123. X        /* append subpath to pathname */
  124. X        if (p && *p)
  125. X            (void) sprintf(buf, "%s/%s", ent->pw_dir, p);
  126. X        /* if *p == NULL, pathname is done (buf), set isdir = 1 */
  127. X        else {
  128. X            *isdir = 1;
  129. X            return strcpy(buf, ent->pw_dir);
  130. X        }
  131. X        } else {
  132. X        char *home = do_set(set_options, "home");
  133. X        if (!home || !*home)
  134. X            home = ALTERNATE_HOME;
  135. X        (void) sprintf(buf, "%s/%s", home, p+2);
  136. X        }
  137. X    } else if (*p == '%') {
  138. X        /* if %user, append user name... else, it's just us */
  139. X        if (!*++p || *p == ' ' || *p == '\t')
  140. X        (void) strcpy(buf, spoolfile);
  141. X        else
  142. #ifndef HOMEMAIL
  143. X        (void) sprintf(buf, "%s/%s", MAILDIR, p);
  144. #else /* HOMEMAIL */
  145. X        {
  146. X        /* If it's NOT us, recur to get the path for ~user/MAILFILE */
  147. X        int t_isdir = *isdir;
  148. X        char *t, tmp[MAXPATHLEN];
  149. X        (void) sprintf(tmp, "~%s/%s", p, MAILFILE);
  150. X        t = getpath(tmp, &t_isdir);
  151. X        if (t_isdir == -1) {
  152. X            *isdir = -1;
  153. X            return t;
  154. X        }
  155. X        /* strcpy(buf, t); --buf already has info because it's static */
  156. X        }
  157. #endif /* HOMEMAIL */
  158. X    } else if (*p == '+') {
  159. X        register char *p2 = do_set(set_options, "folder");
  160. X        if (!p2 || !*p2)
  161. X        p2 = DEF_FOLDER;
  162. X        if (*++p)
  163. X        (void) sprintf(buf, "%s/%s", p2, p);
  164. X        else
  165. X        (void) strcpy(buf, p2);
  166. X        if (*buf != '/') {
  167. X        int t_isdir = *isdir;
  168. X        char *t, tmp[MAXPATHLEN];
  169. X        if (*buf != '~')
  170. X            (void) sprintf(tmp, "~/%s", buf);
  171. X        else
  172. X            (void) strcpy(tmp, buf);
  173. X        t = getpath(tmp, &t_isdir);
  174. X        if (t_isdir == -1) {
  175. X            *isdir = -1;
  176. X            return t;
  177. X        }
  178. X        /* strcpy(buf, t); --buf already has info because it's static */
  179. X        }
  180. X    } else {  /* allow \ to escape the special chars, +, %, ~ */
  181. X        if (*p == '\\')
  182. X        p++;
  183. X        (void) strcpy(buf, p);
  184. X    }
  185. X    }
  186. X    if (stat(buf, &stat_buf)) {
  187. X    (void) access(buf, F_OK); /* set errno to the "real" reason */
  188. X    if (errno == ENOENT && *isdir == 1) {
  189. X        *isdir = 0; /* say it's a regular file even tho it doesn't exist */
  190. X        return buf; /* it may be wanted for creating */
  191. X    }
  192. X    *isdir = -1;
  193. X    return sys_errlist[errno];
  194. X    }
  195. X    *isdir = ((stat_buf.st_mode & S_IFMT) == S_IFDIR);
  196. X    return buf;
  197. }
  198. X
  199. /*
  200. X * Given a (possibly NULL or empty) string, return the name of a a valid
  201. X * directory.  The string may contain the usual filename metachars (see
  202. X * above).  Returns the current user's home directory if the input string
  203. X * does not refer to a directory, the ALTERNATE_HOME if the user's home
  204. X * directory cannot be found, or NULL if none of the above are accessible.
  205. X *
  206. X * NOTE:  Returns the getpath() static buffer, so the same caveats apply.
  207. X */
  208. char *
  209. getdir(path)
  210. char *path;
  211. {
  212. X    int isdir = 0;
  213. X
  214. X    /* getpath() already handles the NULL and empty cases */
  215. X    if (!(path = getpath(path, &isdir)) || isdir != 1) {
  216. X    isdir = 0;
  217. X    path = getpath(ALTERNATE_HOME, &isdir);
  218. X    if (isdir != 1)
  219. X        path = NULL;
  220. X    }
  221. X    return path;
  222. }
  223. X
  224. /*
  225. X * Given a filename[pointer] (p), a file pointer, and a mode, file_to_fp
  226. X * opens the file with the mode.
  227. X * If the mode is "r" then we read the file into the file pointer at the
  228. X * end (fseek(fp, 2, 0)).  If the file is opened for writing, then read
  229. X * from the beginning of fp and write it into the file.
  230. X * This is usually called to read .signatures into messages (thus,
  231. X * opening .signature with "r" and writing to the end of fp which is probably
  232. X * the sendmail process or the message file pointer) or to write fortunes into
  233. X * the message buffer: reading fp (the popened fortune) and writing into file.
  234. X */
  235. file_to_fp(p, fp, mode)
  236. register char *p;
  237. register FILE *fp;
  238. char *mode;
  239. {
  240. X    int     x = 1;
  241. X    char     *file, buf[BUFSIZ];
  242. X    FILE     *tmp_fp;
  243. X
  244. X    if (!p || !*p) {
  245. X    print("specify filename");
  246. X    return -1;
  247. X    }
  248. X    /* Special case for IS_SENDING && !IS_GETTING should eventually go away */
  249. X    if (ison(glob_flags, IS_SENDING) && isoff(glob_flags, IS_GETTING) &&
  250. X        strcmp(p, "-") == 0) {
  251. X    file = p;
  252. X    if (*mode == 'r')
  253. X        tmp_fp = stdin;
  254. X    else
  255. X        tmp_fp = stdout;
  256. X    } else {
  257. X    file = getpath(p, &x);
  258. X    if (x == -1) { /* on error, file contains error message */
  259. X        wprint(file);
  260. X        return -1;
  261. X    }
  262. X    wprint("%s: ", file);
  263. X    if (x) {
  264. X        /* if x == 1, then path is a directory */
  265. X        wprint("is a directory.\n");
  266. X        return -1;
  267. X    } else if (!(tmp_fp = fopen(file, mode))) {
  268. X        wprint("%s\n", sys_errlist[errno]);
  269. X        return -1;
  270. X    }
  271. X    }
  272. X    if (*mode != 'r') {
  273. X    rewind(fp);
  274. X    for(x = 0; fgets(buf, BUFSIZ, fp); x++)
  275. X        (void) fputs(buf, tmp_fp);
  276. X    } else {
  277. X    for(x = 0; fgets(buf, BUFSIZ, tmp_fp); x++)
  278. X        (void) fputs(buf, fp);
  279. X    (void) fflush(fp);
  280. X    }
  281. X    wprint("%s%d line%s\n", (*mode == 'a')? "added ": "",
  282. X                  x, (x == 1)? "": "s");
  283. X    if (file != p || strcmp(file, "-") != 0)
  284. X    (void) fclose(tmp_fp);
  285. X    return 0;
  286. }
  287. X
  288. /* clear all contents of the file.  Careful that the file is opened for
  289. X * _writing_ --tempfile is opened for reading, so don't try to empty it
  290. X * if you're using ftruncate.   Return -1 on error, 0 on success.
  291. X */
  292. emptyfile(fp, fname)
  293. register FILE **fp;
  294. register char *fname;
  295. {
  296. X    Debug("Emptying \"%s\"\n", fname);
  297. #ifndef SYSV
  298. X    return ftruncate(fileno(*fp), 0L);
  299. #else
  300. X    {
  301. X    int omask = umask(077), ret;
  302. X    (void) fclose(*fp);
  303. X    if (!(*fp = fopen(fname, "w")))
  304. X        ret = -1;
  305. X    else
  306. X        ret = 0;
  307. X    (void) umask(omask);
  308. X    return ret;
  309. X    }
  310. #endif /* SYSV */
  311. }
  312. X
  313. /*
  314. X * Finds out how many file descriptors are opened.  Useful for making sure
  315. X * no files got opened in subprocedures which were not subsequently closed.
  316. X * If argc is 0, returns the number of available fds.
  317. X */
  318. nopenfiles(argc)
  319. {
  320. #ifdef MAXFILES
  321. X    register int size = MAXFILES;
  322. #else
  323. X    register int size = getdtablesize();
  324. #endif /* MAXFILES */
  325. X    register int nfiles = 0, totalfiles = size;
  326. X
  327. X    if (argc > 1)
  328. X    return -1;
  329. X
  330. X    if (argc == 1)
  331. X    wprint("open file descriptors:");
  332. X    while (--size >= 0)
  333. X    if (fcntl(size, F_GETFL, 0) != -1) {
  334. X        if (argc == 1)
  335. X        wprint(" %d", size);
  336. X        ++nfiles;
  337. X    }
  338. X    if (argc == 1) {
  339. X    wprint("\n");
  340. X    return 0;
  341. X    }
  342. X    return totalfiles - nfiles;
  343. }
  344. X
  345. /*
  346. X * Close all "extraneous" file descriptors; return the number closed
  347. X */
  348. closefileds (n)
  349. {
  350. X    register int nfiles = 0;
  351. #ifdef MAXFILES
  352. X    register int size = MAXFILES;
  353. #else
  354. X    register int size = getdtablesize();
  355. #endif /* MAXFILES */
  356. X
  357. X    while (--size >= n)
  358. X    if (fcntl(size, F_GETFL, 0) != -1) {
  359. X        (void) close(size);
  360. X        ++nfiles;
  361. X    }
  362. X    return nfiles;
  363. }
  364. X
  365. /*
  366. X * Open a path for writing or appending -- return a FILE pointer.
  367. X * If program is TRUE, then use popen, not fopen and don't check 
  368. X * to see if the file is writable.  If program is FALSE and lockit
  369. X * is TRUE, then lock on open.
  370. X */
  371. FILE *
  372. open_file(p, program, lockit)
  373. register char *p;
  374. {
  375. X    register FILE *newfile = NULL_FILE;
  376. X    register char *tmp;
  377. X    int x = 1;
  378. X
  379. X    if (program)
  380. X    tmp = p, x = 0;
  381. X    else
  382. X    tmp = getpath(p, &x);
  383. X    if (x == 1)
  384. X    print("%s is a directory.\n", tmp);
  385. X    else if (x == -1)
  386. X    print("%s: %s\n", p, tmp);
  387. X    else {
  388. X    register char *mode = NULL;
  389. X    /* if it doesn't exist open for "w" */
  390. X    if (program || Access(tmp, F_OK))
  391. X        mode = "w";
  392. X    /* if we can't write to it, forget it */
  393. X    else if (Access(tmp, W_OK))
  394. X        error(tmp);
  395. X    else
  396. X        mode = "a";
  397. X    if (mode)
  398. X        if (program) {
  399. X        if (!(newfile = popen(tmp, mode)))
  400. X            error("Can't execute %s\n", tmp);
  401. X        } else if (lockit) {
  402. X        /* Lock on open */
  403. X        if (!(newfile = lock_fopen(tmp, mode)))
  404. X            error("Can't write to %s", tmp);
  405. X        } else {
  406. X        /* Ordinary open */
  407. X        if (!(newfile = mask_fopen(tmp, mode)))
  408. X            error("Can't write to %s", tmp);
  409. X        }
  410. X        if (newfile != NULL_FILE)
  411. X        Debug("Successfully opened %s\n", tmp);
  412. X    }
  413. X    return newfile;
  414. }
  415. X
  416. /*
  417. X * Open each file in the vector names[] and place the corresponding
  418. X * file descriptor in files[].  If the file is really a program (pipe),
  419. X * delete the name after opening; otherwise lock the file.
  420. X * Tokens beginning with a "/, ~, or + are files; tokens beginning
  421. X * with a | are programs.
  422. X */
  423. open_list(names, files, size)
  424. char *names[];
  425. FILE *files[];
  426. {
  427. X    register int total = 0, prog;
  428. X    register char *fpath;
  429. X
  430. X    Debug("opening "), print_argv(names);
  431. X    for (total = 0; size && total < size; ) {
  432. X    fpath = names[total] + (prog = (names[total][0] == '|'));
  433. X    /* open_file() locks the file here only if prog is false */
  434. X    if ((files[total] = open_file(fpath, prog, TRUE))) {
  435. X        if (prog) {
  436. X        xfree(names[total]);
  437. X        names[total++] = NULL;
  438. X        } else {
  439. X        /* Seek to end of file AFTER locking */
  440. X        (void) fseek(files[total++], 0L, 2);
  441. X        }
  442. X    } else {
  443. X        Debug("Failed to open %s\n", names[total]);
  444. X        /* Swap the failed file with the last in the list */
  445. X        if (size--) {
  446. X        xfree(names[total]);
  447. X        names[total] = names[size];
  448. X        names[size] = NULL;
  449. X        }
  450. X    }
  451. X    }
  452. X    return size;
  453. }
  454. X
  455. /*
  456. X * find_files gets a set of addresses and an array of
  457. X * char pointers and the maximum size that array can be.
  458. X * The object is to find the files or programs listed in "s".  If the
  459. X * size is 0, then just extract the file names and give error messages
  460. X * for each one since they will not be opened. Return the number of
  461. X * files found and delete all files from the list in * "s".
  462. X * The string "s" is modified to be a list of address -- all names AND
  463. X * files are stripped out of the list.
  464. X * The force parameter causes names to be interpreted as files even if
  465. X * they would normally appear to be addresses.
  466. X */
  467. find_files(s, names, size, force)
  468. register char *s;
  469. char *names[];
  470. {
  471. X    register int     total = 0;
  472. X    char          file[MAXPATHLEN], buf[HDRSIZ], *start = s, c;
  473. X    register char    *p, *b = buf, *fpath;
  474. X
  475. X    do  {
  476. X    if (!(p = get_name_n_addr(s, NULL, file)))
  477. X        break;
  478. X    c = *p, *p = 0;
  479. X    /* See if it's a file.  This doesn't get written back
  480. X     * onto "buf" since it is supposed to be extracted anyway.
  481. X     * The check for '@' in names beginning with '/' is to
  482. X     * avoid mis-identifying X.400 addresses as file names.
  483. X     */
  484. X    if (force || *file == '+' || *file == '~' ||
  485. X        *file == '|' || *file == '/' && !index(file, '@')) {
  486. X        int isdir;
  487. X        /* open either "file" or &file[1] */
  488. X        if (*file == '|') {
  489. X        isdir = 0;
  490. X        fpath = file;
  491. X        } else {
  492. X        isdir = 1;
  493. X        /* if successful, getpath will reset isdir to 0 */
  494. X        fpath = getpath(file, &isdir);
  495. X        }
  496. X        if (!isdir) {
  497. X        if (size && total < size)
  498. X            names[total++] = savestr(fpath);
  499. X        else
  500. X            print("No open space for %s\n", file);
  501. X        } else if (isdir == 1)
  502. X        print("%s: is a directory\n", file);
  503. X        else
  504. X        print("%s: %s\n", file, fpath);
  505. X    } else {
  506. X        b += Strcpy(b, s);
  507. X        *b++ = ',', *b++ = ' ';
  508. X    }
  509. X    for (*p = c, s = p; *s == ',' || isspace(*s); s++)
  510. X        ;
  511. X    } while (*s);
  512. X    for (*b-- = 0; b > buf && (*b == ',' || isspace(*b)); b--)
  513. X    *b = 0;
  514. X    (void) strcpy(start, buf);
  515. X    names[total] = NULL; /* for free_vec() */
  516. X    return total;
  517. }
  518. X
  519. /*
  520. X * access(2) has an undocumented feature which ignores suid.  If you are
  521. X * su'ed and try to read your mail, you will be unable to because access()
  522. X * will give the illusion that you cannot read/write to your mbox.  Solve
  523. X * the problem by using stat() instead.
  524. X */
  525. Access(file, mode)
  526. register char *file;
  527. {
  528. X    struct stat buf;
  529. X
  530. X    if (stat(file, &buf) == -1)
  531. X    return -1;
  532. X    if (mode == R_OK)
  533. X    return (buf.st_mode & 0400)? 0 : -1;
  534. X    if (mode == W_OK)
  535. X    return (buf.st_mode & 0200)? 0 : -1;
  536. X    return 0;
  537. }
  538. X
  539. /*
  540. X * Open a file for read/write/whatever but make sure umask is rw by user only.
  541. X */
  542. FILE *
  543. mask_fopen(file, mode)
  544. char *file, *mode;
  545. {
  546. X    int omask = umask(077);
  547. X    FILE *fp = fopen(file, mode);
  548. X    (void) umask(omask);
  549. X    return fp;
  550. }
  551. X
  552. /*
  553. X * Shorten a file name, replacing its full path name with one using an
  554. X *  accepted mush abbreviation:
  555. X *    ~    home directory
  556. X *    +    folder directory
  557. X *  For files in the current directory, the path is simply skipped.
  558. X * Returns a pointer into a static buffer holding the trimmed path.
  559. X */
  560. char *
  561. trim_filename(name)
  562. char *name;
  563. {
  564. X    static char buf[MAXPATHLEN];
  565. X    char *fldr = do_set(set_options, "folder"),
  566. X     *home = do_set(set_options, "home");
  567. X    int len;
  568. X
  569. X    /* Handling $folder is tough, because if it is not set then we should
  570. X     * trim DEF_FOLDER; but DEF_FOLDER may not be a full path, and we can't
  571. X     * call getpath() because the "name" parameter may point to gepath()'s
  572. X     * static buffer.  So we handle the special case of DEF_FOLDER starting
  573. X     * with a tilde ($home), and forget about it otherwise.  Yuck.
  574. X     */
  575. X    if ((!fldr || !*fldr) && (fldr = DEF_FOLDER) && *fldr == '~' && home) {
  576. X    (void) sprintf(buf, "%s%s", home, fldr + 1);
  577. X    fldr = buf;  /* buf will get overwritten again below */
  578. X    }
  579. X    /* One more special case: if $folder and $home are the same, then we
  580. X     * trim as $home, otherwise we trim as $folder.  This prevents strange
  581. X     * contractions like "+.cshrc" for "~/.cshrc".
  582. X     */
  583. X    if ((!home || strcmp(home, fldr)) && (len = strlen(fldr)) &&
  584. X        !strncmp(fldr, name, len) && (name[len] == '/' || !name[len])) {
  585. X    buf[0] = '+';
  586. X    if (name[len] && name[len + 1])
  587. X        (void) strcpy(buf + 1, name + len + 1);
  588. X    else
  589. X        buf[1] = 0;
  590. X    return buf;
  591. X    } else if (home && (len = strlen(home)) && !strncmp(home, name, len) &&
  592. X        (name[len] == '/' || !name[len])) {
  593. X    buf[0] = '~';
  594. X    (void) strcpy(buf + 1, name + len);
  595. X    return buf;
  596. X    } else if ((fldr = do_set(set_options, "cwd")) &&
  597. X        (len = strlen(fldr)) && !strncmp(fldr, name, len) &&
  598. X        name[len] == '/')
  599. X    return strcpy(buf, name + len + 1);
  600. X    return strcpy(buf, name);
  601. }
  602. SHAR_EOF
  603. chmod 0644 file.c ||
  604. echo 'restore of file.c failed'
  605. Wc_c="`wc -c < 'file.c'`"
  606. test 14503 -eq "$Wc_c" ||
  607.     echo 'file.c: original size 14503, current size' "$Wc_c"
  608. rm -f _shar_wnt_.tmp
  609. fi
  610. # ============= fkeys.c ==============
  611. if test -f 'fkeys.c' -a X"$1" != X"-c"; then
  612.     echo 'x - skipping fkeys.c (File already exists)'
  613.     rm -f _shar_wnt_.tmp
  614. else
  615. > _shar_wnt_.tmp
  616. echo 'x - extracting fkeys.c (Text)'
  617. sed 's/^X//' << 'SHAR_EOF' > 'fkeys.c' &&
  618. /* @(#)fkeys.c        (c) copyright 10/18/86 (Dan Heller) */
  619. X
  620. #include "mush.h"
  621. X
  622. #define L(n)        KEY_LEFTFIRST+(n)-1
  623. #define R(n)        KEY_RIGHTFIRST+(n)-1
  624. #define F(n)        KEY_TOPFIRST+(n)-1
  625. #define BREAK_KEY    KEY_TOPLAST
  626. X
  627. static int func_key();
  628. X
  629. Notify_value
  630. fkey_interposer(client, event, arg, type)
  631. Frame client;
  632. Event *event;
  633. Notify_arg arg;
  634. Notify_event_type type;
  635. {
  636. X    if ((event_is_key_left(event) || event_is_key_right(event) ||
  637. X    event_is_key_top(event)) &&
  638. X    event_is_down(event) && func_key(event_id(event)))
  639. X        return NOTIFY_DONE;
  640. X
  641. X    return notify_next_event_func(client, event, arg, type);
  642. }
  643. X
  644. /*
  645. X * Execute commands defined by a function key.
  646. X * Left keys:
  647. X * L1 = (null)  can't be set
  648. X * L2 ... L10
  649. X * Top function keys
  650. X * F1 ... F9, BREAK/backspace (key not definable)
  651. X * Right function keys
  652. X * R1 ... R15
  653. X * Usually, the last Function key displays the others' settings.
  654. X */
  655. static int
  656. func_key(key)
  657. register int key;
  658. {
  659. X    register char **argv, *p;
  660. X    char buf[256];
  661. X    int n;
  662. X
  663. X    if (key >= KEY_LEFTFIRST && key <= KEY_LEFTLAST)
  664. X    buf[0] = 'L', n = key - KEY_LEFTFIRST;
  665. X    else if (key >= KEY_TOPFIRST && key <= KEY_TOPLAST)
  666. X    buf[0] = 'F', n = key - KEY_TOPFIRST;
  667. X    else if (key >= KEY_RIGHTFIRST && key <= KEY_RIGHTLAST)
  668. X    buf[0] = 'R', n = key - KEY_RIGHTFIRST;
  669. X    (void) sprintf(buf+1, "%d", n+1);
  670. X
  671. X    if (!(p = do_set(fkeys, buf))) {
  672. X    if (!chk_option("quiet", "fkey"))
  673. X        wprint("Function key \"%s\" not set.\n", buf);
  674. X    return FALSE;
  675. X    }
  676. X    /* make_command will screw up "p", so copy it first */
  677. X    (void) strcpy(buf, p);
  678. X    Debug("(%s) \"%s\": ", key, p), turnon(glob_flags, CONT_PRNT);
  679. X    if (argv = make_command(buf, TRPL_NULL, &n))
  680. X    (void) do_command(n, argv, msg_list);
  681. X    return TRUE;
  682. }
  683. SHAR_EOF
  684. chmod 0644 fkeys.c ||
  685. echo 'restore of fkeys.c failed'
  686. Wc_c="`wc -c < 'fkeys.c'`"
  687. test 1717 -eq "$Wc_c" ||
  688.     echo 'fkeys.c: original size 1717, current size' "$Wc_c"
  689. rm -f _shar_wnt_.tmp
  690. fi
  691. # ============= folders.c ==============
  692. if test -f 'folders.c' -a X"$1" != X"-c"; then
  693.     echo 'x - skipping folders.c (File already exists)'
  694.     rm -f _shar_wnt_.tmp
  695. else
  696. > _shar_wnt_.tmp
  697. echo 'x - extracting folders.c (Text)'
  698. sed 's/^X//' << 'SHAR_EOF' > 'folders.c' &&
  699. /* @(#)folders.c    (c) copyright 10/18/86 (Dan Heller) */
  700. X
  701. #include "mush.h"
  702. X
  703. static char oldfolder[MAXPATHLEN];
  704. X
  705. /* folder %[user]  --new mailfile is the spool/mail/login file [user].
  706. X * folder #  --new mailfile is the folder previous to the current folder
  707. X * folder &  --new mailfile is ~/mbox (or whatever "mbox" is set to)
  708. X * folder +file --new mailfile is in the directory "folder"; name is 'file'
  709. X * folder "path" --full path name or the one in current working directory.
  710. X *
  711. X * in all cases, changes are updated unless a '!' is specified after the
  712. X * folder command (e.g. "f!", "folder !" "fo!" .. all permutations)
  713. X * as usual, if new mail has arrived before the file is copied back, then
  714. X * user will be notified beforehand.
  715. X *
  716. X * RETURN -1 on error -- else return 0. All bits in msg_list are set to true.
  717. X */
  718. folder(argc, argv, list)
  719. register char **argv;
  720. char list[];
  721. {
  722. X    int n, updating = !strcmp(*argv, "update"), do_read_only = 0, no_hdrs = 0;
  723. X    char *tmp, *newfolder = NULL, buf[MAXPATHLEN];
  724. X    struct stat statbuf;
  725. X    extern long last_spool_size;
  726. X
  727. X    if (ison(glob_flags, IS_PIPE)) {
  728. X    print("You can't pipe to the %s command.\n", *argv);
  729. X    return -1;
  730. X    } else if (ison(glob_flags, IS_SENDING)) {
  731. X    print("You can't use the %s command when sending.\n", *argv);
  732. X    return -1;
  733. X    } else if (!tempfile || !*tempfile) {
  734. X    print("You can't use the %s command in init files.\n", *argv);
  735. X    return -1;
  736. X    }
  737. X    while (*++argv && (**argv == '-' || **argv == '!'))
  738. X    if (!strcmp(*argv, "-N"))
  739. X        no_hdrs = !iscurses;
  740. X    else if (!updating && !strcmp(*argv, "-n"))
  741. X        turnoff(glob_flags, DO_UPDATE);
  742. X    else if (!strcmp(*argv, "-r"))
  743. X        do_read_only = 1;
  744. X    else if (!strcmp(*argv, "!")) {
  745. X        if (updating)
  746. X        turnon(glob_flags, DO_UPDATE);    /* useful? */
  747. X        else
  748. X        turnoff(glob_flags, DO_UPDATE);
  749. X    } else
  750. X        return help(0, "folder", cmd_help);
  751. X
  752. X    if (updating) {
  753. X    (void) strcpy(buf, mailfile);
  754. X    if (ison(glob_flags, READ_ONLY))
  755. X        do_read_only = 1;
  756. X    } else {
  757. X    if (!*argv) {
  758. X        mail_status(0);
  759. X        return 0;
  760. X    }
  761. X    if (!strcmp(*argv, "#"))
  762. X        if (!*oldfolder) {
  763. X        print("No previous folder\n");
  764. X        return -1;
  765. X        } else
  766. X        newfolder = oldfolder;
  767. X    else if (!strcmp(*argv, "&")) {
  768. X        if (!(newfolder = do_set(set_options, "mbox")) || !*newfolder)
  769. X        newfolder = DEF_MBOX;
  770. X    } else
  771. X        newfolder = *argv;
  772. X    n = 0;
  773. X    tmp = getpath(newfolder, &n);
  774. X    if (n == -1) {
  775. X        print("%s: %s\n", newfolder, tmp);
  776. X        return -1;
  777. X    } else if (n == 1) {
  778. X        print("%s: is a directory\n", tmp);
  779. X        return -1;
  780. X    }
  781. X    /* strcpy so copyback() below (which calls getpath) doesn't change
  782. X     * the data that tmp intended to point to.  Get the cwd if necessary.
  783. X     */
  784. X    n = 0;
  785. X    if (*tmp != '/') {
  786. X        if (!GetCwd(buf, sizeof buf)) {
  787. X        error("getcwd: %s",buf);
  788. X        return -1;
  789. X        }
  790. X        n = strlen(buf);
  791. X        buf[n++] = '/';
  792. X    }
  793. X    (void) strcpy(&buf[n], tmp);
  794. X    }
  795. #ifdef SUNTOOL
  796. X    if (istool > 1)
  797. X    timeout_cursors(TRUE);
  798. #endif /* SUNTOOL */
  799. X    if (stat(buf, &statbuf) == -1 || !(statbuf.st_mode & 0400)) {
  800. X    error("Unable to read %s", buf);
  801. #ifdef SUNTOOL
  802. X    if (istool > 1)
  803. X        timeout_cursors(FALSE);
  804. #endif /* SUNTOOL */
  805. X    return -1;
  806. X    }
  807. X    /* If the file can't be opened for writing, autoset READ_ONLY */
  808. X    if (!(statbuf.st_mode & 0200))
  809. X    do_read_only = 1;
  810. X
  811. X    if (!(n=copyback(updating?"Update folder?":"Change anyway?",!updating))) {
  812. #ifdef SUNTOOL
  813. X    if (istool > 1)
  814. X        timeout_cursors(FALSE);
  815. #endif /* SUNTOOL */
  816. X    /* an error occured updating the folder */
  817. X    return -1;
  818. X    }
  819. X    turnoff(glob_flags, CORRUPTED);    /* copyback() was successful */
  820. X    /* Assure that both oldfolder and mailfile are full paths */
  821. X    if (strcmp(mailfile, buf) || !*oldfolder) {
  822. X    n = 1; /* force load of new folder */
  823. X    if (!updating)
  824. X        (void) strcpy(oldfolder, *oldfolder? mailfile : buf);
  825. X    strdup(mailfile, buf);
  826. X    }
  827. X    do_read_only? turnon(glob_flags,READ_ONLY) : turnoff(glob_flags,READ_ONLY);
  828. X    last_size = spool_size = 0L;
  829. X    while (msg_cnt--) {
  830. X    xfree(msg[msg_cnt].m_date_recv);
  831. X    xfree(msg[msg_cnt].m_date_sent);
  832. X    msg[msg_cnt].m_date_recv = msg[msg_cnt].m_date_sent = NO_STRING;
  833. X    }
  834. X    msg_cnt = 0, msg[0].m_offset = 0L;
  835. X    turnoff(glob_flags, CONT_PRNT);
  836. X
  837. X    turnon(glob_flags, IGN_SIGS);
  838. X    /* clear the tempfile */
  839. X    if (tmpf)
  840. X    (void) fclose(tmpf);
  841. X    if (!do_read_only) {
  842. X    if (!(tmpf = mask_fopen(tempfile, "w"))) {
  843. X        error("error truncating %s", tempfile);
  844. X        turnoff(glob_flags, IGN_SIGS);
  845. #ifdef SUNTOOL
  846. X        if (istool > 1)
  847. X        timeout_cursors(FALSE);
  848. #endif /* SUNTOOL */
  849. X        return -1;
  850. X    }
  851. X    }
  852. X    /* Don't reload the folder if it was removed */
  853. X    if (n > 0) {
  854. X    if (load_folder(mailfile, TRUE, NULL) < 1) {
  855. X        last_msg_cnt = 0;
  856. X        last_size = statbuf.st_size; /* Disable check_new_mail() */
  857. X        turnoff(glob_flags, IGN_SIGS);
  858. #ifdef SUNTOOL
  859. X        if (istool > 1)
  860. X        timeout_cursors(FALSE);
  861. #endif /* SUNTOOL */
  862. X        return -1;
  863. X    }
  864. X    if (do_read_only && !(tmpf = fopen(mailfile, "r"))) {
  865. X        error(mailfile);
  866. X        turnoff(glob_flags, IGN_SIGS);
  867. #ifdef SUNTOOL
  868. X        if (istool > 1)
  869. X        timeout_cursors(FALSE);
  870. #endif /* SUNTOOL */
  871. X        return -1;
  872. X    }
  873. X    }
  874. X    last_msg_cnt = msg_cnt;  /* for check_new_mail */
  875. X    /* Prevent both bogus "new mail" messages and missed new mail */
  876. X    last_size = msg[msg_cnt].m_offset;
  877. X    if (!strcmp(mailfile, spoolfile))
  878. X    spool_size = last_spool_size = last_size;
  879. #ifdef SUNTOOL
  880. X    if (istool) {
  881. X    extern Panel_item folder_text_item;
  882. X    Rect *rect = (Rect *)window_get(hdr_sw, WIN_RECT);
  883. X    (void) pw_rop(hdr_win, 0,0, rect->r_width, rect->r_height, PIX_CLR,
  884. X        (struct pixrect *) 0,0,0);
  885. X    panel_set_value(folder_text_item, mailfile);
  886. X    }
  887. #endif /* SUNTOOL */
  888. X
  889. X    if (!updating || current_msg >= msg_cnt)
  890. X    current_msg = (msg_cnt? 0 : -1);
  891. X    turnoff(glob_flags, IGN_SIGS);
  892. X
  893. X    /* now sort messages according a user-defined default */
  894. X    if (!updating && msg_cnt > 1 && !strcmp(mailfile, spoolfile) &&
  895. X        (tmp = do_set(set_options, "sort"))) {
  896. X    (void) sprintf(buf, "sort %s", tmp);
  897. X    if ((argv = mk_argv(buf, &argc, TRUE)) && argc > 0) {
  898. X        /* msg_list can't be null for do_command and since we're not
  899. X         * interested in the result, call sort directly
  900. X         */
  901. X        (void) sort(argc, argv, NULL);
  902. X        free_vec(argv);
  903. X        if (!updating)
  904. X        current_msg = 0;    /* Sort may move the current message */
  905. X    }
  906. X    }
  907. X    turnoff(glob_flags, DO_UPDATE);
  908. X
  909. X    /* go to first NEW message */
  910. X    for (n = 0; n < msg_cnt && ison(msg[n].m_flags, OLD); n++)
  911. X    ;
  912. X    if (n == msg_cnt) {
  913. X    turnoff(glob_flags, NEW_MAIL);
  914. X    if (!updating) {
  915. X        /* no new message found -- try first unread message */
  916. X        for (n = 0; n < msg_cnt && isoff(msg[n].m_flags, UNREAD); n++)
  917. X        ;
  918. X    }
  919. X    } else {
  920. X    turnon(glob_flags, NEW_MAIL);
  921. X    /* default for toolmode is true */
  922. X    if (istool && !chk_option("quiet", "tool"))
  923. X        bell();
  924. X    }
  925. X    if (msg_cnt && (!updating || current_msg < 0))
  926. X    current_msg = (n == msg_cnt ? 0 : n);
  927. X
  928. X    if ((!istool || istool && !msg_cnt) && !iscurses)
  929. X    mail_status(0);
  930. X    /* be quiet if we're piping */
  931. X    if (!istool && !updating && !no_hdrs && msg_cnt
  932. X        && isoff(glob_flags, DO_PIPE))
  933. X    (void) cmd_line(sprintf(buf, "headers %d", current_msg+1), msg_list);
  934. #ifdef SUNTOOL
  935. X    if (istool > 1) {
  936. X    if (!msg_cnt)
  937. X        print("No Mail in %s\n", mailfile);
  938. X    if (msg_cnt) {
  939. X        display_msg(current_msg, (long)0);
  940. X        do_hdrs(0, DUBL_NULL, NULL);
  941. X        /* Automatic display should not "touch" this message */
  942. X        turnoff(msg[current_msg].m_flags, DO_UPDATE);
  943. X        /* don't update folder just because a message is displayed */
  944. X        turnoff(glob_flags, DO_UPDATE);
  945. X    }
  946. X    timeout_cursors(FALSE);
  947. X    }
  948. #endif /* SUNTOOL */
  949. X    if (list) {
  950. X    clear_msg_list(list);
  951. X    bitput(list, list, msg_cnt, =~); /* macro */
  952. X    }
  953. X    return 0;
  954. }
  955. X
  956. folders(argc, argv)
  957. register char **argv;
  958. {
  959. X    register char *p;
  960. X    char buf[128], unused[MAXMSGS_BITS];
  961. X
  962. X    if (argv && argv[1] && !strcmp(argv[1], "-?"))
  963. X    return help(0, "folders", cmd_help);
  964. X
  965. X    if (!(p = do_set(set_options, "folder")) || !*p)
  966. X    p = DEF_FOLDER;
  967. X    (void) sprintf(buf, "ls -FR %s", p);
  968. X    if (argv = make_command(buf, TRPL_NULL, &argc))
  969. X    return do_command(argc, argv, unused);
  970. X    return -1;
  971. }
  972. X
  973. /*
  974. X * Determine whether a file could be a folder.  If prompt is non-NULL,
  975. X * ask the user whether we should treat the file as a folder anyway.
  976. X */
  977. test_folder(name, prompt)
  978. char *name, *prompt;
  979. {
  980. X    char line[BUFSIZ], *p;
  981. X    FILE *fp = fopen(name, "r");
  982. X    int retval = FALSE;
  983. X
  984. X    if (!fp)
  985. X    return 0;
  986. X    if (fgets(line, sizeof line - 1, fp)) {
  987. #ifndef MSG_SEPARATOR
  988. X    if (p = any(line, " \t")) {
  989. X        skipspaces(1);
  990. X        p = any(p, " \t");
  991. X    }
  992. X    if (p && !strncmp(line, "From ", 5) && (p = parse_date(p + 1)))
  993. #else /* MSG_SEPARATOR */
  994. X    if (!strncmp(line, MSG_SEPARATOR, strlen(MSG_SEPARATOR)))
  995. #endif /* MSG_SEPARATOR */
  996. X        retval = TRUE;
  997. X    } else
  998. X    retval = TRUE;    /* Empty files are legitimate folders */
  999. X    (void) fclose(fp);
  1000. X    if (prompt && !retval) {
  1001. X    char buf[BUFSIZ];
  1002. #ifdef SUNTOOL
  1003. X    if (istool) {
  1004. X        (void) sprintf(buf, "\"%s\": %s", name, prompt);
  1005. X        return ask(buf);
  1006. X    }
  1007. #endif /* SUNTOOL */
  1008. X    print("\"%s\": %s [n] ", name, prompt);
  1009. X    buf[0] = 0;
  1010. X    retval = (Getstr(buf, sizeof (buf), 0) && lower(*buf) == 'y');
  1011. X    }
  1012. X    return retval;
  1013. }
  1014. X
  1015. /* merge_folders filename  -- concatenate the folder specified by filename
  1016. X *                            to the current folder.
  1017. X *
  1018. X * RETURN -1 on error -- else return 0.  A bit in msg_list is set to true
  1019. X * for each of the "new" messages read in to the current folder.
  1020. X */
  1021. merge_folders(n, argv, list)
  1022. register char **argv, list[];
  1023. {
  1024. X    int no_hdrs = 0, newest_msg;
  1025. X    long orig_offset;
  1026. X    char *tmp, *newfolder = NULL, buf[MAXPATHLEN];
  1027. X
  1028. X    if (ison(glob_flags, IS_PIPE)) {
  1029. X    print("You can't pipe to the %s command.\n", *argv);
  1030. X    return -1;
  1031. X    } else if (ison(glob_flags, IS_SENDING)) {
  1032. X    print("You can't use the %s command while sending.\n", *argv);
  1033. X    return -1;
  1034. X    }
  1035. X
  1036. X    while (*++argv && **argv == '-')
  1037. X    if (!strcmp(*argv, "-?"))
  1038. X        return help(0, "merge", cmd_help);
  1039. X    else if (!strcmp(*argv, "-N"))
  1040. X        no_hdrs = !(iscurses || ison(glob_flags, PRE_CURSES));
  1041. X
  1042. X    if (!*argv)
  1043. X    return 0;
  1044. X
  1045. X    if (ison(glob_flags, READ_ONLY)) {
  1046. X    print("Folder is read-only.\n");
  1047. X    return -1;
  1048. X    }
  1049. X
  1050. X    if (!strcmp(*argv, "#"))
  1051. X    if (!*oldfolder) {
  1052. X        print("No previous folder\n");
  1053. X        return -1;
  1054. X    } else
  1055. X        newfolder = oldfolder;
  1056. X    else if (!strcmp(*argv, "&")) {
  1057. X    if (!(newfolder = do_set(set_options, "mbox")) || !*newfolder)
  1058. X        newfolder = DEF_MBOX;
  1059. X    } else
  1060. X    newfolder = *argv;
  1061. X    n = 0;
  1062. X    tmp = getpath(newfolder, &n);
  1063. X    if (n == -1) {
  1064. X    print("%s: %s\n", newfolder, tmp);
  1065. X    return -1;
  1066. X    } else if (n == 1) {
  1067. X    print("%s: is a directory\n", tmp);
  1068. X    return -1;
  1069. X    }
  1070. X
  1071. X    turnon(glob_flags, IGN_SIGS);
  1072. X    orig_offset = msg[msg_cnt].m_offset;
  1073. X    (void) load_folder(tmp, 2, list);
  1074. X    msg[msg_cnt].m_offset = orig_offset;
  1075. X    newest_msg = last_msg_cnt;
  1076. X    Debug("newest_msg = %d\n", newest_msg);
  1077. X    last_msg_cnt = msg_cnt;  /* for check_new_mail */
  1078. X    Debug("msg_cnt = %d\n", msg_cnt);
  1079. X    if (current_msg < 0)
  1080. X    current_msg = 0;
  1081. X    (void) mail_size();
  1082. X    turnoff(glob_flags, IGN_SIGS);
  1083. X
  1084. X    if ((!istool || istool && !msg_cnt)
  1085. X        && !iscurses && !ison(glob_flags, PRE_CURSES))
  1086. X    mail_status(0);
  1087. X    /* be quiet if we're piping or if told not to show headers */
  1088. X    if ((istool || !no_hdrs) && isoff(glob_flags, DO_PIPE)
  1089. X        && newest_msg < msg_cnt)
  1090. X    (void) cmd_line(sprintf(buf, "headers %d", newest_msg + 1), NULL);
  1091. X    return 0;
  1092. }
  1093. X
  1094. /*
  1095. X * Default digest article separator
  1096. X */
  1097. #define ARTICLE_SEP "--------"
  1098. X
  1099. /*
  1100. X * Undigestify messages.  If a message is in digest-format, there are many
  1101. X * messages within this message which are to be extracted.  Kinda like a
  1102. X * folder within a folder.  By default, this routine will create a new
  1103. X * folder that contains the new messages.  -m option will merge the new
  1104. X * messages into the current folder.
  1105. X */
  1106. do_undigest(n, argv, list)
  1107. char *argv[], list[];
  1108. {
  1109. X    int r, articles = 0, merge = 0, appending = 0;
  1110. X    char buf[MAXPATHLEN], cmdbuf[MAXPATHLEN], newlist[MAXMSGS_BITS], *dir;
  1111. X    char *art_sep = ARTICLE_SEP, *mktemp();
  1112. X    FILE *fp;
  1113. X
  1114. X    while (argv && *++argv && **argv == '-') {
  1115. X    switch(argv[0][1]) {
  1116. X        case 'm':
  1117. X        if (ison(glob_flags, READ_ONLY)) {
  1118. X            print("Folder is read only.\n");
  1119. X            return -1;
  1120. X        }
  1121. X        merge++;
  1122. X        when 'p':
  1123. X        if (*++argv)
  1124. X            art_sep = *argv;
  1125. X        else {
  1126. X            print("Specify separator pattern with -p.\n");
  1127. X            return -1;
  1128. X        }
  1129. X        otherwise: return help(0, "undigest", cmd_help);
  1130. X    }
  1131. X    }
  1132. X
  1133. X    if ((n = get_msg_list(argv, list)) == -1)
  1134. X    return -1;
  1135. X
  1136. X    argv += n;
  1137. X
  1138. X    if (*argv) {
  1139. X    int isdir = 1; /* Ignore file nonexistance errors */
  1140. X    (void) strcpy(buf, getpath(*argv, &isdir));
  1141. X    if (isdir < 0) {
  1142. X        print("%s: %s\n", *argv, buf);
  1143. X        return -1;
  1144. X    } else if (isdir == 1) {
  1145. X        print("%s: is a directory\n", buf);
  1146. X        return -1;
  1147. X    }
  1148. X    } else {
  1149. X    register char *p, *p2;
  1150. X    if (Access(dir = ".", W_OK) == 0 ||
  1151. X        (dir = do_set(set_options, "folder")) ||
  1152. X        (dir = do_set(set_options, "tmpdir")))
  1153. X        dir = getdir(dir); /* expand metachars */
  1154. X    if (!dir)
  1155. alted:
  1156. X        dir = ALTERNATE_HOME;
  1157. X    for (n = 0; n < msg_cnt; n++)
  1158. X        if (msg_bit(list, n))
  1159. X        break;
  1160. X
  1161. X    if (!(p = header_field(n, "subject")))
  1162. X        (void) mktemp(sprintf(buf, "%s/digestXXXXX", dir));
  1163. X    else {
  1164. X        if (!lcase_strncmp(p, "re: ", 4))
  1165. X        p += 4;
  1166. X        for (p2 = p; *p2; p2++)
  1167. X        if (!isalnum(*p2) && *p2 != '-' && *p2 != '.') {
  1168. X            *p2 = 0;
  1169. X            break;
  1170. X        }
  1171. X        p2 = buf + Strcpy(buf, dir);
  1172. X        *p2++ = '/';
  1173. X        (void) strcpy(p2, p);
  1174. X    }
  1175. X    }
  1176. X
  1177. X    if (!Access(buf, W_OK))
  1178. X    appending = ((fp = mask_fopen(buf, "a")) != NULL_FILE);
  1179. X    else
  1180. X    fp = mask_fopen(buf, "w");
  1181. X    if (!fp) {
  1182. X    if (!*argv && strcmp(dir, ALTERNATE_HOME))
  1183. X        goto alted;
  1184. X    error("can't create %s", buf);
  1185. X    return -1;
  1186. X    }
  1187. X
  1188. X    for (n = 0; n < msg_cnt; n++) {
  1189. X    if (!msg_bit(list, n))
  1190. X        continue;
  1191. X
  1192. X    print("undigesting message %d\n", n+1);
  1193. X    /* copy message into file making sure all headers exist. */
  1194. X    r = undigest(n, fp, art_sep);
  1195. X    if (r <= 0)
  1196. X        break;
  1197. X    articles += r;
  1198. X    }
  1199. X    (void) fclose(fp);
  1200. X    if (r <= 0) {
  1201. X    if (!appending)
  1202. X        (void) unlink(buf);
  1203. X    return -1;
  1204. X    }
  1205. X    if (merge) {
  1206. X    (void) cmd_line(sprintf(cmdbuf, "\\merge -N %s", buf), newlist);
  1207. X    (void) unlink(buf);
  1208. X    print("Merged in %d messages.\n", articles);
  1209. X    } else
  1210. X    print("Added %d messages to \"%s\".\n", articles, buf);
  1211. X    clear_msg_list(list);
  1212. X    for (n = 0; n < msg_cnt; n++)
  1213. X    if (msg_bit(newlist, n))
  1214. X        set_msg_bit(list, n);
  1215. X    return 0;
  1216. }
  1217. X
  1218. /*
  1219. X * split digest-message 'n' to file "fp" using article separator "sep".
  1220. X * return number of articles copied or -1 if system error on fputs.
  1221. X * A digest is a folder-in-a-message in a special, semi-standard form.
  1222. X */
  1223. undigest(n, fp, sep)
  1224. int n;
  1225. FILE *fp;
  1226. char *sep;
  1227. {
  1228. X    int  art_cnt = 0, on_hdr = -1; /* on_hdr is -1 if hdr not yet found */
  1229. X    int  sep_len = (sep ? strlen(sep) : strlen(sep = ARTICLE_SEP));
  1230. X    long get_hdr = 0L;
  1231. X    char from[HDRSIZ], line[HDRSIZ], last_sep[HDRSIZ];
  1232. X    char from_hdr[256], afrom[256], adate[64];
  1233. X    char *fdate = "Xxx Xxx 00 00:00:00 0000"; /* Dummy date in ctime form */
  1234. X    SIGRET (*oldint)(), (*oldquit)();
  1235. X
  1236. X    if (!msg_get(n, from, sizeof from)) {
  1237. X    error("Unable to find msg %d", n+1);
  1238. X    return -1;
  1239. X    }
  1240. #ifndef MSG_SEPARATOR
  1241. X    else {
  1242. X    char *p = from + 5;
  1243. X    skipspaces(0);
  1244. X    p = index(p, ' ');
  1245. X    if (p) {
  1246. X        skipspaces(0);
  1247. X        fdate = p;
  1248. X    }
  1249. X    if (fputs(from, fp) == EOF)
  1250. X        return -1;
  1251. X    }
  1252. #endif /* !MSG_SEPARATOR */
  1253. X
  1254. X    on_intr();
  1255. X    *afrom = *adate = *last_sep = '\0';
  1256. X    while (ftell(tmpf) < msg[n].m_offset + msg[n].m_size &&
  1257. X       fgets(line, sizeof (line), tmpf)) {
  1258. X    if (ison(glob_flags, WAS_INTR))
  1259. X        goto handle_error;
  1260. X    if (*line == '\n' && on_hdr > 0)    /* blank line -- end of header */
  1261. X        on_hdr = 0;
  1262. X
  1263. X    /* Check for the beginning of a digest article */
  1264. X    if (!strncmp(line, sep, sep_len)) {
  1265. X        if (get_hdr) {
  1266. X        if (do_set(set_options, "warning"))
  1267. X            print("Article with no header? (added to article #%d)\n",
  1268. X                art_cnt);
  1269. X        /* Don't start a new message for whatever this is,
  1270. X         * just fseek back and keep appending to the last one.
  1271. X         */
  1272. X        if (fseek(tmpf, get_hdr, L_SET) < 0 ||
  1273. X            fputs(last_sep, fp) == EOF) {
  1274. X            art_cnt = -1;
  1275. X            goto handle_error;
  1276. X        }
  1277. X        get_hdr = 0L;
  1278. X        on_hdr = 0;
  1279. X        } else {
  1280. X        (void) strcpy(last_sep, line);
  1281. X        get_hdr = ftell(tmpf);
  1282. X        *afrom = *adate = '\0';
  1283. X        on_hdr = -1;    /* Haven't found the new header yet */
  1284. X        }
  1285. X        continue;
  1286. X    }
  1287. X
  1288. X    if (get_hdr) {
  1289. X        char *p = *line == '>' ? line + 1 : line;
  1290. X        if (*line == '\n') {
  1291. X        if (*afrom || *adate) {
  1292. X            (void) fseek(tmpf, get_hdr, L_SET);
  1293. X            /* Terminate the previous article */
  1294. X            art_cnt++;
  1295. #ifdef MSG_SEPARATOR
  1296. #ifdef END_MSG_SEP
  1297. X            if (fputs(END_MSG_SEP, fp) == EOF) {
  1298. X            art_cnt = -1;
  1299. X            goto handle_error;
  1300. X            }
  1301. #endif /* END_MSG_SEP */
  1302. #ifdef MMDF
  1303. X            /* MMDF has a newline in MSG_SEPARATOR */
  1304. X            if (fputs(MSG_SEPARATOR, fp) == EOF)
  1305. #else /* !MMDF */
  1306. X            /* Other MSG_SEPARATORs need a newline */
  1307. X            if (fputs(MSG_SEPARATOR, fp) == EOF ||
  1308. X                fputc('\n', fp) == EOF)
  1309. #endif /* MMDF */
  1310. #else /* !MSG_SEPARATOR */
  1311. X            /* Everybody else needs a From_ line */
  1312. X            if (fprintf(fp, "From %s  %s", *afrom ? afrom : "unknown",
  1313. X                *adate ? date_to_ctime(adate) : fdate) == EOF)
  1314. #endif /* MSG_SEPARATOR */
  1315. X            {
  1316. X            art_cnt = -1;
  1317. X            goto handle_error;
  1318. X            }
  1319. X            /* Make sure there is a From: without a leading > */
  1320. X            if (*afrom && *from_hdr && fputs(from_hdr, fp) == EOF) {
  1321. X            art_cnt = -1;
  1322. X            goto handle_error;
  1323. X            }
  1324. X            get_hdr = 0L;
  1325. X        } else if (on_hdr < 0)
  1326. X            /* Skip blanks between "--------" and the hdr */
  1327. X            get_hdr = ftell(tmpf);
  1328. X        } else if (on_hdr < 0)
  1329. X        on_hdr = 1;
  1330. X        if (on_hdr > 0 && !strncmp(p, "From: ", 6)) {
  1331. X        (void) get_name_n_addr(p + 6, NULL, afrom);
  1332. X        (void) no_newln(afrom);
  1333. X        /* Get the From: minus the leading > */
  1334. X        if (p != line)
  1335. X            (void) strcpy(from_hdr, p);
  1336. X        else /* We don't need From: twice! */
  1337. X            *from_hdr = '\0';
  1338. X        } else if (on_hdr > 0 && !strncmp(line, "Date: ", 6)) {
  1339. X        if (p = parse_date(line+6))
  1340. X            (void) strcpy(adate, p);
  1341. X        } else if (on_hdr > 0 && !lcase_strncmp(line, "end", 3)) {
  1342. X        if (!*afrom && !*adate)
  1343. X            break;
  1344. X        }
  1345. X    } else if (fputs(line, fp) == EOF) {
  1346. X        /* Pipe broken, out of file space, etc */
  1347. X        art_cnt = -1;
  1348. X        goto handle_error;
  1349. X    }
  1350. X    }
  1351. X    ++art_cnt;
  1352. #ifdef END_MSG_SEP
  1353. X    if (art_cnt > 0 && fputs(END_MSG_SEP, fp) == EOF) {
  1354. X    art_cnt = -1;
  1355. X    goto handle_error;
  1356. X    }
  1357. #endif /* END_MSG_SEP */
  1358. X    /* If we're still looking for a header, there is some stuff left
  1359. X     * at the end of the digest.  Create an extra article for it.
  1360. X     */
  1361. X    if (get_hdr) {
  1362. X    char *p;
  1363. X    (void) fseek(tmpf, get_hdr, L_SET);
  1364. X    if (ftell(tmpf) >= msg[n].m_offset + msg[n].m_size)
  1365. X        goto handle_error;
  1366. #ifdef MSG_SEPARATOR
  1367. #ifdef MMDF
  1368. X    if (fputs(MSG_SEPARATOR, fp) == EOF)
  1369. #else /* !MMDF */
  1370. X    if (fputs(MSG_SEPARATOR, fp) == EOF ||
  1371. X        fputc('\n', fp) == EOF)
  1372. #endif /* MMDF */
  1373. #else /* !MSG_SEPARATOR */
  1374. X    if (fputs(from, fp) == EOF)
  1375. #endif /* MSG_SEPARATOR */
  1376. X        art_cnt = -1;
  1377. X    if (!(p = header_field(n, "from")))
  1378. X        p = "Mush-Undigest (Real author unknown)";
  1379. X    if (fprintf(fp, "From: %s\n", p) == EOF)
  1380. X        art_cnt = -1;
  1381. X    if (!(p = header_field(n, "date")))
  1382. X        p = fdate, (void) no_newln(p);
  1383. X    if (fprintf(fp, "Date: %s\n", p) == EOF)
  1384. X        art_cnt = -1;
  1385. X    if (!(p = header_field(n, "subject")))
  1386. X        p = "Digest";
  1387. X    if (fprintf(fp, "Subject: Trailing part of %s\n\n", p) == EOF)
  1388. X        art_cnt = -1;
  1389. X    /* header_field() moves the pointer, so seek again */
  1390. X    (void) fseek(tmpf, get_hdr, L_SET);
  1391. X    while (art_cnt > 0 && ftell(tmpf) < msg[n].m_offset + msg[n].m_size
  1392. X        && fgets(line, sizeof (line), tmpf)) {
  1393. X        if (fputs(line, fp) == EOF)
  1394. X        art_cnt = -1;
  1395. #ifdef END_MSG_SEP
  1396. X        if (!strncmp(line, END_MSG_SEP, strlen(END_MSG_SEP)))
  1397. X        break;
  1398. #endif /* END_MSG_SEP */
  1399. X    }
  1400. X    /* The END_MSG_SEP, if any, of the digest will have been output
  1401. X     * by the while loop above, so we don't need to add one here.
  1402. X     */
  1403. X    ++art_cnt;
  1404. X    }
  1405. handle_error:
  1406. X    if (art_cnt == -1)
  1407. X    error("cannot completely undigest");
  1408. X    else if (ison(glob_flags, WAS_INTR))
  1409. X    art_cnt = -1;
  1410. X    off_intr();
  1411. X    return art_cnt;
  1412. }
  1413. SHAR_EOF
  1414. chmod 0644 folders.c ||
  1415. echo 'restore of folders.c failed'
  1416. Wc_c="`wc -c < 'folders.c'`"
  1417. test 20079 -eq "$Wc_c" ||
  1418.     echo 'folders.c: original size 20079, current size' "$Wc_c"
  1419. rm -f _shar_wnt_.tmp
  1420. fi
  1421. # ============= glob.c ==============
  1422. if test -f 'glob.c' -a X"$1" != X"-c"; then
  1423.     echo 'x - skipping glob.c (File already exists)'
  1424.     rm -f _shar_wnt_.tmp
  1425. else
  1426. > _shar_wnt_.tmp
  1427. echo 'x - extracting glob.c (Text)'
  1428. sed 's/^X//' << 'SHAR_EOF' > 'glob.c' &&
  1429. #include "mush.h"
  1430. #include "glob.h"
  1431. X
  1432. /*
  1433. X * Buried somewhere in here is the skeleton of a pattern matcher posted
  1434. X * by koblas@mips.COM (David Koblas).  It has been hacked almost beyond
  1435. X * recognition to handle more complex patterns, and directory search has
  1436. X * been added (patterns are split at '/' characters when file globbing).
  1437. X */
  1438. X
  1439. #ifdef TEST    /* Define TEST to build a stand-alone file globbing program */
  1440. X
  1441. extern char *malloc(), *realloc();
  1442. X
  1443. #define getpath(x,y) (*(y) = 0, (x))
  1444. #define Access access
  1445. #define Strcpy(x,y) (strcpy(x,y), strlen(x))
  1446. #define savestr(x)  (strcpy(malloc(strlen(x)+1),x))
  1447. #ifndef max
  1448. #define max(x,y) ((x) > (y) ? (x) : (y))
  1449. #endif /* max */
  1450. #ifndef min
  1451. #define min(x,y) ((x) > (y) ? (y) : (x))
  1452. #endif /* min */
  1453. #define xfree free
  1454. #undef wprint
  1455. #define wprint printf
  1456. #define debug 0
  1457. #undef sprintf
  1458. X
  1459. #define TESTGLOB(str1,str2) \
  1460. X    printf("%s %s = %s\n",str1,str2,glob(str1,str2)?"TRUE":"FALSE")
  1461. X
  1462. main(argc, argv)
  1463. int argc;
  1464. char **argv;
  1465. {
  1466. X    char **e;
  1467. X    int f;
  1468. X
  1469. X    if (argc > 1)
  1470. X    while (*++argv) {
  1471. X        (void) printf("%s -->\n", *argv);
  1472. X        if (f = filexp(*argv, &e)) {
  1473. X        columnate(f, e, 0);
  1474. X        }
  1475. X    }
  1476. #ifdef TEST2    /* Define TEST2 to automatically run these test cases */
  1477. X    TESTGLOB("abcdefg", "abcdefg");
  1478. X    TESTGLOB("abcdefg", "a?cd?fg");
  1479. X    TESTGLOB("abcdefg", "ab[cde]defg");
  1480. X    TESTGLOB("abcdefg", "ab[a-z]defg");
  1481. X    TESTGLOB("abcdefg", "ab[a-z]defg");
  1482. X    TESTGLOB("ab]defg", "ab[a]c]defg");
  1483. X    TESTGLOB("ab]defg", "ab[a\\]c]defg");
  1484. X    TESTGLOB("abcdefg", "ab*fg");
  1485. X    TESTGLOB("./bc/def/gh/ij", "*de*");
  1486. X    TESTGLOB("./der/den/deq/der/", "*deq*");
  1487. X    TESTGLOB("./bc/def/gh/ij", "*ij");
  1488. X    TESTGLOB("./ij", ".?ij");
  1489. X    TESTGLOB("./bc/def/gh/ij", "./*");
  1490. X    TESTGLOB("abcdef", "*def");
  1491. X    TESTGLOB("abcdef", "*abcdef");
  1492. X    TESTGLOB("abcdef", "abc*");
  1493. X    TESTGLOB("abcdef", "abcdef*");
  1494. X    TESTGLOB("abcdef", "*?*{xxx,,yy}");
  1495. X    TESTGLOB("abcdef", "abcde{f}");
  1496. X    TESTGLOB("abcdef", "abcdef{xxx,,yyy}");
  1497. X    TESTGLOB("abcdef", "abc{def,qwrx}");
  1498. X    TESTGLOB("abcdef", "abc{ab,def,qwrx}");
  1499. X    TESTGLOB("abcdef", "{naqrwer,fuwnwer,as,abc,a}{ab,def,qwrx}");
  1500. X    TESTGLOB("abcdef", "{naqrwer,*,as,abc,a}{ab,def,qwrx}");
  1501. X    TESTGLOB("abcdef", "{{a*,b*},as,a}{ab,def,qwrx}");
  1502. X    TESTGLOB("abcdef", "{{c*,b*},as,a}{ab,def,qwrx}");
  1503. X    TESTGLOB("abcdef", "{{c*,?b*},as,a}{ab,def,qwrx}");
  1504. X    TESTGLOB("abcdef", "{naqrwer,fuwnwer,as,abc,a}{ab,d*f,qwrx}");
  1505. #endif /* TEST2 */
  1506. }
  1507. X
  1508. char *
  1509. any(s1, s2)
  1510. register char *s1, *s2;
  1511. {
  1512. X    register char *p;
  1513. X    if (!s1 || !*s1 || !s2 || !*s2)
  1514. X    return 0;
  1515. X    for( ; *s1; s1++) {
  1516. X    for(p = s2; *p; p++)
  1517. X        if (*p == *s1)
  1518. X        return s1;
  1519. X    }
  1520. X    return 0;
  1521. }
  1522. X
  1523. #endif /* TEST */
  1524. X
  1525. /*
  1526. X * Make a string into a one-element vector
  1527. X */
  1528. char **
  1529. unitv(s)
  1530. char *s;
  1531. {
  1532. X    char **v;
  1533. X
  1534. X    if (v = (char **)malloc((unsigned)(2 * sizeof(char *)))) {
  1535. X    v[0] = savestr(s);
  1536. X    v[1] = NULL;
  1537. X    }
  1538. X    return v;
  1539. }
  1540. X
  1541. /*
  1542. X * Append one vector to another
  1543. X */
  1544. catv(s1, v1, s2, v2)
  1545. int s1, s2;
  1546. char ***v1, **v2;
  1547. {
  1548. X    int i;
  1549. X
  1550. X    if (s1 < 0 || !v1)
  1551. X    return -1;
  1552. X    if (s2 < 0 || !v2)
  1553. X    return s1;
  1554. X
  1555. X    /* realloc(NULL, size) should be legal, but Sun doesn't support it. */
  1556. X    if (*v1)
  1557. X        *v1 = (char **)realloc(*v1,(unsigned)((s1+s2+1) * sizeof(char **)));
  1558. X    else
  1559. X        *v1 = (char **)malloc((unsigned)((s1+s2+1) * sizeof(char **)));
  1560. X
  1561. X    if (*v1) {
  1562. X    for (i = 0; i < s2 && v2[i]; i++)
  1563. X        (*v1)[s1 + i] = v2[i]; 
  1564. X    (*v1)[s1 + i] = NULL;
  1565. X    xfree(v2);
  1566. X    return s1 + i;
  1567. X    }
  1568. X    return -1;
  1569. }
  1570. X
  1571. /*
  1572. X * A duplicate-eliminating comparison for sorting.  It treats an empty
  1573. X * string as greater than any other string, and forces empty one of any
  1574. X * pair of of equal strings.  Two passes are sufficient to move the empty
  1575. X * strings to the end where they can be deleted by the calling function.
  1576. X *
  1577. X * This is NOT compatible with the ANSI C qsort(), which requires that the
  1578. X * comparison function will not modify its arguments!
  1579. X */
  1580. uniqcmp(p1, p2)
  1581. char **p1, **p2;
  1582. {
  1583. X    int cmp;
  1584. X
  1585. X    if (**p1 && !**p2)
  1586. X    return -1;
  1587. X    if (**p2 && !**p1)
  1588. X    return 1;
  1589. X    if (cmp = strcmp(*p1, *p2))
  1590. X    return cmp;
  1591. X    **p2 = 0;
  1592. X    return -1;
  1593. }
  1594. X
  1595. /*
  1596. X * Expand a pattern into a list of file names.  Returns the number of
  1597. X * matches.  As in csh, names generated from pattern sets are returned
  1598. X * even if there are no actual matches.
  1599. X */
  1600. filexp(pat, exp)
  1601. char *pat, ***exp;
  1602. {
  1603. X    char **t1, **t2;
  1604. X    int n, new, cnt;
  1605. X
  1606. X    if (!exp)
  1607. X    return -1;
  1608. X    if (!pat || !*pat)
  1609. X    return 0;
  1610. X
  1611. X    if ((n = sxp(pat, &t1)) > 0)
  1612. X    cnt = 0;
  1613. X    else
  1614. X    return n;
  1615. X    *exp = DUBL_NULL;
  1616. X    while (n--)
  1617. X    if ((new = fxp(t1[n], &t2)) > 0 || new++ == 0 && t2)
  1618. X        cnt = catv(cnt, exp, new, t2);
  1619. X    if (cnt > 1) {
  1620. X    /* Two sort passes to eliminate duplicates -- see uniqcmp() */
  1621. X    qsort((char *)*exp, cnt, sizeof(char *), uniqcmp);
  1622. X    qsort((char *)*exp, cnt, sizeof(char *), uniqcmp);
  1623. X    while (!(*exp)[cnt - 1][0]) {
  1624. X        xfree((*exp)[--cnt]);
  1625. X        (*exp)[cnt] = NULL;
  1626. X    }
  1627. X    }
  1628. X    return cnt;
  1629. }
  1630. X
  1631. /*
  1632. X * Expand a filename with globbing chars into a list of matching filenames.
  1633. X * Pattern set notatation which crosses directories is not handled, e.g.
  1634. X * "fi{le/exp,nger/h}and" will NOT expand to "file/expand finger/hand".
  1635. X * Such patterns must be pre-expanded by sxp() before calling fxp().
  1636. X *
  1637. X * The list of expansions is placed in *exp, and the number of matches
  1638. X * is returned, or -1 on an error.
  1639. X */
  1640. fxp(name, exp)
  1641. char *name, ***exp;
  1642. {
  1643. X    char *p;
  1644. X    int isdir;
  1645. X
  1646. X    if (!exp)
  1647. X    return -1;
  1648. X
  1649. X    isdir = 1; /* ignore no such file */
  1650. X    p = getpath(name, &isdir);
  1651. X    if (isdir < 0)
  1652. X    return -1;
  1653. X    else if (isdir)
  1654. X    return ((*exp = unitv(p)) ? 1 : -1);
  1655. X    return pglob(p, 0, exp);
  1656. }
  1657. X
  1658. /*
  1659. X * Match all globbings in a path.  Mutually recursive with dglob(), below.
  1660. X * The first "skip" characters of the path are not globbed, see dglob().
  1661. X *
  1662. X * Returns the number of matches, or -1 on an error.  *exp is set to the
  1663. X * list of matches.
  1664. X *
  1665. X * If the path has no metachars, it is returned in *exp whether it matches
  1666. X * a real file or not.  This allows patterns built by sxp() to be recognized
  1667. X * and returned even when there are no matches (ala csh generation of names
  1668. X * from pattern sets).  pglob() still returns zero in this case.
  1669. X */
  1670. pglob(path, skip, exp)
  1671. char *path, ***exp;
  1672. int skip;
  1673. {
  1674. X    char *t, *t2;
  1675. X    int ret = 0;
  1676. X
  1677. X    if (!path || !exp || skip < 0)
  1678. X    return -1;
  1679. X    *exp = DUBL_NULL; /* Must be null in case of zero matches and no sets */
  1680. X
  1681. X    for (t = t2 = path + skip; (t2 = any(t2, META)) && *t2 == '/'; t = t2++)
  1682. X    ;
  1683. X    if (!t2) {
  1684. X    ret = ((*exp = unitv(path)) ? 1 : -1);
  1685. X    if (ret > 0 && Access(path, F_OK) < 0)
  1686. X        ret = 0;
  1687. X    } else {
  1688. X    if (t2 = index(t + 1, '/'))
  1689. X        *t2++ = 0;
  1690. X    if (*t == '/') {
  1691. X        *t++ = 0;
  1692. X        if (!*path)
  1693. X        ret = dglob("/", t, t2, exp);
  1694. X        else
  1695. X        ret = dglob(path, t, t2, exp);
  1696. X    } else {
  1697. X        ret = dglob("", t, t2, exp);
  1698. X    }
  1699. X    }
  1700. X    return ret;
  1701. }
  1702. X
  1703. /*
  1704. X * Search a directory (possibly recursively) for glob matches.
  1705. X * Argument pat1 is a pattern to be matched in this directory,
  1706. X * and pat2 is a pattern to be matched in matched subdirectories.
  1707. X *
  1708. X * Matches are returned through *exp.
  1709. X */
  1710. dglob(dir, pat1, pat2, exp)
  1711. char *dir, *pat1, *pat2, ***exp;
  1712. {
  1713. X    DIR *dirp;
  1714. X    struct dirent *dp;
  1715. X    char *b, *d, buf[MAXPATHLEN], **tmp;
  1716. X    int n, ret = 0, skip;
  1717. X
  1718. X    if (!dir || !exp)
  1719. X    return -1;
  1720. X    d = (*dir ? dir : ".");
  1721. X    if (!(dirp = opendir(d)))
  1722. X    return -1;
  1723. X    b = buf + Strcpy(buf, dir);
  1724. X    if (b > buf && *(b - 1) != '/')
  1725. X    *b++ = '/';
  1726. X    skip = b - buf; /* We know this much matches, don't glob it again */
  1727. X    while (ret >= 0 && (dp = readdir(dirp))) {
  1728. X    if (fglob(dp->d_name, pat1)) {
  1729. X        if (pat2) {
  1730. X        (void) sprintf(b, "%s/%s", dp->d_name, pat2);
  1731. X        n = pglob(buf, skip, &tmp);
  1732. X        ret = catv(ret, exp, n, tmp);
  1733. X        } else {
  1734. X        (void) strcpy(b, dp->d_name);
  1735. X        ret = catv(ret, exp, 1, unitv(buf));
  1736. X        }
  1737. X    }
  1738. X    }
  1739. X    closedir(dirp);
  1740. X    return ret;
  1741. }
  1742. X
  1743. /*
  1744. X * Match file names.  This means that metachars do not match leading ".".
  1745. X */
  1746. fglob(str, pat)
  1747. char *str, *pat;
  1748. {
  1749. X    if (!str || !pat || *str == '.' && *pat != '.')
  1750. X    return FALSE;
  1751. X    else
  1752. X    return glob(str, pat);
  1753. }
  1754. X
  1755. /*
  1756. X * Match two concatenated patterns.  Mainly for use by sglob().
  1757. X */
  1758. static
  1759. glob2(str, pat1, pat2)
  1760. char *str, *pat1, *pat2;
  1761. {
  1762. X    char buf[MAXPATHLEN];
  1763. X
  1764. X    if (!str || !pat1 && !pat2)
  1765. X    return FALSE;
  1766. X    (void) sprintf(buf, "%s%s", pat1? pat1 : "", pat2? pat2 : "");
  1767. X    return glob(str, buf);
  1768. }
  1769. X
  1770. /*
  1771. X * The basic globbing matcher.
  1772. X *
  1773. X * "*"           = match 0 or more occurances of anything
  1774. X * "[abc]"       = match any of "abc" (ranges supported)
  1775. X * "{xx,yy,...}" = match any of "xx", "yy", ... where
  1776. X *                 "xx", "yy" can be any pattern or empty
  1777. X * "?"           = match any character
  1778. X */
  1779. glob(str, pat)
  1780. char *str, *pat;
  1781. {
  1782. X    int done = FALSE, ret = FALSE;
  1783. X
  1784. X    if (!str || !pat)
  1785. X    return FALSE;
  1786. X
  1787. X    while (*pat && !done && (*str || (*pat == '{' || *pat == '*'))) /*}*/ {
  1788. X    /*
  1789. X     * First look for a literal match, stepping over backslashes
  1790. X     * in the pattern to match against the "protected" character.
  1791. X     * Ordering and precendence are important in this expression!
  1792. X     */
  1793. X    if (*pat == '\\' && *str == *++pat || *str == *pat) {
  1794. X        str++;
  1795. X        pat++;
  1796. X    } else switch (*pat++) {
  1797. X        case '*':    /* Match any string */
  1798. X        if (!*pat) {
  1799. X            while (*str)
  1800. X            str++;
  1801. X            break;
  1802. X        }
  1803. X        /*
  1804. X         * Try the rest of the glob against every
  1805. X         * possible suffix of the string.  A bit
  1806. X         * inefficient in cases that eventually fail.
  1807. X         */
  1808. X        while (*str && !(ret = glob(str++, pat)))
  1809. X            ;
  1810. X        return ret;
  1811. X        break;
  1812. X        case '[':    /* Match a set */
  1813. X        repeat:
  1814. X        /* If we've hit the end of the set, give up. */
  1815. SHAR_EOF
  1816. true || echo 'restore of glob.c failed'
  1817. fi
  1818. echo 'End of  part 8'
  1819. echo 'File glob.c is continued in part 9'
  1820. echo 9 > _shar_seq_.tmp
  1821. exit 0
  1822. exit 0 # Just in case...
  1823. -- 
  1824. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1825. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1826. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1827. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1828.