home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume12 / vmail / part03 < prev    next >
Encoding:
Text File  |  1988-03-13  |  42.6 KB  |  1,851 lines

  1. Article 997 of comp.sources.unix:
  2. Path: s.cc.purdue.edu!h.cc.purdue.edu!j.cc.purdue.edu!pur-ee!iuvax!inuxc!ihnp4!ptsfa!ames!amdcad!sun!pitstop!sundc!seismo!uunet!munnari!jz
  3. From: jz@mulga.oz.au (Justin Zobel)
  4. Newsgroups: comp.sources.unix
  5. Subject: v12i006:  vmail - screen-based mail handler, Part03/03
  6. Message-ID: <1856@munnari.oz>
  7. Date: 13 Oct 87 21:17:15 GMT
  8. Sender: kre@munnari.oz
  9. Lines: 1837
  10. Approved: kre@munnari.oz.au
  11.  
  12. Submitted by: jz@mulga.oz.au
  13. Posting-number: Volume 12, Issue 6
  14. Archive-name: vmail/Part03
  15.  
  16. This is the third of three parts of vmail, an interactive mail handler
  17. that sits on top of MH.
  18.     vmail has a number of advantages over raw MH.  It is screen-based and
  19. faster (and more convenient) than the MH show-scan-rmm refile cycle.
  20. vmail makes it feasible for users to organise and keep track of moderate
  21. volumes of mail without wasting too much time, and is very simple to use.
  22. It has been in use at Melbourne University Computer Science Department
  23. for about six months without any problems, and has become the interface of
  24. choice for many users.
  25.  
  26. : ---------------------------------------- cut here
  27.  
  28. echo x - "load.c" 2>&1
  29. sed "s/^X//" >"load.c" <<'!The!End!'
  30. X/* --------------------
  31. X    vmail -- load.c
  32. X
  33. X    Routines to find folders, find all mail items in folders.  Empty
  34. X    folders are ignored.
  35. X
  36. X    Copyright (C) J. Zobel, University of Melbourne, October 1987.
  37. X-------------------- */
  38. X
  39. X#include "defs.h"
  40. X#include <ctype.h>
  41. X
  42. X
  43. X/* --------------------
  44. X    Find all folders in mail directory, set up linked list.
  45. X-------------------- */
  46. Xvoid
  47. Xfind_folders()
  48. X{
  49. X    struct direct *dp;
  50. X    struct stat statbuf;
  51. X    DIR        *dirp;
  52. X    int        i, n = 0, scmp();
  53. X    char    str[LEN], **fds, **ftmp;
  54. X    folder    tmp, last = (folder) NULL;
  55. X
  56. X    stat(mail_dir, &statbuf);
  57. X        /* guess number of folders - 10 is an error bound */
  58. X    ftmp = fds = (char **) malloc(sizeof(char *) * (statbuf.st_nlink+10));
  59. X    dirp = opendir(mail_dir);
  60. X    for(dp=readdir(dirp) ; dp != (struct direct *)NULL ; dp=readdir(dirp)) {
  61. X        if(strcmp(dp->d_name, ".") == 0)
  62. X            continue;
  63. X        if(strcmp(dp->d_name, "..") == 0)
  64. X            continue;
  65. X        sprintf(str, "%s/%s", mail_dir, dp->d_name);
  66. X        stat(str, &statbuf);
  67. X        if(! (statbuf.st_mode & S_IFDIR))
  68. X            continue;
  69. X        if(! (statbuf.st_mode & S_IREAD) || ! (statbuf.st_mode & S_IWRITE)
  70. X                || ! (statbuf.st_mode & S_IEXEC)) {
  71. X            printf("Folder %s unreadable.\n", dp->d_name);
  72. X            continue;
  73. X        }
  74. X        *ftmp = NEWSTR(strlen(dp->d_name)+1);
  75. X        strcpy(*ftmp, dp->d_name);
  76. X        n++, ftmp++;
  77. X    }
  78. X    closedir(dirp);
  79. X    if(n == 0) {
  80. X        printf("No folders.\n");
  81. X        exit(1);
  82. X    }
  83. X    qsort(fds, n, sizeof(char *), scmp);
  84. X    for(i=0, ftmp=fds ; i < n ; ftmp++, i++) {
  85. X        tmp = NEW(mail_folder);
  86. X        tmp->name = *ftmp;
  87. X        tmp->last = tmp->mail = (item) NULL;
  88. X        tmp->pages = tmp->pagenum = 1;
  89. X        tmp->valid = false;
  90. X        tmp->prev = last;
  91. X        tmp->next = (folder) NULL;
  92. X        if(last != (folder) NULL)
  93. X            last->next = tmp;
  94. X        else
  95. X            folders = tmp;
  96. X        last = tmp;
  97. X    }
  98. X}
  99. X
  100. X
  101. Xint
  102. Xscmp(p, q)
  103. X    char    **p, **q;
  104. X{
  105. X    return(strcmp(*p, *q));
  106. X}
  107. X
  108. X
  109. X/* --------------------
  110. X    Find all mail items in given folder, throw away folder record if no
  111. X    mail items.  Returns next folder in linked list, adds new folder
  112. X    records to linked list if original record overflows.
  113. X-------------------- */
  114. Xfolder
  115. Xfind_mail(flr, load_time)
  116. X    folder    flr;
  117. X    int        load_time;
  118. X{
  119. X    struct direct *dp, *readdir();
  120. X    DIR        *dirp, *opendir();
  121. X    int        i, n = 0, items[MAXITEMS], *itmp = items, dcmp();
  122. X    char    str[LEN];
  123. X    item    tmp, last = (item) NULL;
  124. X    folder    new_folder();
  125. X
  126. X    sprintf(str, "%s/%s", mail_dir, flr->name);
  127. X    dirp = opendir(str);
  128. X    if(dirp == (DIR *) NULL) {
  129. X        if(load_time)
  130. X            printf("%s: strange folder.\n", str);
  131. X    } else {
  132. X        for(dp=readdir(dirp) ; dp!=(struct direct *) NULL ; dp=readdir(dirp)) {
  133. X                    /* get numbers of all mail items */
  134. X            if(n >= MAXITEMS) {
  135. X                no_control();
  136. X                printf("\nMore than %d items in folder.\n",MAXITEMS-1);
  137. X                exit(1);
  138. X            }
  139. X                /* ignore anything that is not string of digits */
  140. X            if(! digits(dp->d_name))
  141. X                continue;
  142. X                    /* assume any file that is string of digits is a mail
  143. X                       item and not a directory */
  144. X            *itmp = atoi(dp->d_name);
  145. X            n++, itmp++;
  146. X        }
  147. X        closedir(dirp);
  148. X    }
  149. X    if(n == 0) {        /* Empty folder - delete from list */
  150. X        flr->valid = EMPTY;
  151. X        if(load_time)
  152. X            printf("\t%s: empty\n", flr->name);
  153. X        if(flr->prev != (folder) NULL)
  154. X            flr->prev->next = flr->next;
  155. X        else
  156. X            folders = flr->next;
  157. X        if(flr->next != (folder) NULL)
  158. X            flr->next->prev = flr->prev;
  159. X        return(flr->next);
  160. X    } else
  161. X        qsort(items, n, sizeof(int), dcmp);
  162. X    if(load_time)        /* show status of folder */
  163. X        if(n == 1)
  164. X            printf("\t%s: %d\n", flr->name, *items);
  165. X        else
  166. X            printf("\t%s: %d-%d (%d items)\n",flr->name,*items,*(items+n-1),n);
  167. X    for(i=0, itmp=items ; i < n ; itmp++, i++) {
  168. X            /* add item to list of items in folder */
  169. X        tmp = NEW(mail_item);
  170. X        get_title(flr, tmp, *itmp);
  171. X        if(i % lines == 0 && last != (item) NULL) {
  172. X                /* add new record for folder */
  173. X            flr->last = last;
  174. X            flr = new_folder(flr);
  175. X            last = (item) NULL;
  176. X        }
  177. X        tmp->next = (item) NULL;
  178. X        tmp->prev = last;
  179. X        if(last != (item) NULL)
  180. X            last->next = tmp;
  181. X        else
  182. X            flr->mail = tmp;
  183. X        last = tmp;
  184. X    }
  185. X    flr->last = last;
  186. X    return(flr->next);
  187. X}
  188. X
  189. X
  190. Xint
  191. Xdcmp(a, b)
  192. X    int        *a, *b;
  193. X{
  194. X    return(*a - *b);
  195. X}
  196. X
  197. X
  198. X/* --------------------
  199. X    Return true if string consists only of digits.
  200. X-------------------- */
  201. Xint
  202. Xdigits(str)
  203. X    char    *str;
  204. X{
  205. X    for( ; *str != '\0' ; str++)
  206. X        if(! isdigit(*str))
  207. X            return(false);
  208. X    return(true);
  209. X}
  210. X
  211. X
  212. X/* --------------------
  213. X    Make header line from mail item.
  214. X-------------------- */
  215. Xvoid
  216. Xget_title(flr, mail, num)
  217. X    folder    flr;
  218. X    item    mail;
  219. X    int        num;
  220. X{
  221. X    FILE    *fp, *fopen();
  222. X    char    date[10], str[LEN], subj[42], from[20], to[16], fill[42];
  223. X    char    repl = ' ', *first(), *s, *fgets();
  224. X    int        i;
  225. X
  226. X    subj[0] = from[0] = to[0] = fill[0] = '\0';
  227. X    sprintf(str, "%s/%s/%d", mail_dir, flr->name, num);
  228. X    fp = fopen(str, "r");
  229. X    while((s=fgets(str, LEN, fp)) != (char *) NULL && *str != '\n') {
  230. X            /* while not eof and reading header */
  231. X        str[strlen(str)-1] = '\0';
  232. X            /* should also check date */
  233. X        if(! lstrncmp("date:", str, 5))
  234. X            get_date(str+5, date);
  235. X        else if(! lstrncmp("subject:", str, 8)) {
  236. X            s = first(str+8);
  237. X            strncpy(subj, s, 41);
  238. X            subj[41] = '\0';
  239. X        } else if(! lstrncmp("replied:", str, 8))
  240. X            repl = '-';
  241. X        else if(! lstrncmp("from:", str, 5)) {
  242. X            s = first(str+5);
  243. X            if(! isuser(s)) {
  244. X                strncpy(from, s, 19);
  245. X                from[19] = '\0';
  246. X            }
  247. X        } else if(! lstrncmp("to:", str, 3)) {
  248. X            s = first(str+3);
  249. X            strncpy(to, s, 15);
  250. X            to[15] = '\0';
  251. X        } else if(! lstrncmp("apparently-to:", str, 14)) {
  252. X            s = first(str+14);
  253. X            strncpy(to, s, 15);
  254. X            to[15] = '\0';
  255. X        }
  256. X    }
  257. X    if(s != (char *) NULL) {
  258. X        for(i=0 ; i < 50 && fgets(str, LEN, fp) != (char *) NULL ;) {
  259. X            str[strlen(str)-1] = ' ';
  260. X            strncpy(fill+i, str, 42-i);
  261. X            i += strlen(str);
  262. X        }
  263. X        fill[41] = '\0';
  264. X    }
  265. X    fclose(fp);
  266. X    if(subj[0] == '\0')
  267. X        strcpy(subj, "(none)");
  268. X    flatten(subj);
  269. X    flatten(fill);
  270. X    if(from[0] == '\0')
  271. X        sprintf(str, "  %8s %cTo: %-15s  %s << %s",date,repl,to,subj,fill);
  272. X    else
  273. X        sprintf(str, "  %8s %c%-19s  %s << %s",date,repl,from,subj,fill);
  274. X    str[cols-6] = '\0';
  275. X    mail->title = NEWSTR(strlen(str)+1);
  276. X    mail->number = num;
  277. X    strcpy(mail->title, str);
  278. X}
  279. X
  280. X
  281. X#ifdef USDATE
  282. X#define DAY1    date[3]
  283. X#define DAY2    date[4]
  284. X#define MTH1    date[0]
  285. X#define MTH2    date[1]
  286. X#else
  287. X#define DAY1    date[0]
  288. X#define DAY2    date[1]
  289. X#define MTH1    date[3]
  290. X#define MTH2    date[4]
  291. X#endif
  292. X#define YR1        date[6]
  293. X#define YR2        date[7]
  294. X
  295. X/* --------------------
  296. X    Get date from first argument; assumes format is one of
  297. XDate: Fri, 3 Apr 87 ...                        (a)
  298. XDate: Wed, 17 Jun 87 ...                    (b)
  299. XDate: 07 Sep 87 ...                            (c)
  300. XDate: Tue Sep 29 12:27:01 EST 1987            (d)
  301. X    Dates are put into Imperial form (dd-mm-yy), or US form (mm-dd-yy)
  302. X    if -DUSDATE is set.
  303. X
  304. X    This routine should be more flexible re: recognizing date formats.
  305. X-------------------- */
  306. Xvoid
  307. Xget_date(str, date)
  308. X    char    *str, *date;
  309. X{
  310. X    int        i;
  311. X    bool    get_month();
  312. X    char    *next_token();
  313. X
  314. X    date[2] = date[5] = '-';
  315. X    date[8] = '\0';
  316. X    for(; *str == ' ' || *str == '\t' ; str++)
  317. X        ;
  318. X    if(*str == '\0')
  319. X        goto unknown;
  320. X    if(! isdigit(*str))        /* day of month not first field */
  321. X        str = next_token(str);        /* skip day of week */
  322. X    if(str == (char *) NULL + 1)
  323. X        goto unknown;
  324. X
  325. X    if(isdigit(*str)) {        /* one of (a), (b) or (c) formats */
  326. X        if(isdigit(*(str+1)))    /* two-number date */
  327. X#ifdef USDATE
  328. X            DAY1 = *str, DAY2 = *(++str);
  329. X        else
  330. X            DAY1 = '0', DAY2 = *str;
  331. X#else
  332. X            DAY1 = (*str == '0') ? ' ' : *str, DAY2 = *(++str);
  333. X        else
  334. X            DAY1 = ' ', DAY2 = *str;
  335. X#endif
  336. X        str = next_token(str);    /* go to month */
  337. X        if(str == (char *) NULL + 1)
  338. X            goto unknown;
  339. X        if(! get_month(str, date))
  340. X            goto unknown;
  341. X        str = next_token(str);    /* go to year */
  342. X        if(str == (char *) NULL + 1)
  343. X            goto unknown;
  344. X        if(! isdigit(*str) || ! isdigit(*(str+1)))
  345. X            goto unknown;
  346. X        YR1 = *str, YR2 = *(str+1);
  347. X    } else {    /* format (d) */
  348. X        if(! get_month(str, date))
  349. X            goto unknown;
  350. X        str = next_token(str);    /* go to day */
  351. X        if(str == (char *) NULL + 1)
  352. X            goto unknown;
  353. X        if(! isdigit(*str) || ! isdigit(*(str+1)))
  354. X            goto unknown;
  355. X#ifdef USDATE
  356. X        DAY1 = *str, DAY2 = *(++str);
  357. X#else
  358. X        DAY1 = (*str == '0') ? ' ' : *str, DAY2 = *(++str);
  359. X#endif
  360. X        str += strlen(str) - 2;        /* go to year */
  361. X        if(! isdigit(*str) || ! isdigit(*(str+1)))
  362. X            goto unknown;
  363. X        YR1 = *str, YR2 = *(str+1);
  364. X    }
  365. X    return;        /* date is ok */
  366. X
  367. Xunknown:            /* erase date */
  368. X    for(i=0 ; i < 8 ; i++)
  369. X        date[i] = ' ';
  370. X}
  371. X
  372. X
  373. Xstatic char *month[] = {
  374. X    "jan", "feb", "mar", "apr", "may", "jun",
  375. X    "jul", "aug", "sep", "oct", "nov", "dec"
  376. X};
  377. X
  378. X/* --------------------
  379. X    Put the month from the first part of str in date as a number.
  380. X-------------------- */
  381. Xbool
  382. Xget_month(str, date)
  383. X    char    *str, *date;
  384. X{
  385. X    int        i;
  386. X
  387. X    for(i=0 ; i < 12 && lstrncmp(month[i], str, 3) != 0 ; i++)
  388. X        ;
  389. X    if(i == 12)        /* string doesn't match month */
  390. X        return(false);
  391. X    else {    
  392. X        if(i < 9)    /* single digit month */
  393. X#ifdef USDATE
  394. X            MTH1 = ' ', MTH2 = i + '0' + 1;
  395. X#else
  396. X            MTH1 = '0', MTH2 = i + '0' + 1;
  397. X#endif
  398. X        else
  399. X            MTH1 = '1', MTH2 = i + '0' - 9;
  400. X        return(true);
  401. X    }
  402. X}
  403. X
  404. X
  405. X/* --------------------
  406. X    Replace tabs and newlines in string by spaces.
  407. X-------------------- */
  408. Xvoid
  409. Xflatten(str)
  410. X    char    *str;
  411. X{
  412. X    char    prev, *s = str;
  413. X
  414. X    for(prev='\0' ; (*s = *str) != '\0' ; prev = *str, str++) {
  415. X        if(*str == '\t')
  416. X            *s = ' ';
  417. X        if(prev != ' ' || *s != ' ')
  418. X            s++;
  419. X    }
  420. X}
  421. X
  422. X
  423. X/* --------------------
  424. X    Return pointer to first non-white character in string.
  425. X-------------------- */
  426. Xchar *
  427. Xfirst(str)
  428. X    char    *str;
  429. X{
  430. X    for(; *str == ' ' || *str == '\t' || *str == '\n' ; str++)
  431. X        ;
  432. X    return(str);
  433. X}
  434. X
  435. X
  436. X/* --------------------
  437. X    Check given string for occurence of user's name.
  438. X-------------------- */
  439. Xint
  440. Xisuser(str)
  441. X    char    *str;
  442. X{
  443. X    int        len = strlen(user);
  444. X
  445. X    for(; *str != '\0' ; str++)
  446. X            /* assume all chars in all unames alphanumeric */
  447. X        if(*str == *user && strncmp(str, user, len) == 0 &&
  448. X                (*(str+len) < 'a' || *(str+len) > 'z') &&
  449. X                (*(str+len) < 'A' || *(str+len) > 'Z') &&
  450. X                (*(str+len) < '0' || *(str+len) > '9'))
  451. X            return(true);
  452. X    return(false);
  453. X}
  454. X
  455. X
  456. X/* --------------------
  457. X    Make a new folder record.
  458. X-------------------- */
  459. Xfolder
  460. Xnew_folder(flr)
  461. X    folder    flr;
  462. X{
  463. X    folder    f;
  464. X
  465. X    f = NEW(mail_folder);
  466. X    f->prev = flr;
  467. X    f->next = flr->next;
  468. X    flr->next = f;
  469. X    if(f->next != (folder) NULL)
  470. X        f->next->prev = f;
  471. X    f->pagenum = f->pages = flr->pages + 1;
  472. X    f->name = flr->name;
  473. X    f->valid = true;
  474. X    f->mail = f->last = (item) NULL;
  475. X    flr = f;
  476. X    for(f=f->prev; f != (folder) NULL && f->name == flr->name ; f=f->prev)
  477. X        f->pages = flr->pages;
  478. X    return(flr);
  479. X}
  480. X
  481. X
  482. X/* --------------------
  483. X    Find next free slot in directory for mail item (that is, find next
  484. X    unused number) -- structures in vmail may not be up to date if user
  485. X    has initiated a "send" process.
  486. X-------------------- */
  487. Xint
  488. Xnext_vacant(flr)
  489. X    folder    flr;
  490. X{
  491. X    struct direct *dp, *readdir();
  492. X    DIR        *dirp, *opendir();
  493. X    char    str[LEN];
  494. X    int        i, n = 0;
  495. X
  496. X    sprintf(str, "%s/%s", mail_dir, flr->name);
  497. X    dirp = opendir(str);
  498. X    for(dp=readdir(dirp) ; dp != (struct direct *) NULL ; dp=readdir(dirp)) {
  499. X            /* ignore anything that is not string of digits */
  500. X        if(! digits(dp->d_name))
  501. X            continue;
  502. X        if((i = atoi(dp->d_name)) > n)
  503. X            n = i;
  504. X    }
  505. X    closedir(dirp);
  506. X    return(n+1);
  507. X}
  508. !The!End!
  509.  
  510. echo x - "low.c" 2>&1
  511. sed "s/^X//" >"low.c" <<'!The!End!'
  512. X/* --------------------
  513. X    vmail -- low.c
  514. X
  515. X    Low level screen update and data structure update routines, other
  516. X    similar stuff.
  517. X
  518. X    Copyright (C) J. Zobel, University of Melbourne, October 1987.
  519. X-------------------- */
  520. X
  521. X#include "defs.h"
  522. X
  523. X/* --------------------
  524. X    Get string from terminal (visibly).
  525. X-------------------- */
  526. Xvoid
  527. Xget_string(mes, str)
  528. X    char    *mes, *str;
  529. X{
  530. X    char    c;
  531. X    int        i;
  532. X
  533. X    move(STATUS, 0);
  534. X    clrtoeol();
  535. X    addstr(mes);
  536. X    refresh();
  537. X    for(i=0 ; (c = getchar()) != '\n' && c != '\r' && c != ESC ; )
  538. X        if(i > 0 && c == '\b') {
  539. X            i--;
  540. X            mvaddch(STATUS, strlen(mes)+i, ' ');
  541. X            move(STATUS, strlen(mes)+i);
  542. X            refresh();
  543. X        } else if(c != '\b') {
  544. X            str[i++] = c;
  545. X            addch(c);
  546. X            refresh();
  547. X        }
  548. X    str[i] = '\0';
  549. X    move(y, 0);
  550. X}
  551. X
  552. X
  553. X/* --------------------
  554. X    Put string on status line.
  555. X-------------------- */
  556. Xvoid
  557. Xaddstatus(str, stand)
  558. X    char    *str;
  559. X    bool    stand;
  560. X{
  561. X    move(STATUS, 0);
  562. X    clrtoeol();
  563. X    if(stand)
  564. X        standout();        /* doesn't do anything? */
  565. X    addstr(str);
  566. X    if(stand)
  567. X        standend();
  568. X    move(y, 0);
  569. X    refresh();
  570. X}
  571. X
  572. X
  573. X/* --------------------
  574. X    Fudge deleteline because curses wont delete the line immediately
  575. X    after a ^Z, and because it doesnt clear the last line of the screen.
  576. X-------------------- */
  577. Xvoid
  578. Xdeleteline()
  579. X{
  580. X    mvaddstr(y, 0, "scribble");
  581. X    deleteln();
  582. X    move(FIRST+lines-1, 0);
  583. X    clrtoeol();
  584. X    move(y, 0);
  585. X}
  586. X
  587. X
  588. Xint
  589. Xputch(c)
  590. X    char    c;
  591. X{
  592. X    putchar(c);    /* turn macro into routine */
  593. X}
  594. X
  595. X
  596. X/* --------------------
  597. X    Put prompt on screen, wait for getchar().
  598. X-------------------- */
  599. Xint
  600. Xuse_prompt(win)
  601. X    WINDOW    *win;
  602. X{
  603. X    int        c = (cols-42)/2;
  604. X
  605. X    mvwaddstr(win,FIRST+lines-1,c,"-- hit space to continue, q to quit -- ");
  606. X    wrefresh(win);
  607. X    return(getchar());
  608. X}
  609. X
  610. X
  611. X/* --------------------
  612. X    Put info for mail item on screen at row i.
  613. X-------------------- */
  614. Xvoid
  615. Xshow_title(str, i, mail)
  616. X    char    *str;
  617. X    int        i;
  618. X    item    mail;
  619. X{
  620. X    sprintf(str, "%4d%s", mail->number, mail->title);
  621. X    mvaddstr(i, 0, str);
  622. X}
  623. X
  624. X
  625. X/* --------------------
  626. X    Put header for page at top of screen.
  627. X-------------------- */
  628. Xvoid
  629. Xadd_page_header(str)
  630. X    char    *str;
  631. X{
  632. X    sprintf(str, "%s (page %d of %d) ", curflr->name, curflr->pagenum,
  633. X                                                            curflr->pages);
  634. X    mvaddstr(TITLE, 0, str);
  635. X}
  636. X
  637. X
  638. X/* --------------------
  639. X    Flush typeahead.
  640. X-------------------- */
  641. Xchar
  642. Xflushin()
  643. X{
  644. X    struct sgttyb sg;
  645. X
  646. X    if(do_flush) {
  647. X        ioctl(0, TIOCGETP, &sg);
  648. X        ioctl(0, TIOCSETP, &sg);
  649. X    }
  650. X    return(getchar());
  651. X}
  652. X
  653. X
  654. X/* --------------------
  655. X    Print message, wait for getchar();
  656. X-------------------- */
  657. Xvoid
  658. Xhold_end()
  659. X{
  660. X    tputs(SO, 1, putch);
  661. X    printf("-- any character to continue --");
  662. X    tputs(SE, 1, putch);
  663. X    fflush(stdout);
  664. X    getchar();
  665. X}
  666. X
  667. X
  668. X#define ctol(c)        (((c) >= 'A' && (c) <= 'Z') ? c-'A'+'a' : c)
  669. X
  670. X/* --------------------
  671. X    Case independent strncmp.
  672. X-------------------- */
  673. Xlstrncmp(s1, s2, n)
  674. X    char *s1, *s2;
  675. X    int n;
  676. X{
  677. X    for( ; ctol(*s1) == ctol(*s2) && n > 0 ; s1++, s2++, n--)
  678. X        ;
  679. X    return((n > 0) ? *s1 - *s2 : 0);
  680. X}
  681. X
  682. X
  683. X/* --------------------
  684. X    String processing - more useful form of index.
  685. X    Skip current non-white then skip white, replacing by null.
  686. X-------------------- */
  687. Xchar *
  688. Xnext_token(t)
  689. X    char    *t;
  690. X{
  691. X    for( ; *t != ' ' && *t != '\t' && *t != '\0' ; t++)
  692. X        ;
  693. X    for( ; *t == ' ' || *t == '\t' ; t++)
  694. X        *t = '\0';
  695. X    return(t);
  696. X}
  697. !The!End!
  698.  
  699. echo x - "main.c" 2>&1
  700. sed "s/^X//" >"main.c" <<'!The!End!'
  701. X/* --------------------
  702. X    vmail -- main.c
  703. X
  704. X    Definitions of global variables, main switch.
  705. X
  706. X    Copyright (C) J. Zobel, University of Melbourne, October 1987.
  707. X-------------------- */
  708. X
  709. X#include "defs.h"
  710. X
  711. Xfolder    folders = (folder) NULL, curflr, alternate = (folder) NULL;
  712. Xitem    curmail;
  713. Xchar     *user, *pager = (char *) NULL, *editor = (char *) NULL,
  714. X        *mail_dir = (char *) NULL, *shell = (char *) NULL, *context;
  715. Xbool    top_level = true, do_flush = true, comp_args = true, repl_args = true,
  716. X        forw_args = true;
  717. Xint        lines, cols, y, folder_protect = FPROT;
  718. Xjmp_buf    env;
  719. X
  720. X#define HELP        37
  721. X
  722. Xchar *help_scr[] = {
  723. X"       .                Re-execute last command (if one of acdDefirR)",
  724. X"                                   (on repeat, r does not prompt for folder)",
  725. X"       /<exp>           Search forwards for title with <exp> (/ to repeat)",
  726. X"       ?<exp>           Search backwards for title with <exp> (? to repeat)",
  727. X"       <space>          Show current mail item",
  728. X"       [n]<return>      Go forward one (or n) active page(s) of mail items",
  729. X"       [n]<backspace>   Go back one (or n) active page(s) of mail items",
  730. X"       ^L               Refresh",
  731. X"       !                Invoke shell",
  732. X"       |                Pipe current mail item into command",
  733. X"       ^                Go to first active page",
  734. X"       $                Go to last active page",
  735. X"       a                Answer current mail item (call to \"repl\")",
  736. X"       c                Compose new mail item (call to \"comp\")",
  737. X"       C                Go to folder chooser",
  738. X"       [n]d             Delete current (or next inclusive n) mail item(s)",
  739. X"       D                Delete current mail item, show next",
  740. X"       e                Edit current mail item",
  741. X"       f                Forward current mail item (call to \"forw\")",
  742. X"       F                List all folders",
  743. X"       g,G              Go to named (or alternate) folder",
  744. X"       h                Show this page",
  745. X"       [n]H             Go to top of page (or nth from top line)",
  746. X"       i                Incorporate new mail (call to \"inc\")",
  747. X"       [n]j             Move cursor down one (or n) line(s)",
  748. X"       [n]k             Move cursor up one (or n) line(s)",
  749. X"       [n]L             Go to bottom of page (or nth from bottom line)",
  750. X"       M                Go to middle of page",
  751. X"       [n]n             Go forward one (or n) folders, make new folder active",
  752. X"       [n]p             Go back one (or n) folders, make new folder active",
  753. X"       P                Print name of alternate folder",
  754. X"       q                Exit from vmail",
  755. X"       r,R              Refile item in named (or previous) folder",
  756. X"       s                Save current item in named file",
  757. X"       u                Undo most recent deletion (1 mail item only)",
  758. X"       v                Make current folder inactive",
  759. X"       z                Pack current folder"
  760. X};
  761. X
  762. X
  763. X#define USAGE    printf( \
  764. X"Usage: vmail [-inc] [-flush] [-comp] [-forw] [-ans] [+curfolder] folders ...\n"), \
  765. X                exit(1)
  766. X
  767. Xmain(argc, argv)
  768. X    int        argc;
  769. X    char    *argv[];
  770. X{
  771. X    char    c, last = '\0', get_number(), flushin();
  772. X    int        count = 1,        /* accumulate count to give to command */
  773. X            pcount = 1;
  774. X    bool    flag = false;    /* used to check whether to reset count */
  775. X
  776. X    argc--, argv++;
  777. X
  778. X    process_args(argc, argv);
  779. X#ifndef VERSION
  780. X#define VERSION    "(local version)"
  781. X#endif
  782. X    printf("vmail %s -- reading mail headers\n", VERSION);
  783. X    init(argc, argv);
  784. X    setjmp(env);        /* return point from interrupt */
  785. X    c = flushin();
  786. X    for(;;) {
  787. X        while(c == '.')
  788. X            if(last == '\0') {
  789. X                beep();
  790. X                c = flushin();
  791. X            } else {
  792. X                count = pcount;
  793. X                c = last;
  794. X            }
  795. X        switch(c) {
  796. X            case '|':    /* pipe mail to command */
  797. X                do_pipe();
  798. X                break;
  799. X            case '!':    /* call shell */
  800. X                call_shell();
  801. X                break;
  802. X            case '/':    /* search forwards */
  803. X                search(true);
  804. X                break;
  805. X            case '?':    /* search backwards */
  806. X                search(false);
  807. X                break;
  808. X            case '^':    /* first active page */
  809. X                goto_first_page();
  810. X                break;
  811. X            case '$':    /* last active page */
  812. X                goto_last_page();
  813. X                break;
  814. X            case '\n':    /* next folder */
  815. X            case '\r':    /* next folder */
  816. X                next_page(count, true);
  817. X                break;
  818. X            case ' ':    /* show current item */
  819. X                show_mail();
  820. X                break;
  821. X            case DEL:    /* previous folder */
  822. X            case '\b':    /* previous folder */
  823. X                prev_page(count, true);
  824. X                break;
  825. X            case 'a':    /* answer - call to repl */
  826. X                repl();
  827. X                break;
  828. X            case 'c':    /* compose - fork of comp */
  829. X                comp();
  830. X                break;
  831. X            case 'C':    /* go to folder chooser */
  832. X                choose();
  833. X                break;
  834. X            case 'd':    /* delete item */
  835. X                delete_item(count);
  836. X                break;
  837. X            case 'D':    /* delete item, show next */
  838. X                change_item(false);
  839. X                show_mail();
  840. X                break;
  841. X            case 'e':    /* edit current mail item */
  842. X                edit();
  843. X                break;
  844. X            case 'f':    /* forward - call to forw */
  845. X                forw();
  846. X                break;
  847. X            case 'F':    /* list all folders */
  848. X                list_folders();
  849. X                break;
  850. X            case 'g':    /* go to named folder */
  851. X                goto_folder(true);
  852. X                break;
  853. X            case 'G':    /* go to last-named folder */
  854. X                goto_folder(false);
  855. X                break;
  856. X            case 'h':    /* help */
  857. X                help();
  858. X                break;
  859. X            case 'H':    /* go to top of page */
  860. X                cursor_first(count);
  861. X                break;
  862. X            case 'i':    /* inc */
  863. X                inc();
  864. X                break;
  865. X            case 'j':    /* cursor down */
  866. X                cursor_down(count);
  867. X                break;
  868. X            case 'k':    /* cursor up */
  869. X                cursor_up(count);
  870. X                break;
  871. X            case CTRL_L: /* redraw */
  872. X                display_page();
  873. X                break;
  874. X            case 'L':    /* go to bottom of page */
  875. X                cursor_last(count);
  876. X                break;
  877. X            case 'M':    /* go to middle of page */
  878. X                cursor_middle();
  879. X                break;
  880. X            case 'n':    /* next folder - load if not there */
  881. X                goto_next_folder(count);
  882. X                break;
  883. X            case 'p':    /* prev folder - load if not there */
  884. X                goto_prev_folder(count);
  885. X                break;
  886. X            case 'P':    /* print name of last-named folder */
  887. X                show_folder();
  888. X                break;
  889. X            case 'q':    /* quit */
  890. X                to_normal();
  891. X                fix_mh();
  892. X                exit(0);
  893. X                break;
  894. X            case 'r':    /* move item to named folder */
  895. X                move_item(true);
  896. X                break;
  897. X            case 'R':    /* move item to previous folder */
  898. X                move_item(false);
  899. X                break;
  900. X            case 's':    /* save item in named file */
  901. X                save_item();
  902. X                break;
  903. X            case 'u':    /* undo */
  904. X                undo();
  905. X                break;
  906. X            case 'v':    /* make folder inactive */
  907. X                inactive();
  908. X                break;
  909. X            case 'z':    /* pack current folder */
  910. X                pack_folder();
  911. X                break;
  912. X            case '0':    /* start of count */
  913. X            case '1':
  914. X            case '2':
  915. X            case '3':
  916. X            case '4':
  917. X            case '5':
  918. X            case '6':
  919. X            case '7':
  920. X            case '8':
  921. X            case '9':
  922. X                c = get_number(c, &count);
  923. X                flag = true;
  924. X                break;
  925. X            default:
  926. X                addstatus("command unknown -- `h' for help", true);
  927. X                break;
  928. X        }
  929. X        switch(c) {
  930. X                /* repeatable commands */
  931. X            case 'r':    /* refile item in named folder */
  932. X                last = 'R';
  933. X                pcount = 1;
  934. X                break;
  935. X            case 'a':    /* reply - call to repl */
  936. X            case 'c':    /* compose - fork of comp */
  937. X            case 'd':    /* delete item */
  938. X            case 'D':    /* delete item, show next */
  939. X            case 'e':    /* edit current mail item */
  940. X            case 'f':    /* forward - call to forw */
  941. X            case 'i':    /* inc */
  942. X            case 'R':    /* refile to previous folder */
  943. X            case 'w':    /* write to file */
  944. X                last = c;
  945. X                pcount = count;
  946. X                break;
  947. X            default:
  948. X                break;
  949. X        }
  950. X        if(!flag) {
  951. X            c = flushin();
  952. X            count = 1;
  953. X        } else {
  954. X            flag = false;
  955. X        }
  956. X    }
  957. X}
  958. X
  959. X
  960. X/* --------------------
  961. X    Read a number from terminal.
  962. X-------------------- */
  963. Xchar
  964. Xget_number(c, count)
  965. X    char    c;
  966. X    int        *count;
  967. X{
  968. X    *count = c - '0';
  969. X    while((c = getchar()) >= '0' && c <= '9')
  970. X        *count = *count * 10 + c - '0';
  971. X    return(c);
  972. X}
  973. X
  974. X
  975. XWINDOW *helpwin = (WINDOW *) NULL;
  976. X
  977. X/* --------------------
  978. X    Display help messages on help screen.
  979. X-------------------- */
  980. Xhelp()
  981. X{
  982. X    WINDOW    *newwin();
  983. X    int        i, j;
  984. X
  985. X    if(helpwin == (WINDOW *) NULL)
  986. X        helpwin = newwin(0, 0, 0, 0);
  987. X    wclear(helpwin);
  988. X    for(j=i=0 ; i <= HELP ; i++, j++) {
  989. X        if((i+2) % lines == 0) {
  990. X            if(use_prompt(helpwin) == 'q')
  991. X                break;
  992. X            j=0;
  993. X            wclear(helpwin);
  994. X        }
  995. X        if(i >= HELP) {
  996. X            use_prompt(helpwin);
  997. X            break;
  998. X        }
  999. X        mvwaddstr(helpwin, j+1, 0, help_scr[i]);
  1000. X    }
  1001. X    display_page();
  1002. X}
  1003. X
  1004. X
  1005. X/* --------------------
  1006. X    Process arguments, command-line or given in profile.
  1007. X-------------------- */
  1008. Xvoid
  1009. Xprocess_args(argc, argv)
  1010. X    int argc;
  1011. X    char *argv[];
  1012. X{
  1013. X    for( ; argc > 0 ; argc--, argv++)
  1014. X        if(!strcmp(argv[0], "-inc")) {    /* incorporate mail before starting */
  1015. X            if(!vfork()) {
  1016. X                execlp(INC, INC, 0);
  1017. X                printf("Warning: can't execute %s\n", INC);
  1018. X                exit(0);
  1019. X            }
  1020. X            wait(0);
  1021. X            printf("\n");
  1022. X        } else if(!strcmp(argv[0], "-flush"))    /* don't flush typeahead */
  1023. X            do_flush = false;
  1024. X        else if(!strcmp(argv[0], "-forw"))        /* no args to forw */
  1025. X            forw_args = false;
  1026. X        else if(!strcmp(argv[0], "-comp"))        /* no args to comp */
  1027. X            comp_args = false;
  1028. X        else if(!strcmp(argv[0], "-ans"))        /* no args to repl */
  1029. X            repl_args = false;
  1030. X        else if(*argv[0] == '-') {
  1031. X            printf("%s: illegal option\n", argv[0]);
  1032. X            USAGE;
  1033. X        }
  1034. X}
  1035. !The!End!
  1036.  
  1037. echo x - "move.c" 2>&1
  1038. sed "s/^X//" >"move.c" <<'!The!End!'
  1039. X/* --------------------
  1040. X    vmail -- move.c
  1041. X
  1042. X    Routines to delete or move a mail item from current folder.
  1043. X
  1044. X    Copyright (C) J. Zobel, University of Melbourne, October 1987.
  1045. X-------------------- */
  1046. X
  1047. X#include "defs.h"
  1048. X#include <errno.h>
  1049. X
  1050. Xstatic char prevfile[LEN] = "";
  1051. X
  1052. Xextern int errno;
  1053. X
  1054. X/* --------------------
  1055. X    Move current item to folder prevfile (get_name = false) or as read from
  1056. X    terminal (get_name = true).
  1057. X-------------------- */
  1058. Xvoid
  1059. Xmove_item(get_name)
  1060. X    bool    get_name;
  1061. X{
  1062. X    char    *s, str[LEN], str2[LEN];
  1063. X    folder    f, new_folder(), create_folder();
  1064. X    item    m;
  1065. X    bool    redraw,                        /* true if screen to be refreshed */
  1066. X            change_item();
  1067. X    int        fdi, fdo, i = FIRST-1, num, N;
  1068. X
  1069. X        /* get name if required */
  1070. X    if(get_name || *prevfile == '\0') {
  1071. X        get_string("folder? ", str);
  1072. X        if(*str == '\0') {
  1073. X            addstatus("no name given for folder", true);
  1074. X            return;
  1075. X        }
  1076. X        strcpy(prevfile, str);
  1077. X    } else
  1078. X        strcpy(str, prevfile);
  1079. X        /* find first page of named folder */
  1080. X    sprintf(str2, "refiling to %s ...", str);
  1081. X    addstatus(str2, false);
  1082. X    GOTO_NAME(f, str);
  1083. X    if(f == (folder) NULL) {    /* create folder */
  1084. X        f = create_folder(str);
  1085. X        if(f == (folder) NULL)
  1086. X            return;
  1087. X    } else {
  1088. X        if(f->name == curflr->name) {
  1089. X            addstatus("can't move item to current folder", true);
  1090. X            return;
  1091. X        }
  1092. X        if(f->valid) {        /* goto last mail item in folder */
  1093. X            LAST_OF_NAME(f);
  1094. X            for(i=FIRST, m=f->mail ; m->next != (item) NULL ; i++, m=m->next)
  1095. X                ;
  1096. X        }
  1097. X    }
  1098. X        /* remember current location of mail */
  1099. X    m = curmail;
  1100. X    num = curmail->number;
  1101. X    s = curflr->name;
  1102. X        /* delete item from current folder, update current folder & screen */
  1103. X    redraw = change_item(true);
  1104. X    if(f->valid)        /* update structures of mail items */
  1105. X        if(i > lines) {
  1106. X                /* create new folder record */
  1107. X            f = new_folder(f);
  1108. X            f->mail = f->last = m;
  1109. X            m->prev = m->next = (item) NULL;
  1110. X            N = f->prev->last->number;
  1111. X        } else {
  1112. X                /* insert at end of list of mail items */
  1113. X            m->prev = f->last; m->next = (item) NULL;
  1114. X            f->last->next = m;
  1115. X            f->last = m;
  1116. X            N = m->prev->number;
  1117. X        }
  1118. X    else
  1119. X        N = next_vacant(f);
  1120. X/* to avoid race between "send" or other process in background and vmail
  1121. X   foreground, compute next free slot, dont just use given value 
  1122. X*/
  1123. X    sprintf(str2, "%s/%s/%d", mail_dir, f->name, N);
  1124. X        /* loop until unused file name found */
  1125. X    for(errno=0 ; (fdo = open(str2, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0
  1126. X                                            && errno == EEXIST ; errno=0) {
  1127. X        N = N + 1;
  1128. X        sprintf(str2, "%s/%s/%d", mail_dir, f->name, N);
  1129. X    }
  1130. X    if(f->valid)
  1131. X        m->number = N;
  1132. X    sprintf(str, "%s/%s/%d", mail_dir, s, num);
  1133. X    fdi = open(str, O_RDONLY);
  1134. X    while((i = read(fdi, str2, LEN)) > 0)    /* copy original to new */
  1135. X        write(fdo, str2, i);
  1136. X    close(fdi);
  1137. X    close(fdo);
  1138. X    unlink(str);                            /* remove original */
  1139. X    if(redraw)
  1140. X        display_page();
  1141. X    else {
  1142. X        add_page_header(str);
  1143. X        move(y, 0);
  1144. X        refresh();
  1145. X    }
  1146. X    addstatus("refiled", true);
  1147. X    if(curflr == (folder) NULL) {
  1148. X        to_normal();
  1149. X        exit(1);
  1150. X    }
  1151. X}
  1152. X
  1153. X
  1154. X/* --------------------
  1155. X    Delete count items.
  1156. X-------------------- */
  1157. Xvoid
  1158. Xdelete_item(count)
  1159. X    int        count;
  1160. X{
  1161. X    bool    redraw = false, change_item();
  1162. X    item    m;
  1163. X    char    str[LEN];
  1164. X
  1165. X    for( ; count > 0 ; count--) {
  1166. X        m = curmail->next;
  1167. X        redraw = change_item(false);
  1168. X        if(redraw || m != curmail) /* on new page, or last item deleted */
  1169. X            break;
  1170. X    }
  1171. X    if(redraw)
  1172. X        display_page();
  1173. X    else {
  1174. X        add_page_header(str);
  1175. X        move(y, 0);
  1176. X        refresh();
  1177. X    }
  1178. X}
  1179. X
  1180. X
  1181. X/* --------------------
  1182. X    Structure for deleted item.
  1183. X-------------------- */
  1184. Xstruct {
  1185. X    folder flr;
  1186. X    int number;
  1187. X} deleted = {(folder) NULL, 0};
  1188. X
  1189. X
  1190. X/* --------------------
  1191. X    Either delete (do_move = false) or prepare to move (do_move = true) item.
  1192. X    Delete item from current folder, update screen, find new current folder
  1193. X    if current folder has become empty.
  1194. X-------------------- */
  1195. Xbool
  1196. Xchange_item(do_move)
  1197. X    bool    do_move;
  1198. X{
  1199. X    item    tmp, m = curmail;
  1200. X    folder    F, p, f = curflr, pval, nval;
  1201. X    char    s1[LEN], s2[LEN];
  1202. X    bool    redraw, doexit = false;
  1203. X
  1204. X    if(curmail->next == (item) NULL && curmail->prev == (item) NULL) {
  1205. X            /* have last item in page */
  1206. X        redraw = true;
  1207. X        pval = curflr->prev; PREV_VALID(pval);
  1208. X        nval = curflr->next; NEXT_VALID(nval);
  1209. X        if(pval == (folder) NULL && nval == (folder) NULL) {
  1210. X                        /* no more active pages */
  1211. X            if(! do_move)
  1212. X                addstatus("Deleting last active mail item -- bye", true);
  1213. X            doexit = true;
  1214. X        } else {
  1215. X                /* update pages, pagenum */
  1216. X            for(p=curflr->prev ; p != (folder) NULL && p->name == curflr->name
  1217. X                                                                    ; p=p->prev)
  1218. X                p->pages -= 1;
  1219. X            for(p=curflr->next ; p != (folder) NULL && p->name == curflr->name
  1220. X                                                                    ; p=p->next)
  1221. X                p->pages -= 1, p->pagenum -= 1;
  1222. X                /* find next active page, drop current page from list */
  1223. X            if(curflr->prev != (folder) NULL)
  1224. X                curflr->prev->next = curflr->next;
  1225. X            else
  1226. X                folders = curflr->next;
  1227. X            if(curflr->next != (folder) NULL)
  1228. X                curflr->next->prev = curflr->prev;
  1229. X            curflr = (nval == (folder) NULL) ? pval : nval;
  1230. X            curmail = curflr->mail;
  1231. X        }
  1232. X    } else {
  1233. X        redraw = false;
  1234. X        deleteline();
  1235. X            /* move first item from next to current */
  1236. X        if(curflr->next != (folder) NULL && curflr->name == curflr->next->name)
  1237. X            show_title(s1, FIRST+lines-1, curflr->next->mail);
  1238. X        for(p=curflr->next ; p != (folder) NULL && p->name == curflr->name ;
  1239. X                                                                    p=p->next) {
  1240. X            tmp = p->mail;
  1241. X            p->mail = tmp->next;
  1242. X            if(p->mail == (item) NULL) { /* remove folder from list */
  1243. X                p->prev->next = p->next;
  1244. X                if(p->next != (folder) NULL)
  1245. X                    p->next->prev = p->prev;
  1246. X                    /* update page counts */
  1247. X                for(F=p->prev ; F != (folder) NULL && F->name == p->name
  1248. X                                                                    ; F=F->prev)
  1249. X                    F->pages -= 1;
  1250. X            } else
  1251. X                p->mail->prev = (item) NULL;
  1252. X            p->prev->last->next = tmp;
  1253. X            tmp->prev = p->prev->last;
  1254. X            tmp->next = (item) NULL;
  1255. X            p->prev->last = tmp;
  1256. X        }
  1257. X            /* delete item from linked list of items */
  1258. X        if(m->prev == (item) NULL)
  1259. X            curflr->mail = m->next;
  1260. X        else
  1261. X            m->prev->next = m->next;
  1262. X        if(m->next == (item) NULL) {
  1263. X            curflr->last = m->prev;
  1264. X            curmail = m->prev;
  1265. X            y--;
  1266. X        } else {
  1267. X            m->next->prev = m->prev;
  1268. X            curmail = m->next;
  1269. X        }
  1270. X        move(y, 0);
  1271. X    }
  1272. X    if(! do_move) {
  1273. X        deleted.flr = f;
  1274. X        deleted.number = m->number;
  1275. X        sprintf(s1, "%s/%s/%d", mail_dir, f->name, m->number);
  1276. X        sprintf(s2, "%s/%s/#%d", mail_dir, f->name, m->number);
  1277. X        rename(s1, s2);
  1278. X        if(doexit) {
  1279. X            to_normal();
  1280. X            exit(0);
  1281. X        }
  1282. X    }
  1283. X    return(redraw);
  1284. X}
  1285. X
  1286. X
  1287. X/* --------------------
  1288. X    Create folder of given name if user agrees, creating directory and
  1289. X    entry in linked list of folders.
  1290. X-------------------- */
  1291. Xfolder
  1292. Xcreate_folder(str)
  1293. X    char    *str;
  1294. X{
  1295. X    struct stat statbuf;
  1296. X    char    str2[LEN], c;
  1297. X    folder    fnew, p, f;
  1298. X
  1299. X    squash(str);
  1300. X    sprintf(str2, "%s does not exist (or is empty) - create? ", str);
  1301. X    mvaddstr(STATUS, 0, str2); refresh();
  1302. X    c = getchar();
  1303. X    move(STATUS, 0); clrtoeol(); move(y, 0); refresh();
  1304. X    if(c != 'y')
  1305. X        return((folder) NULL);
  1306. X    for(p=(folder) NULL, f=folders ; f != (folder) NULL &&
  1307. X                            strcmp(str, f->name) > 0 ; p=f, f=f->next)
  1308. X        ;
  1309. X        /* create physical folder */
  1310. X    sprintf(str2, "%s/%s", mail_dir, str);
  1311. X    if(stat(str2, &statbuf)) {        /* doesn't exist */
  1312. X        if(mkdir(str2, folder_protect)) {
  1313. X            addstatus("Cannot make folder", true);
  1314. X            return((folder) NULL);
  1315. X        }
  1316. X    } else
  1317. X        if(!(statbuf.st_mode & S_IREAD) || !(statbuf.st_mode & S_IWRITE)
  1318. X                                    || !(statbuf.st_mode & S_IEXEC)) {
  1319. X                addstatus("Cannot write in folder", true);
  1320. X                return((folder) NULL);
  1321. X        }
  1322. X            /* make a new folder record, insert it */
  1323. X    fnew = NEW(mail_folder);
  1324. X    fnew->name = NEWSTR(strlen(str)+1);
  1325. X    strcpy(fnew->name, str);
  1326. X    fnew->mail = fnew->last = (item) NULL;
  1327. X    fnew->next = fnew->prev = (folder) NULL;
  1328. X    fnew->pages = fnew->pagenum = 1;
  1329. X    fnew->valid = false;
  1330. X    if(p == (folder) NULL) {
  1331. X        fnew->next = folders;
  1332. X        folders->prev = fnew;
  1333. X        folders = fnew;
  1334. X    } else {
  1335. X        p->next = fnew;
  1336. X        if(f != (folder) NULL)
  1337. X            f->prev = fnew;
  1338. X        fnew->prev = p;
  1339. X        fnew->next = f;
  1340. X    }
  1341. X    return(fnew);
  1342. X}
  1343. X
  1344. X
  1345. X/* --------------------
  1346. X    Crude undo.  Sophisticated undo rather too painful to code.
  1347. X-------------------- */
  1348. Xvoid
  1349. Xundo()
  1350. X{
  1351. X    folder    f = deleted.flr, p;
  1352. X    char    s1[LEN], s2[LEN];
  1353. X
  1354. X    if(f == (folder) NULL) {
  1355. X        addstatus("nothing to undo", true);
  1356. X        return;
  1357. X    } else {
  1358. X        addstatus("undoing ...", true);
  1359. X        deleted.flr = (folder) NULL;
  1360. X    }
  1361. X    sprintf(s1, "%s/%s/#%d", mail_dir, f->name, deleted.number);
  1362. X    sprintf(s2, "%s/%s/%d", mail_dir, f->name, deleted.number);
  1363. X    rename(s1, s2);
  1364. X        /* find first page of folder */
  1365. X    p = f; FRST_OF_NAME(p);
  1366. X        /* find last page of folder */
  1367. X    LAST_OF_NAME(f);
  1368. X    curflr = p;
  1369. X    p->next = f->next;
  1370. X/* should free old folder/mail records */
  1371. X/*    p->valid = false; */
  1372. X    p->mail = p->last = (item) NULL;
  1373. X    p->pagenum = p->pages = 1;
  1374. X    if(p->next != (folder) NULL)
  1375. X        p->next->prev = p;
  1376. X    find_mail(curflr, false);
  1377. X    curmail = curflr->mail;
  1378. X    y = FIRST;
  1379. X    display_page();
  1380. X}
  1381. X
  1382. X
  1383. X/* --------------------
  1384. X    Pack current folder.  Unsets "deleted" if last removed record was
  1385. X    on current folder.
  1386. X-------------------- */
  1387. Xvoid
  1388. Xpack_folder()
  1389. X{
  1390. X    folder    f = curflr;
  1391. X    item    m;
  1392. X    char    path[LEN], s1[LEN], s2[LEN];
  1393. X    bool    found;
  1394. X    int        newnum = 1;
  1395. X
  1396. X    addstatus("Packing folder ...", false);
  1397. X    FRST_OF_NAME(f);
  1398. X        /* unset undo if packing that folder */
  1399. X    if(deleted.flr != (folder) NULL && deleted.flr->name == curflr->name)
  1400. X        deleted.flr = (folder) NULL;
  1401. X    sprintf(path, "%s/%s/", mail_dir, curflr->name);
  1402. X    for( ; f != (folder) NULL && f->name == curflr->name ; f=f->next)
  1403. X        for(m=f->mail ; m != (item) NULL ; m=m->next) {
  1404. X            for(found=false ; !found && newnum < m->number ; ) {
  1405. X                sprintf(s1, "%s%d", path, newnum);
  1406. X                if(! access(s1, R_OK))
  1407. X                    newnum++;
  1408. X                else
  1409. X                    found = true;
  1410. X            }
  1411. X            if(found) {
  1412. X                sprintf(s2, "%s%d", path, m->number);
  1413. X                rename(s2, s1);
  1414. X                m->number = newnum;
  1415. X            }
  1416. X        }
  1417. X    display_page();
  1418. X}
  1419. !The!End!
  1420.  
  1421. echo x - "page.c" 2>&1
  1422. sed "s/^X//" >"page.c" <<'!The!End!'
  1423. X/* --------------------
  1424. X    vmail -- page.c
  1425. X
  1426. X    Routines for paging between folders.
  1427. X
  1428. X    Copyright (C) J. Zobel, University of Melbourne, October 1987.
  1429. X-------------------- */
  1430. X
  1431. X#include "defs.h"
  1432. X
  1433. X/* --------------------
  1434. X    Clear screen, show current page of mail items.
  1435. X-------------------- */
  1436. Xvoid
  1437. Xdisplay_page()
  1438. X{
  1439. X    int        i;
  1440. X    item    mail;
  1441. X    char    str[LEN];
  1442. X
  1443. X    clear();
  1444. X    add_page_header(str);
  1445. X    for(i=FIRST, mail=curflr->mail ; mail != (item) NULL ; i++, mail=mail->next)
  1446. X        show_title(str, i, mail);
  1447. X    move(y, 0);
  1448. X    refresh();
  1449. X}
  1450. X
  1451. X
  1452. X/* --------------------
  1453. X    Find previous valid page of mail items (times count).
  1454. X-------------------- */
  1455. Xvoid
  1456. Xprev_page(count, display)
  1457. X    int        count;
  1458. X    bool    display;
  1459. X{
  1460. X    bool    show = false;
  1461. X    folder    f, p = curflr;
  1462. X
  1463. X    for( ; count > 0 ; count--) {
  1464. X        f = curflr->prev; PREV_VALID(f);
  1465. X        if(f == (folder) NULL)
  1466. X            break;
  1467. X        else {
  1468. X            show = true;
  1469. X            y = FIRST;
  1470. X            curflr = f;
  1471. X            curmail = curflr->mail;
  1472. X        }
  1473. X    }
  1474. X    if(p != curflr)
  1475. X        alternate = p;
  1476. X    if(display) {
  1477. X        if(show)
  1478. X            display_page();
  1479. X        if(f == (folder) NULL)
  1480. X            addstatus("first active page", true);
  1481. X    }
  1482. X}
  1483. X
  1484. X
  1485. X/* --------------------
  1486. X    Find next valid page of mail items (times count).
  1487. X-------------------- */
  1488. Xvoid
  1489. Xnext_page(count, display)
  1490. X    int        count;
  1491. X    bool    display;
  1492. X{
  1493. X    bool    show = false;
  1494. X    folder    f, p = curflr;
  1495. X
  1496. X    for( ; count > 0 ; count--) {
  1497. X        f = curflr->next; NEXT_VALID(f);
  1498. X        if(f == (folder) NULL)
  1499. X            break;
  1500. X        else {
  1501. X            show = true;
  1502. X            y = FIRST;
  1503. X            curflr = f;
  1504. X            curmail = curflr->mail;
  1505. X        }
  1506. X    }
  1507. X    if(p != curflr)
  1508. X        alternate = p;
  1509. X    if(display) {
  1510. X        if(show)
  1511. X            display_page();
  1512. X        if(f == (folder) NULL)
  1513. X            addstatus("last active page", true);
  1514. X    }
  1515. X}
  1516. X
  1517. X
  1518. X/* --------------------
  1519. X    Move cursor up count lines.
  1520. X-------------------- */
  1521. Xvoid
  1522. Xcursor_up(count)
  1523. X    int count;
  1524. X{
  1525. X    bool    redraw = false;
  1526. X
  1527. X    addstatus("", false);                /* clear status line */
  1528. X    for( ; count > 0 ; count--)
  1529. X        if(curmail->prev == (item) NULL) {
  1530. X            if(curflr->prev == (folder) NULL ||
  1531. X                                    curflr->name != curflr->prev->name) {
  1532. X                addstatus("first page of folder", true);
  1533. X                break;
  1534. X            } else {
  1535. X                redraw = true;
  1536. X                prev_page(1, false);
  1537. X                    /* find last mail item on page */
  1538. X                for( ; curmail->next != (item) NULL ; y++)
  1539. X                    curmail=curmail->next;
  1540. X            }
  1541. X        } else {
  1542. X            y--;
  1543. X            curmail = curmail->prev;
  1544. X        }
  1545. X    if(redraw)
  1546. X        display_page();
  1547. X    else {
  1548. X        move(y, 0);
  1549. X        refresh();
  1550. X    }
  1551. X}
  1552. X
  1553. X
  1554. X/* --------------------
  1555. X    Move cursor down count lines.
  1556. X-------------------- */
  1557. Xvoid
  1558. Xcursor_down(count)
  1559. X    int        count;
  1560. X{
  1561. X    bool    redraw = false;
  1562. X
  1563. X    addstatus("", false);                /* clear status line */
  1564. X    for( ; count > 0 ; count--)
  1565. X        if(curmail->next == (item) NULL) {
  1566. X            if(curflr->next == (folder) NULL ||
  1567. X                                    curflr->name != curflr->next->name) {
  1568. X                addstatus("last page of folder", true);
  1569. X                break;
  1570. X            } else {
  1571. X                redraw = true;
  1572. X                next_page(1, false);
  1573. X            }
  1574. X        } else {
  1575. X            y++;
  1576. X            curmail = curmail->next;
  1577. X        }
  1578. X    if(redraw)
  1579. X        display_page();
  1580. X    else {
  1581. X        move(y, 0);
  1582. X        refresh();
  1583. X    }
  1584. X}
  1585. X
  1586. X
  1587. X/* --------------------
  1588. X    Go to next folder, make active if not so already.
  1589. X-------------------- */
  1590. Xvoid
  1591. Xgoto_next_folder(count)
  1592. X    int        count;
  1593. X{
  1594. X    bool    changed = false;
  1595. X    folder    f, p = curflr;
  1596. X
  1597. X    for( ; count > 0 ; count--) {
  1598. X            /* skip other pages of current folder */
  1599. X        for(f=p ; p->next->name == f->name ; p=p->next)
  1600. X            ;
  1601. X        if(p->next == (folder) NULL) {
  1602. X            addstatus("last folder", true);
  1603. X            break;
  1604. X        } else {
  1605. X            changed = true;
  1606. X            p = p->next;
  1607. X        }
  1608. X    }
  1609. X    if(changed) {
  1610. X        do {
  1611. X            if(!p->valid) {
  1612. X                p->valid = true;
  1613. X                addstatus("reading mail headers ...", true);
  1614. X                find_mail(p, false);
  1615. X            }
  1616. X            if(p->valid == EMPTY)
  1617. X                p = p->next;
  1618. X        } while(p != (folder) NULL && p->valid != true);
  1619. X        if(p == (folder) NULL)
  1620. X            addstatus("can't go forward that many folders", true);
  1621. X        else {
  1622. X            alternate = curflr;
  1623. X            curflr = p;
  1624. X            curmail = p->mail;
  1625. X            y = FIRST;
  1626. X            display_page();
  1627. X        }
  1628. X    }
  1629. X}
  1630. X
  1631. X
  1632. X/* --------------------
  1633. X    Goto previous folder, make active if not so already.
  1634. X-------------------- */
  1635. Xvoid
  1636. Xgoto_prev_folder(count)
  1637. X    int        count;
  1638. X{
  1639. X    bool    changed = false;
  1640. X    folder    f, p = curflr;
  1641. X
  1642. X    for( ; count > 0 ; count--) {
  1643. X            /* skip other pages of current folder */
  1644. X        for(f=p ; p->prev->name == f->name ; p=p->prev)
  1645. X            ;
  1646. X        if(curflr->prev == (folder) NULL) {
  1647. X            addstatus("first folder", true);
  1648. X            break;
  1649. X        } else {
  1650. X            changed = true;
  1651. X            p = p->prev;
  1652. X        }
  1653. X    }
  1654. X    if(changed) {
  1655. X        do {
  1656. X            if(!p->valid) {
  1657. X                p->valid = true;
  1658. X                addstatus("reading mail headers ...", true);
  1659. X                find_mail(p, false);
  1660. X            }
  1661. X            if(p->valid == EMPTY)
  1662. X                p = p->prev;
  1663. X        } while(p != (folder) NULL && p->valid != true);
  1664. X        if(p == (folder) NULL)
  1665. X            addstatus("can't go back that many folders", true);
  1666. X        else {
  1667. X                /* skip other pages of current folder */
  1668. X            for(f=p ; p->prev->name == f->name ; p=p->prev)
  1669. X                ;
  1670. X            alternate = curflr;
  1671. X            curflr = p;
  1672. X            curmail = p->mail;
  1673. X            y = FIRST;
  1674. X            display_page();
  1675. X        }
  1676. X    }
  1677. X}
  1678. X
  1679. X
  1680. X/* --------------------
  1681. X    Make new folder current as specified - read from terminal (get_name =
  1682. X    true) or use value in alternate (get_name = false).
  1683. X-------------------- */
  1684. Xvoid
  1685. Xgoto_folder(get_name)
  1686. X    bool    get_name;
  1687. X{
  1688. X    char    buf[LEN], *str = buf;
  1689. X    folder    f;
  1690. X    int        i;
  1691. X
  1692. X    if(get_name || alternate == (folder) NULL) {
  1693. X            /* no alternate folder */
  1694. X        get_string("folder? ", str);
  1695. X        for(i=0 ; *str == ' ' ; i++, str++)
  1696. X            ;
  1697. X        for(; i < LEN && buf[i] != ' ' && buf[i] != '\0' ; i++)
  1698. X            ;
  1699. X        if(i < LEN)
  1700. X            buf[i] = '\0';
  1701. X        GOTO_NAME(f, str);
  1702. X        if(f == (folder) NULL) {
  1703. X            addstatus("no such folder", true);
  1704. X            return;
  1705. X        }
  1706. X        if(!f->valid) {
  1707. X            f->valid = true;
  1708. X            addstatus("reading mail headers ...", true);
  1709. X            find_mail(f, false);
  1710. X            if(f->valid == EMPTY) {
  1711. X                sprintf(str, "%s -- folder empty", f->name);
  1712. X                addstatus(str, true);
  1713. X                return;
  1714. X            }
  1715. X        }
  1716. X    } else
  1717. X        f = alternate;
  1718. X    if(f == curflr)
  1719. X        addstatus("already in that folder", true);
  1720. X    else {
  1721. X        alternate = curflr;
  1722. X        curflr = f;
  1723. X        curmail = f->mail;
  1724. X        y = FIRST;
  1725. X        display_page();
  1726. X    }
  1727. X}
  1728. X
  1729. X
  1730. X/* --------------------
  1731. X    Go to first line of page, as offset by count.
  1732. X-------------------- */
  1733. Xvoid
  1734. Xcursor_first(count)
  1735. X    int        count;
  1736. X{
  1737. X    item    m = curflr->mail;
  1738. X
  1739. X    addstatus("", false);                /* clear status line */
  1740. X    for(y=FIRST ; m != (item) NULL && count > 1 ; count--, y++, m=m->next)
  1741. X        ;
  1742. X    if(m == (item) NULL)
  1743. X        beep();
  1744. X    else {
  1745. X        curmail = m;
  1746. X        move(y, 0);
  1747. X        refresh();
  1748. X    }
  1749. X}
  1750. X
  1751. X
  1752. X/* --------------------
  1753. X    Go to middle line of page.
  1754. X-------------------- */
  1755. Xvoid
  1756. Xcursor_middle()
  1757. X{
  1758. X    item    m, p;
  1759. X    bool    tog = false;
  1760. X
  1761. X    addstatus("", false);                /* clear status line */
  1762. X        /* find middle of page - p traverses page at half speed */
  1763. X    for(y=FIRST, m=p=curflr->mail ; m != (item) NULL ; m=m->next, tog= !tog)
  1764. X        if(tog) {
  1765. X            y++;
  1766. X            p = p->next;
  1767. X        }
  1768. X    curmail = p;
  1769. X    move(y, 0);
  1770. X    refresh();
  1771. X}
  1772. X
  1773. X
  1774. X/* --------------------
  1775. X    Go to last line of page, as offset by count.
  1776. X-------------------- */
  1777. Xvoid
  1778. Xcursor_last(count)
  1779. X    int        count;
  1780. X{
  1781. X    item    m = curflr->mail;
  1782. X
  1783. X    addstatus("", false);                /* clear status line */
  1784. X        /* find last item, including y-value */
  1785. X    for(y=FIRST ; m->next != (item) NULL ; y++, m=m->next)
  1786. X        ;
  1787. X    for(; m != (item) NULL && count > 1 ; count--, y--, m=m->prev)
  1788. X        ;
  1789. X    if(m == (item) NULL)
  1790. X        beep();
  1791. X    else {
  1792. X        curmail = m;
  1793. X        move(y, 0);
  1794. X        refresh();
  1795. X    }
  1796. X}
  1797. X
  1798. X
  1799. X/* --------------------
  1800. X    Go to first page.  ***should use count as offset*** 
  1801. X-------------------- */
  1802. Xvoid
  1803. Xgoto_first_page()
  1804. X{
  1805. X    folder    f;
  1806. X
  1807. X    f = folders; NEXT_VALID(f);
  1808. X    if(f == (folder) NULL) {
  1809. X        /* no valid folders - bleah?! */
  1810. X        addstatus("no valid folders, exiting", true);
  1811. X        to_normal();
  1812. X        exit(1);
  1813. X    } else if(f != curflr) {
  1814. X        alternate = curflr;
  1815. X        curflr = f;
  1816. X        curmail = f->mail;
  1817. X        y = FIRST;
  1818. X        display_page();
  1819. X    }
  1820. X}
  1821. X
  1822. X
  1823. X/* --------------------
  1824. X    Go to last page.  ***should use count as offset***
  1825. X-------------------- */
  1826. Xvoid
  1827. Xgoto_last_page()
  1828. X{
  1829. X    folder    f, p;
  1830. X
  1831. X    for(f=folders ; f != (folder) NULL ; f=f->next)
  1832. X        if(f->valid)
  1833. X            p = f;
  1834. X    if(p == (folder) NULL) {
  1835. X        /* no valid folders - bleah?! */
  1836. X        addstatus("no valid folders, exiting", true);
  1837. X        to_normal();
  1838. X        exit(1);
  1839. X    } else if(p != curflr) {
  1840. X        alternate = curflr;
  1841. X        curflr = p;
  1842. X        curmail = p->mail;
  1843. X        y = FIRST;
  1844. X        display_page();
  1845. X    }
  1846. X}
  1847. !The!End!
  1848. exit
  1849.  
  1850.  
  1851.