jz@mulga.oz.au (Justin Zobel) (10/14/87)
Submitted by: jz@mulga.oz.au Posting-number: Volume 12, Issue 5 Archive-name: vmail/Part02 This is the second of three parts of vmail, an interactive mail handler that sits on top of MH. vmail has a number of advantages over raw MH. It is screen-based and faster (and more convenient) than the MH show-scan-rmm refile cycle. vmail makes it feasible for users to organise and keep track of moderate volumes of mail without wasting too much time, and is very simple to use. It has been in use at Melbourne University Computer Science Department for about six months without any problems, and has become the interface of choice for many users. : ---------------------------------------- cut here echo x - "call.c" 2>&1 sed "s/^X//" >"call.c" <<'!The!End!' X/* -------------------- X vmail -- call.c X X Routines that call MH equivalents, editor, shell. X X Copyright (C) J. Zobel, University of Melbourne, October 1987. X-------------------- */ X X#include "defs.h" X#include <signal.h> X X#define WARNING "Warning -- mail headers may be out of date" X Xunion wait status; X X/* -------------------- X Fork a call to `comp'. X Terminal type must be reset before call. X-------------------- */ Xvoid Xcomp() X{ X char *tmp, *argv[20], str[LEN], s1[LEN], *next_token(); X int i, (*oldint)(), (*oldquit)(), (*signal())(); X X *s1 = '\0'; X if(comp_args) { X sprintf(str, "(give options to) comp "); X get_string(str, s1); X } X clear(); X addstatus("composing mail ...", false); X move(STATUS+1, 0); X refresh(); X top_level = false; /* used by tstp() so that right thing is done X when process is restarted */ X if(! vfork()) { X argv[0] = COMP; X for(i=1, tmp=s1 ; *tmp != '\0' ; i++) { X argv[i] = tmp; X tmp = next_token(tmp); X } X argv[i] = 0; X no_control(); X execv(COMP, argv); X printf("Warning: can't execute %s\n", COMP); X exit(0); X } X oldint = signal(SIGINT, SIG_IGN); X oldquit = signal(SIGQUIT, SIG_IGN); X wait(&status); X signal(SIGINT, oldint); X signal(SIGQUIT, oldquit); X top_level = true; X to_control(); X hold_end(); /* wait for user to want to continue - may wish X to read error messages */ X display_page(); X addstatus(WARNING, true); /* vmail's data structures not updated */ X} X X X/* -------------------- X Fork a call to `forw'. X Terminal type must be reset before call. X-------------------- */ Xvoid Xforw() X{ X char *tmp, *argv[20], str[LEN], s1[LEN], s2[10], *next_token(); X int i, (*oldint)(), (*oldquit)(), (*signal())(); X X sprintf(s2, "%d", curmail->number); X *s1 = '\0'; X if(forw_args) { X sprintf(str, "(give options to) forw +%s %s ", curflr->name, s2); X get_string(str, s1); X } X clear(); X addstatus("forwarding mail ...", false); X move(STATUS+1, 0); X refresh(); X top_level = false; /* used by tstp() so that right thing is done X when process is restarted */ X if(! vfork()) { X sprintf(str, "+%s", curflr->name); X argv[0] = FORW; argv[1] = str; argv[2] = s2; X for(i=3, tmp=s1 ; *tmp != '\0' ; i++) { X argv[i] = tmp; X tmp = next_token(tmp); X } X argv[i] = 0; X no_control(); X execv(FORW, argv); X printf("Warning: can't execute %s\n", FORW); X exit(0); X } X oldint = signal(SIGINT, SIG_IGN); X oldquit = signal(SIGQUIT, SIG_IGN); X wait(&status); X signal(SIGINT, oldint); X signal(SIGQUIT, oldquit); X top_level = true; X to_control(); X hold_end(); /* wait for user to want to continue - may wish X to read error messages */ X display_page(); X addstatus(WARNING, true); /* vmail's data structures not updated */ X} X X X/* -------------------- X Fork a call to `repl'. X Terminal type must be reset before call. X-------------------- */ Xvoid Xrepl() X{ X char *tmp, *argv[20], str[LEN], s1[LEN], s2[10], *next_token(); X int i, (*oldint)(), (*oldquit)(), (*signal())(); X X sprintf(s2, "%d", curmail->number); X *s1 = '\0'; X if(repl_args) { X sprintf(str, "(give options to) repl +%s %s ", curflr->name, s2); X get_string(str, s1); X } X clear(); X addstatus("answering mail ...", false); X move(STATUS+1, 0); X refresh(); X top_level = false; /* used by tstp() so that right thing is done X when process is restarted */ X if(! vfork()) { X sprintf(str, "+%s", curflr->name); X argv[0] = REPL; argv[1] = str; argv[2] = s2; X for(i=3, tmp=s1 ; *tmp != '\0' ; i++) { X argv[i] = tmp; X tmp = next_token(tmp); X } X argv[i] = 0; X no_control(); X execv(REPL, argv); X printf("Warning: can't execute %s\n", REPL); X exit(0); X } X oldint = signal(SIGINT, SIG_IGN); X oldquit = signal(SIGQUIT, SIG_IGN); X wait(&status); X signal(SIGINT, oldint); X signal(SIGQUIT, oldquit); X top_level = true; X to_control(); X hold_end(); /* wait for user to want to continue - may wish X to read error messages */ X display_page(); X addstatus(WARNING, true); /* vmail's data structures not updated */ X} X X X/* -------------------- X Fork a call to editor. X Terminal type must be reset before call. X-------------------- */ Xvoid Xedit() X{ X char str[LEN]; X int (*oldint)(), (*oldquit)(), (*signal())(); X X clear(); X mvaddstr(TITLE, 0, "editing mail ..."); X move(STATUS, 0); X refresh(); X top_level = false; /* used by tstp() so that right thing is done X when process is restarted */ X if(! vfork()) { X no_control(); X sprintf(str, "%s/%s/%d", mail_dir, curflr->name, curmail->number); X execlp(editor, editor, str, 0); X printf("Warning: can't execute %s\n", editor); X exit(0); X } X oldint = signal(SIGINT, SIG_IGN); X oldquit = signal(SIGQUIT, SIG_IGN); X wait(&status); X signal(SIGINT, oldint); X signal(SIGQUIT, oldquit); X top_level = true; X to_control(); X hold_end(); /* wait for user to want to continue - may wish X to read error messages */ X display_page(); X} X X X/* -------------------- X Fork a call to shell. X Terminal type must be reset before call. X X This should perhaps be modified so that only a single command can be X issued, as in vi ... but this was simpler to do. X-------------------- */ Xvoid Xcall_shell() X{ X int (*oldint)(), (*oldquit)(), (*signal())(); X X clear(); X mvaddstr(TITLE, 0, "calling shell ..."); X move(STATUS, 0); X refresh(); X top_level = false; /* used by tstp() so that right thing is done X when process is restarted */ X if(! vfork()) { X no_control(); X fix_mh(); X execlp(shell, shell, "-i", 0); X printf("Warning: can't execute %s\n", shell); X exit(0); X } X oldint = signal(SIGINT, SIG_IGN); X oldquit = signal(SIGQUIT, SIG_IGN); X wait(&status); X signal(SIGINT, oldint); X signal(SIGQUIT, oldquit); X top_level = true; X to_control(); X hold_end(); /* wait for user to want to continue - may wish X to read error messages */ X display_page(); X} X X X/* -------------------- X Pipe current mail item into given command. X-------------------- */ Xvoid Xdo_pipe() X{ X char str[LEN], s1[LEN]; X int (*oldint)(), (*oldquit)(), (*signal())(); X X *s1 = '\0'; X sprintf(str, "(give command to) show +%s %d | ", curflr->name, X curmail->number); X get_string(str, s1); X clear(); X addstatus("piping mail ...", false); X move(STATUS+1, 0); X refresh(); X sprintf(str, "%s %s/%s/%d | %s", CAT, mail_dir, curflr->name, X curmail->number, s1); X top_level = false; /* used by tstp() so that right thing is done X when process is restarted */ X no_control(); X oldint = signal(SIGINT, SIG_IGN); X oldquit = signal(SIGQUIT, SIG_IGN); X system(str); /* exec needs full path of command => use system */ X signal(SIGINT, oldint); X signal(SIGQUIT, oldquit); X top_level = true; X to_control(); X hold_end(); /* wait for user to want to continue - may wish X to read error messages */ X display_page(); X} !The!End! echo x - "choose.c" 2>&1 sed "s/^X//" >"choose.c" <<'!The!End!' X/* -------------------- X vmail -- choose.c X X Tty-based folder selection. X X Copyright (C) J. Zobel, University of Melbourne, October 1987. X-------------------- */ X X#include "defs.h" X X#define START 10 /* offset from margin of screen */ X#define RESET() toggle=false, /* left- or right- hand indicator */ \ X i=FIRST+1, /* initial row */ \ X j=START, /* initial column */ \ X f=first /* first folder */ X XWINDOW *chooser = (WINDOW *) NULL; X X/* -------------------- X Display folder selection page, read commands to move around page and X select folders. X-------------------- */ Xvoid Xchoose() X{ X int n, i, j; X bool toggle; X folder next, f, showing(), X first = folders, /* first folder on screen */ X last; /* folder after last folder on screen */ X char c, str[LEN], flushin(); X X RESET(); X if(chooser == (WINDOW *) NULL) X chooser = newwin(0, 0, 0, 0); X last = showing(first); Xrestart: X while((c = flushin()) != ' ' && c != 'q') switch(c) { X case '\r': /* go to next screen of folder names */ X case '\n': X if(last == (folder) NULL) X beep(); X else { X first = last; X last = showing(first); X RESET(); X } X break; X case '\b': /* go back one page of folders */ X if(first == folders) X beep(); X else { X for(i=(lines-FIRST)*2 ; i > FIRST && first->prev != (folder) NULL ; i--, first=first->prev) X ; X last = showing(first); X RESET(); X } X break; X case CTRL_L: /* redraw */ X last = showing(first); X break; X case 'h': /* move left */ X if(! toggle) /* already at left */ X beep(); X else { X next = f; X FRST_OF_NAME(next); X next = next->prev; X if(next == (folder) NULL) X beep(); X else { X f = next; j = START; toggle = false; X wmove(chooser, i, j); X wrefresh(chooser); X } X } X break; X case 'j': /* move down */ X if(i+1 >= lines) /* at bottom */ X beep(); X else { X next = f; X for(n=0 ; n < 2 && next != (folder) NULL ; n++) { X LAST_OF_NAME(next); X next = next->next; X } X if(next == (folder) NULL) X beep(); X else { X i++; f = next; X wmove(chooser, i, j); X wrefresh(chooser); X } X } X break; X case 'k': /* move up */ X if(i <= FIRST+1) /* at top */ X beep(); X else { X next = f; X for(n=0 ; n < 2 && next != (folder) NULL ; n++) { X FRST_OF_NAME(next); X next = next->prev; X } X if(next == (folder) NULL) X beep(); X else { X i--; f = next; X wmove(chooser, i, j); X wrefresh(chooser); X } X } X break; X case 'l': /* move right */ X if(toggle) /* at right */ X beep(); X else { X next = f; X LAST_OF_NAME(next); X next = next->next; X if(next == (folder) NULL) X beep(); X else { X f = next; j = cols/2; toggle = true; X wmove(chooser, i, j); X wrefresh(chooser); X } X } X break; X default: X beep(); X break; X } X if(c == ' ') { /* attempt to go to folder */ X if(! f->valid) { /* load folder */ X mvwaddstr(chooser, FIRST, 0, "Reading mail headers ..."); X wmove(chooser, i, j); X wrefresh(chooser); X f->valid = true; X find_mail(f, false); X } X if(f->valid == EMPTY) { /* folder is empty */ X /* Go back to chooser, display "empty" message */ X sprintf(str, "%s -- folder empty", f->name); X if(first == f) /* find new first folder for page */ X if(f->next != (folder) NULL) X first = f->next; X else X first = folders; X last = showing(first); X RESET(); X mvwaddstr(chooser, FIRST, 0, str); X wmove(chooser, i, j); X wrefresh(chooser); X goto restart; X } X curflr = f; X curmail = f->mail; X y = FIRST; X } X display_page(); X} X X X/* -------------------- X Show all folder names starting with "start" ending with last folder or X when page is full. X-------------------- */ Xfolder Xshowing(start) X folder start; X{ X int count, i = FIRST+1, j = START; X bool toggle = false; X folder p, f; X item m; X char str[LEN]; X X wclear(chooser); X mvwaddstr(chooser, TITLE, 0, X"h,j,k,l -- left,down,up,right <return>,<bs> -- next/prev page of folders"); X mvwaddstr(chooser, STATUS, 0, X"<space> -- select folder q -- quit, no change ^L -- refresh"); X for(p=f=start ; i < lines && f != (folder) NULL ; p=f=p->next) { X if(f->valid) { X /* find last page of folder, count items in folders */ X for(count=0, p=f ; p->next != (folder) NULL && X p->next->name == f->name ; p=p->next) X for(m=p->mail ; m != (item) NULL ; m=m->next, count++) X ; X for(m=p->mail ; m != (item) NULL ; m=m->next, count++) X ; X if(count == 1) X sprintf(str, "%s: %d, 1 item", f->name, f->mail->number); X else X sprintf(str, "%s: %d-%d, %d items", f->name, f->mail->number, X p->last->number, count); X } else X sprintf(str, "%-15s(inactive)", f->name); X if(f == curflr) standout(); X mvwaddstr(chooser, i, j, str); X if(f == curflr) standend(); X if(toggle) X j = START, i++; X else X j = cols/2; X toggle = ! toggle; X } X wmove(chooser, FIRST+1, START); X wrefresh(chooser); X return(f); X} X X XWINDOW *folwin = (WINDOW *) NULL; X X/* -------------------- X List all folders, active or otherwise. X-------------------- */ Xvoid Xlist_folders() X{ X folder f = folders, p; X int count, i = FIRST, half = cols/2; X bool toggle = false; X char str[LEN]; X WINDOW *newwin(); X item m; X X if(folwin == (WINDOW *) NULL) X folwin = newwin(0, 0, 0, 0); X wclear(folwin); X for(p=f ; ; p=f=p->next) { X if((i+2) % lines == 0) { X if(use_prompt(folwin) == 'q') X break; X i = FIRST; X wclear(folwin); X } X if(f == (folder) NULL) { X use_prompt(folwin); X break; X } X if(f->valid) { X /* find last page of folder, count items in folders */ X for(count=0, p=f ; p->next != (folder) NULL && X p->next->name == f->name ; p=p->next) X for(m=p->mail ; m != (item) NULL ; m=m->next, count++) X ; X for(m=p->mail ; m != (item) NULL ; m=m->next, count++) X ; X if(count == 1) X sprintf(str, "%s: %d, 1 item", f->name, f->mail->number); X else X sprintf(str, "%s: %d-%d, %d items", f->name, f->mail->number, X p->last->number, count); X } else X sprintf(str, "%-15s(inactive)", f->name); X if(f == curflr) X wstandout(folwin); X if(toggle) { X mvwaddstr(folwin, i, half, str); X i++; X } else X mvwaddstr(folwin, i, 10, str); X if(f == curflr) X wstandend(folwin); X toggle = !toggle; X } X display_page(); X} !The!End! echo x - "cmds.c" 2>&1 sed "s/^X//" >"cmds.c" <<'!The!End!' X/* -------------------- X vmail -- cmds.c X X Commands not included in page.c or move.c. X X Copyright (C) J. Zobel, University of Melbourne, October 1987. X-------------------- */ X X#include "defs.h" X#include <signal.h> X X/* -------------------- X Print the name of the alternate folder. X-------------------- */ Xvoid Xshow_folder() X{ X char str[LEN]; X X if(alternate == (folder) NULL) X addstatus("No alternate folder", false); X else { X sprintf(str, "alternate folder is %s\n", alternate->name); X addstatus(str, false); X } X} X X X/* -------------------- X Make current folder inactive - remove list of mail headers, set valid X to false. X-------------------- */ Xvoid Xinactive() X{ X folder f, p; X X /* find first page of folder */ X p = curflr; FRST_OF_NAME(p); X /* find last page of folder */ X f = curflr; LAST_OF_NAME(f); X p->next = f->next; X p->valid = false; X/* should free records */ X p->mail = p->last = (item) NULL; X p->pagenum = p->pages = 1; X if(p->next != (folder) NULL) X p->next->prev = p; X curflr = p; X NEXT_VALID(curflr); X if(curflr == (folder) NULL) { X curflr = p; X PREV_VALID(curflr); X } X if(curflr == (folder) NULL) { X addstatus("Making last active folder inactive", true); X to_normal(); X exit(0); X } else { X curmail = curflr->mail; X y = FIRST; X display_page(); X } X} X X X/* -------------------- X Show current mail item with fork to pager. X-------------------- */ Xvoid Xshow_mail() X{ X int (*oldint)(), (*oldquit)(), (*signal())(); X char str[LEN]; X union wait status; X X clear(); X refresh(); X top_level = false; /* used by tstp() so that right thing is done X when process is restarted */ X if(! vfork()) { X no_control(); X sprintf(str, "%s/%s/%d", mail_dir, curflr->name, curmail->number); X execlp(pager, pager, str, 0); X printf("Warning: can't execute %s\n", pager); X exit(0); X } X oldint = signal(SIGINT, SIG_IGN); X oldquit = signal(SIGQUIT, SIG_IGN); X wait(&status); X signal(SIGINT, oldint); X signal(SIGQUIT, oldquit); X top_level = true; X to_control(); X hold_end(); /* wait for user to want to continue - may wish X to read error messages */ X display_page(); X} X X X/* -------------------- X Save current mail item to named file. If first non-space character X is ~, expand it. X-------------------- */ Xvoid Xsave_item() X{ X struct passwd *pwent, *getpwnam(); X char *tmp, buf[LEN], *str = buf, save[LEN]; X FILE *fi, *fo, *fopen(); X X sprintf(save, "%s.%d", curflr->name, curmail->number); X sprintf(str, "file (%s)? ", save); X get_string(str, str); X for(; *str == ' ' ; str++) X ; X for(tmp=str ; *tmp != '\0' && *tmp != ' ' ; tmp++) X ; X *tmp = '\0'; X if(*str != '\0') /* don't take default */ X if(*str == '~') { /* expand home directory name */ X tmp = str + 2; X if(*(str+1) == '/') X pwent = getpwnam(user); X else { X for( ; *tmp != '\0' && *tmp != '/' ; tmp++) X ; X *(tmp++) = '\0'; X if((pwent = getpwnam(str+1)) == (struct passwd *) NULL) { X sprintf(save, "%s: no such user", str+1); X addstatus(save, true); X return; X } X } X sprintf(save, "%s/%s", pwent->pw_dir, tmp); X } else /* take str as given */ X strcpy(save, str); X if((fo = fopen(save, "w")) == (FILE *) NULL) { X sprintf(str, "%s: no write permission", save); X addstatus(str, true); X return; X } X addstatus("saving ...", false); X sprintf(str, "%s/%s/%d", mail_dir, curflr->name, curmail->number); X fi = fopen(str, "r"); X while(fgets(str, LEN, fi) != (char *) NULL) X fprintf(fo, "%s", str); X fclose(fi); fclose(fo); X addstatus("saved", false); X} !The!End! echo x - "find.c" 2>&1 sed "s/^X//" >"find.c" <<'!The!End!' X/* -------------------- X vmail -- find.c X X Searching titles for strings. X X Copyright (C) J. Zobel, University of Melbourne, October 1987. X-------------------- */ X X#include "defs.h" X Xstatic char str[LEN] = "^.*"; X X/* -------------------- X Get string for searching, either forwards (forwards = true) or backwards X (forwards = false). Then scan mail headers using regex(). X-------------------- */ Xvoid Xsearch(forwards) X int forwards; X{ X char s1[LEN], *s2, *re_comp(); X int i; X bool found = false; X folder f; X item m; X X get_string((forwards) ? "/" : "?", s1); X if((i = strlen(s1)) > 0) { X /* new search string */ X strcpy(str+3, s1); X str[i+=3] = '.', str[++i] = '*', str[++i] = '\0'; X } else if(strlen(str+3) == 0) { X addstatus("", false); X return; X } X if((s2 = re_comp(str)) != (char *) NULL) { X addstatus(s2, false); X return; X } X addstatus("searching ...", false); X if(forwards) X for(i=y+1, f=curflr, m=curmail->next ; ! found && m != curmail ; ) { X if(m == (item) NULL) { X f = f->next; NEXT_VALID(f); X if(f == (folder) NULL) { X f = folders; X NEXT_VALID(f); X } X m = f->mail; X i = FIRST; X } X if(! (found = re_exec(m->title))) X m = m->next, i++; X } X else X for(i=y-1, f=curflr, m=curmail->prev ; ! found && m != curmail ; ) { X if(m == (item) NULL) { X f = f->prev; PREV_VALID(f); X if(f == (folder) NULL) { X /* find last folder */ X for(f=folders ; f->next != (folder) NULL ; f=f->next) X ; X PREV_VALID(f); X } X /* find last mail item with count */ X for(i=FIRST, m=f->mail ; m->next != (item) NULL ; i++,m=m->next) X ; X } X if(! (found = re_exec(m->title))) X m = m->prev, i--; X } X curmail = m, y = i; X if(f != curflr) { X curflr = f; X display_page(); X } else if(! found) /* come back to current folder (known to be valid) */ X addstatus("pattern not found", true); X else X addstatus("", false); X} !The!End! echo x - "inc.c" 2>&1 sed "s/^X//" >"inc.c" <<'!The!End!' X/* -------------------- X vmail -- inc.c X X Incorporate new mail with "inc", parse output and update appropriate X folder-pages. X X Copyright (C) J. Zobel, University of Melbourne, October 1987. X-------------------- */ X X#include "defs.h" X X#define INCORP "Incorporating new mail into " X#define TAIL "...\n" X Xstatic folder isfol; /* folder into which mail is being incorporated */ Xstatic item ismail; /* last item of mail on isfol page */ Xstatic int count; /* number of items on isfol page */ X X/* -------------------- X Call, inc, parse output to work out what items have been read and into X what folders. X-------------------- */ Xvoid Xinc() X{ X char str[LEN], line[LEN], *tmp, *fgets(); X FILE *pp, *fdopen(); X int num, pd[2], len = strlen(INCORP), numbers(); X bool redraw = true; X union wait status; X X addstatus("incorporating mail ... ", true); X pipe(pd); X if(! vfork()) { X close(pd[0]); X dup2(pd[1], 1); X close(pd[1]); X dup2(1, 2); X execl(INC, "inc", 0); X } X close(pd[1]); X if((pp = fdopen(pd[0], "r")) == (FILE *) NULL) { X addstatus("panic, can't incorporate mail", true); X return; X } X while((tmp = fgets(str, LEN, pp)) != (char *) NULL) /* read "inc" output */ X if(strncmp(tmp, INCORP, len) == 0) { /* reading into new folder */ X tmp += len; X tmp[strlen(tmp)-strlen(TAIL)] = '\0'; X goto_incorp_folder(tmp); X if(isfol == (folder) NULL) { X sprintf(line, "can't go to folder %s", tmp); X addstatus(line, true); X redraw = false; X break; X } else { X sprintf(line, "incorporating mail into folder %s", tmp); X addstatus(line, true); X } X } else if(num = numbers(tmp)) /* continuing to read into folder */ X create_mail_record(num); X else if(strlen(str) > 2) { /* vmail is confused - break */ X str[strlen(str)-1] = '\0'; X addstatus(str, true); X redraw = false; X break; X } X if(redraw) { X if(isfol != curflr) X alternate = curflr; X curflr = isfol; X curmail = ismail; X y = count; X display_page(); X } X fclose(pp); X wait(&status); X} X X X/* -------------------- X Extract the first number from str. X-------------------- */ Xint Xnumbers(str) X char *str; X{ X char save, *tmp; X int i; X X for(; *str == ' ' ; str++) X ; X for(tmp=str ; *str >= '0' && *str <= '9' ; str++) X ; X save = *str, *str = '\0', i = atoi(tmp), *str = save; X return(i); X} X X X/* -------------------- X Find the folder into which mail is being incorporated, set isfol, X ismail, count. X-------------------- */ Xvoid Xgoto_incorp_folder(str) X char *str; X{ X folder create_folder(); X X GOTO_NAME(isfol, str); X if(isfol == (folder) NULL) { /* new folder */ X isfol = create_folder(str); X isfol->valid = true; X ismail = (item) NULL; X count = FIRST-1; X } else { X if(! isfol->valid) { X isfol->valid = true; X find_mail(isfol, false); X } X /* find last appropriate folder record */ X LAST_OF_NAME(isfol); X for(count=FIRST, ismail=isfol->mail ; ismail->next != (item) NULL ; X count++, ismail=ismail->next) X ; X } X} X X X/* -------------------- X Create and insert new item record into isfol, may need to create new page. X-------------------- */ Xvoid Xcreate_mail_record(num) X int num; X{ X item m; X folder new_folder(); X X m = NEW(mail_item); X get_title(isfol, m, num); X m->next = (item) NULL; X if(count > lines) { X /* create new folder record */ X isfol = new_folder(isfol); X ismail = (item) NULL; X count = FIRST; X } else X count++; X if(ismail == (item) NULL) { X /* new record, possibly new folder */ X m->prev = (item) NULL; X isfol->mail = isfol->last = m; X } else { X /* insert at end of list of mail items */ X m->prev = ismail; X ismail->next = isfol->last = m; X } X ismail = m; X} !The!End! echo x - "init.c" 2>&1 sed "s/^X//" >"init.c" <<'!The!End!' X/* -------------------- X vmail -- init.c X X Initialisation routines - setting ttystate, finding valid folders, X trapping signals. X X Ttystate is controlled by a mix of curses and ioctl. For simplicity, X initial setups are done with curses. Curses is also used for basic X screen manipulation. However, for speed ioctl is used in switching X in and out of normal terminal state. X X Copyright (C) J. Zobel, University of Melbourne, October 1987. X-------------------- */ X X#include "defs.h" X#include <signal.h> X Xstatic struct sgttyb tty, t_tty; /* for holding tty state */ Xstatic struct tchars chrs, t_chrs; Xstatic struct ltchars lchrs, t_lchrs; X Xstatic char termcap[1024], /* termcap entry */ X *cur_folder; /* initial current folder */ X X/* -------------------- X Start-up routine - set terminal control, signals, etc. X-------------------- */ Xvoid Xinit(argc, argv) X int argc; X char **argv; X{ X folder ftmp, find_mail(); X char *pargv[20], /* argv from profile */ X *profile = (char *) NULL, /* location of MH profile */ X *home = (char *) NULL, /* home directory */ X *term = (char *) NULL; /* terminal type */ X int pargc = 0; /* argc from profile */ X X get_home(&home); X get_env(&term, &profile, home); X tgetent(termcap, term); X cols = tgetnum("co"); X /* lines holds no. of lines for headers, ie "li" less STATUS, TITLE */ X lines = tgetnum("li") - 2; X ioctl(0, TIOCGETP, &tty); X ioctl(0, TIOCGETP, &chrs); X ioctl(0, TIOCGETP, &lchrs); X X read_profile(&pargc, pargv, profile, home); X process_args(pargc, pargv); X find_folders(); X mark_valid_folders(pargc, pargv); X /* give precedence to command line args => process second */ X mark_valid_folders(argc, argv); X for(ftmp=folders ; ftmp != (folder) NULL ;) X if(ftmp->valid) X ftmp = find_mail(ftmp, true); X else X ftmp = ftmp->next; X if(curflr->valid == EMPTY) { X printf("%s: folder empty.\n", curflr->name); X exit(1); X } X /* find last instance of initial folder */ X LAST_OF_NAME(curflr); X curmail = curflr->mail; X X initscr(); X crmode(); X noecho(); X nonl(); X X signal(SIGTSTP, tstp); X signal(SIGINT, tint); X X ioctl(0, TIOCGETP, &t_tty); X ioctl(0, TIOCGETP, &t_chrs); X ioctl(0, TIOCGETP, &t_lchrs); X X y = FIRST; X display_page(); X} X X X/* -------------------- X Find user name, home directory. X-------------------- */ Xvoid Xget_home(home) X char **home; X{ X struct passwd *pwent, *getpwuid(); X X pwent = getpwuid(getuid()); X *home = NEWSTR(strlen(pwent->pw_dir)+1); X strcpy(*home, pwent->pw_dir); X if(access(*home, R_OK | W_OK | X_OK)) { X printf("%s: no permissions.\n", *home); X exit(1); X } X user = NEWSTR(strlen(pwent->pw_name)+1); X strcpy(user, pwent->pw_name); X} X X X/* -------------------- X Find pager, editor, shell, terminal type, MH profile - defaults are PAGER, X EDITOR, SHELL, none, PROFILE. Set by PAGER, EDITOR, SHELL, TERM, MH X environment variables. X-------------------- */ Xvoid Xget_env(term, profile, home) X char **term, **profile, *home; X{ X char **tmp; X X for(tmp = environ ; *tmp != (char *) NULL ; tmp++) X if(!strncmp("PAGER=", *tmp, 6)) { X pager = NEWSTR(strlen(*tmp)-4); X strcpy(pager, *tmp+6); X } else if(!strncmp("EDITOR=", *tmp, 7)) { X editor = NEWSTR(strlen(*tmp)-5); X strcpy(editor, *tmp+7); X } else if(!strncmp("SHELL=", *tmp, 6)) { X shell = NEWSTR(strlen(*tmp)-4); X strcpy(shell, *tmp+6); X } else if(!strncmp("TERM=", *tmp, 5)) { X *term = NEWSTR(strlen(*tmp)-3); X strcpy(*term, *tmp+5); X } else if(!strncmp("MH=", *tmp, 3)) { X *profile = NEWSTR(strlen(*tmp)-1); X strcpy(*profile, *tmp+3); X } X if(*term == (char *) NULL) { X printf("Terminal type unknown\n"); X exit(1); X } X if(*profile == (char *) NULL) { X *profile = NEWSTR(strlen(home)+strlen(PROFILE)+2); X sprintf(*profile, "%s/%s", home, PROFILE); X } X if(pager == (char *) NULL) { X pager = NEWSTR(strlen(PAGER)+1); X strcpy(pager, PAGER); X } X if(shell == (char *) NULL) { X shell = NEWSTR(strlen(SHELL)+1); X strcpy(shell, SHELL); X } X if(editor == (char *) NULL) { X editor = NEWSTR(strlen(EDITOR)+1); X strcpy(editor, EDITOR); X } X} X X Xstatic char argkeep[LEN]; /* storage for args from profile */ X X/* -------------------- X Find mail directory, current-folder, context, default options. X-------------------- */ Xvoid Xread_profile(pargc, pargv, profile, home) X int *pargc; X char **pargv, *profile, *home; X{ X FILE *fp, *fopen(); X char str[LEN], *ptr, iscontext[LEN], *index(), *next_token(); X X if((fp = fopen(profile, "r")) == (FILE *) NULL) { X printf("Profile: %s: cannot open.\n", profile); X exit(1); X } X *iscontext = '\0'; X while(fgets(str, LEN, fp) != (char *) NULL) { X /* get entries from profile */ X if(lstrncmp("context:", str, 8) == 0 && *(ptr=str+8) != '\0') { X squash(str); X strcpy(iscontext, str+8); X } else if(lstrncmp("vmail:", str, 6) == 0 && *(ptr=str+6) != '\0') { X for( ; *ptr == ' ' || *ptr == '\t' ; ptr++) X ; X *index(ptr, '\n') = '\0'; X strcpy(argkeep, ptr); X for(ptr=argkeep ; *ptr != '\0' ; ) { X pargv[(*pargc)++] = ptr; X ptr = next_token(ptr); X } X } else if(lstrncmp("path:", str, 5) == 0 && *(ptr=str+5) != '\0') { X squash(str); X if(*ptr == '/') { /* full pathname */ X mail_dir = NEWSTR(strlen(ptr)+1); X strcpy(mail_dir, ptr); X } else { X mail_dir = NEWSTR(strlen(home)+strlen(ptr)+1); X sprintf(mail_dir, "%s/%s", home, ptr); X } X } else if(lstrncmp("folder-protect:", str, 15) == 0 && X *(str+15) != '\0') { X squash(str); X folder_protect = atoo(str+15); X } else if(lstrncmp("current-folder:", str, 15) == 0 && X *(str+15) != '\0') { X squash(str); X cur_folder = NEWSTR(strlen(str+15)+1); X strcpy(cur_folder, str); X } X } X fclose(fp); X if(mail_dir == (char *) NULL) { X mail_dir = NEWSTR(strlen(home)+strlen(MAILDIR)+2); X sprintf(mail_dir, "%s/%s", home, str+6); X } X if(access(mail_dir, R_OK | W_OK | X_OK)) { X printf("%s: no permissions.\n", mail_dir); X exit(1); X } X if(*iscontext == '\0') X strcpy(iscontext, CONTEXT); X context = NEWSTR(strlen(mail_dir)+strlen(iscontext)+2); X sprintf(context, "%s/%s", mail_dir, iscontext); X if(access(context, R_OK | W_OK)) { X printf("%s: no permissions.\n", context); X exit(1); X } X if(cur_folder == (char *) NULL) X cur_folder = CURFOL; X} X X X/* -------------------- X Squash spaces, tabs, newlines out of given string. X-------------------- */ Xvoid Xsquash(str) X char *str; X{ X int i, j; X X for(i=0, j=0 ; (str[j] = str[i]) != '\0' ; i++) X if(str[j] != ' ' && str[j] != '\t' && str[j] != '\n') X j++; X} X X X/* -------------------- X Mark folders as specified by setenv, command line. At startup, default X is for only active folder to be cur_folder. X-------------------- */ Xvoid Xmark_valid_folders(argc, argv) X int argc; X char **argv; X{ X char *name; X folder f; X X name = cur_folder; X /* find valid folders - mark all folders from argv as valid */ X for(; argc > 0 ; argc--, argv++) X if(**argv == '+') /* startup folder */ X name = (*argv) + 1; X else if(**argv != '-') { /* not a flag */ X GOTO_NAME(f, *argv); X if(f == (folder) NULL) X printf("Warning: no such folder as %s\n", *argv); X else X f->valid = true; X } X GOTO_NAME(f, name); X if(f == (folder) NULL) { X printf("%s does not exist\n", name); X exit(1); X } X f->valid = true; X curflr = f; X} X X X/* -------------------- X Reset terminal, clean up. X-------------------- */ Xvoid Xto_normal() X{ X move(lines+FIRST-1, 0); X refresh(); X no_control(); X printf("\n"); X} X X X/* -------------------- X Reset terminal. X-------------------- */ Xvoid Xno_control() X{ X ioctl(0, TIOCSETP, &tty); X ioctl(0, TIOCSETP, &chrs); X ioctl(0, TIOCSETP, &lchrs); X} X X X/* -------------------- X Set terminal. X-------------------- */ Xvoid Xto_control() X{ X ioctl(0, TIOCSETP, &t_tty); X ioctl(0, TIOCSETP, &t_chrs); X ioctl(0, TIOCSETP, &t_lchrs); X} X X X#define mask(s) (1 << ((s)-1)) X X/* -------------------- X Trap for ^Z. X-------------------- */ Xvoid Xtstp() X{ X int x, y; X X getyx(curscr, y, x); X to_normal(); X fix_mh(); X X signal(SIGTSTP, SIG_DFL); X sigsetmask(sigblock(0) &~ mask(SIGTSTP)); X kill(0, SIGTSTP); X sigblock(mask(SIGTSTP)); X signal(SIGTSTP, tstp); X X if(top_level) { X to_control(); X touchwin(curscr); X wmove(curscr, y, x); X wrefresh(curscr); X } X} X X X/* -------------------- X Trap for ^?. X-------------------- */ Xvoid Xtint() X{ X touchwin(stdscr); X addstatus("-- interrupt --", true); X longjmp(env, 0); /* jump to main */ X} X X X/* -------------------- X Convert an ascii string to octal. X-------------------- */ Xint Xatoo(str) X char *str; X{ X int i; X X for(; *str < '0' && *str > '7' ; str++) X ; X for(i=0 ; *str >= '0' && *str <= '7' ; str++) X i = i*8 + *str - '0'; X return(i); X} X X X/* -------------------- X Update MH environment - context and current mail item of current folder. X-------------------- */ Xfix_mh() X{ X char str[LEN], buf[20]; X X update(context, "Current-Folder:", curflr->name, 15); X sprintf(str, "%s/%s/%s", mail_dir, curflr->name, SEQU); X sprintf(buf, "%d", curmail->number); X update(str, "cur:", buf, 4); X} X X X/* -------------------- X Update file, replacing line beginning with match of len by "match new". X-------------------- */ Xupdate(file, match, new, len) X char *file, *match, *new; X int len; X{ X FILE *fp, *tmp, *fopen(); X bool change = false; X char *mktemp(), *fgets(); X char str[LEN], *name = mktemp("/tmp/vmail.XXXXXX"); X X if((fp = fopen(file, "r")) == (FILE *) NULL) { X if((fp = fopen(file, "w+")) == (FILE *) NULL) X printf("Can't open %s for writing\n", file); X else { X fprintf(fp, "%s %s\n", match, new); X close(fp); X } X } else { X if((tmp = fopen(name, "w+")) == (FILE *) NULL) X printf("Can't open %s\n", file); X else { X while(fgets(str, LEN, fp) != (char *) NULL) X if(lstrncmp(str, match, len) == 0) { X change = true; X fprintf(tmp, "%s %s\n", match, new); X } else X fprintf(tmp, "%s", str); X if(! change) X fprintf(tmp, "%s %s\n", match, new); X fclose(fp); X fclose(tmp); X if((fp = fopen(file, "w+")) == (FILE *) NULL) X printf("Can't open %s for writing\n", file); X else { X tmp = fopen(name, "r"); X while(fgets(str, LEN, tmp) != (char *) NULL) X fprintf(fp, "%s", str); X fclose(fp); X fclose(tmp); X unlink(name); X } X } X } X} !The!End! exit