/* * MSGED.C * * Written on 30-Jul-90 by jim nutt. Changes on 10-Jul-94 by John Dennis. * Released to the public domain. * * Msged Mail Reader. */ #include #include #include #include #include #include #ifndef UNIX #include #endif #include "addr.h" #include "nedit.h" #include "maintmsg.h" #include "misc.h" #include "keys.h" #include "winsys.h" #include "menu.h" #include "main.h" #include "dialogs.h" #include "bmg.h" #include "screen.h" #include "memextra.h" #include "strextra.h" #include "areas.h" #include "version.h" #include "specch.h" #include "dosmisc.h" #include "config.h" #include "list.h" #include "help.h" #include "getopts.h" #include "keycode.h" #include "helpcmp.h" #include "helpinfo.h" #include "system.h" #include "msged.h" #include "maincmds.h" #include "readmail.h" #include "makemsgn.h" #include "nshow.h" #include "charset.h" #include "wrap.h" #include "textfile.h" #include "group.h" #ifdef MSDOS #ifdef USE_CRITICAL #include "critical.h" #endif #if !defined(NODOSSWAP) && !defined(__FLAT__) #include /* FP_OFF macro */ #include "spawn.h" #endif #endif #ifdef USE_MSGAPI #include "msg.h" #endif #ifndef randomize #define randomize() srand((unsigned) time(NULL)) #endif /* prototypes */ static void highest(void); static void gotomsg(unsigned long i); static void pm_next_area(void); /* local/global variables */ int scan = 0; /* set scan = 1 to scan for new mail at startup */ static int endMain = 0; static int errorlevel = 0; int direction = RIGHT; /* travel direction in msgbase */ char *msgbuf = NULL; /* message buffer used for reading, size: BUFLEN */ int msgederr = 0; /* errno for msged */ int set_rcvd = 1; /* used to tell readmsg() not to set rcvd */ /* only used readmail between.c and msged.c */ static unsigned long root = 0; /* root message of a thread */ static unsigned long back = 0; /* Where you were before you said "go * root" */ static unsigned long areastart = 0; /* msg number we started on in this * area */ static unsigned long lastfound = 0; /* msg number last found */ static unsigned long oldmsg = 0; static int command; #if defined(MSDOS) && defined(__TURBOC__) extern unsigned _stklen = 16384; #endif static void delete(void) { deletemsg(); } static void move(void) { if (message != NULL) { movemsg(); } } static void outtxt(void) { if (message != NULL) { writetxt(); } } static void set(void) { set_switch(); } static void chngaddr(void) { change_curr_addr(); } static void chngname(void) { change_username(); } static void chngnodel(void) { change_nodelist(); } static void do_help(void) { show_help(); } static void first(void) { CurArea.current = CurArea.first; } char *r_getbind(unsigned int key) { unsigned int i = 0; void (*action) (void); if (key & 0xff) { action = mainckeys[key & 0xff]; } else { action = mainakeys[(key >> 8) & 0xff]; } while (maincmds[i].label != NULL && action != maincmds[i].action) { i++; } return maincmds[i].label; } char *r_getlabels(int i) { return maincmds[i].label; } void r_assignkey(unsigned int key, char *label) { unsigned int i = 0; while ((maincmds[i].label != NULL) && (strncmp(maincmds[i].label, label, strlen(maincmds[i].label)) != 0)) { i++; } if (maincmds[i].label != NULL) { if (key & 0xff) { mainckeys[key & 0xff] = maincmds[i].action; } else { mainakeys[(key >> 8) & 0xff] = maincmds[i].action; } } } void dispose(msg * message) { if (message == NULL) { return; } if (message->text != NULL) { message->text = clearbuffer(message->text); } release(message->isto); release(message->isfrom); release(message->reply); release(message->msgid); release(message->subj); release(message->to.domain); release(message->from.domain); release(message->replyarea); release(message->charset_name); release(message); } msg *KillMsg(msg * m) { dispose(m); return NULL; } /* edithdr - lets the user edit the header only */ static void edithdr(void) { int q; if (message == NULL) { return; } q = 0; while (!q) { if (EditHeader(message) == Key_Esc) { if (confirm("Cancel?")) { message = KillMsg(message); return; } } else { q = 1; } } /* TE 07/2000: We have to rewrite all the message, not just the header, because of the @FLAGS kludge which is in the body MsgWriteHeader(message, WR_HEADER); */ writemsg(message); message = KillMsg(message); } /* cleanup - make sure you exit straight after calling this! */ void cleanup(char *msg,...) { AREA *a; if (CurArea.status) { highest(); AreaSetLast(&CurArea); MsgAreaClose(); } setcwd(ST->home); WndClose(hMnScr); TTgotoxy(term.NRow - 1, 0); TTclose(); for (a = &(arealist[SW->area = 0]); SW->area < SW->areas; a = &(arealist[++SW->area])) { errorlevel |= (a->netmail && a->new) ? 0x01 : 0; errorlevel |= (a->echomail && a->new) ? 0x02 : 0; errorlevel |= (a->uucp && a->new) ? 0x04 : 0; errorlevel |= (a->news && a->new) ? 0x08 : 0; errorlevel |= (a->local && a->new) ? 0x10 : 0; } if (msg) { va_list ap; va_start(ap, msg); putchar('\n'); vprintf(msg, ap); va_end(ap); } group_destroy_arealist(); destroy_charset_maps(); DeinitMem(); #ifdef USE_MSGAPI MsgApiTerm(); #endif TTCurSet(1); /*#ifdef UNIX TTScolor(7); #endif */ #ifdef USE_CRITICAL remove24h(); #endif } static void quit(void) { endMain = 1; } /* search - search header and message text for keyword */ static void search(void) { LINE *l = NULL; static char prompt[256] = ""; static char patstr[256] = ""; char tempstr[60]; char totalmsg[12]; /* to allow for upto 4,294,967,295 msgs */ msg *m = NULL; unsigned long tmp = 0; int done = 0; unsigned int boxwidth; /* boxwidth of search display */ unsigned int mlength; if (!GetString(" Keyword Search (message header and text) ", "Search for:", prompt, 64)) { return; } if (strlen(prompt) == 0) { return; } sprintf(tempstr, " Searching for \"%.30s\" ", prompt); sprintf(totalmsg, "%ld", CurArea.messages); mlength = (2 * strlen(totalmsg)) + 26; if (mlength <= (strlen(tempstr) + 18)) { boxwidth = (strlen(tempstr) + 18); } else { boxwidth = mlength; } if (!OpenMsgWnd(boxwidth, 6, tempstr, NULL, 0, 0)) { return; } message = KillMsg(message); /* * We use this to ensure the rcvd bit doesn't get set. Possibly a * mistake if the msg found is addressed to the user... */ set_rcvd = 0; SendMsgWnd("Press Esc to stop", 0); if ((stricmp(prompt, patstr) == 0) && (lastfound == CurArea.current) && (CurArea.current < CurArea.messages)) { if (direction == RIGHT) { tmp = CurArea.current + 1; } else { if (CurArea.current > 1) { tmp = CurArea.current - 1; } } bmg_setsearch(prompt); } else { tmp = CurArea.current; lastfound = 0; strcpy(patstr, prompt); bmg_setsearch(prompt); } for (;;) { if ((direction == RIGHT) && (tmp > CurArea.messages)) { break; } else if (tmp == 0) { break; } /* check for event here */ if (done == TRUE || (KeyHit() && GetKey() == Key_Esc)) { break; } l = NULL; if ((m = readmsg(tmp)) != NULL) { sprintf(tempstr, "Reading message #%ld of #%ld", tmp, CurArea.messages); SendMsgWnd(tempstr, 1); if (bmg_search(m->isto) != NULL) { done = TRUE; break; } if (bmg_search(m->isfrom) != NULL) { done = TRUE; break; } if (bmg_search(m->subj) != NULL) { done = TRUE; break; } l = m->text; while ((l != NULL) && (done == FALSE)) { if (l->text && (strlen(l->text) > 0)) { if (bmg_search(l->text) != NULL) { done = TRUE; break; } } l = l->next; } if (done == TRUE) { break; } dispose(m); m = NULL; } if (direction == RIGHT) { tmp++; } else { tmp--; } } CloseMsgWnd(); set_rcvd = 1; /* readmsg() is now free to set it */ if (done) { CurArea.current = tmp; lastfound = tmp; oldmsg = tmp; message = m; TTBeginOutput(); ShowMsgHeader(message); if (l != NULL) { l->block = TRUE; RefreshMsg(l, 6); } else { RefreshMsg(message->text, 6); } TTEndOutput(); } else { dispose(m); } } /* hdrsearch - search header for keyword */ static void hdrsearch(void) { LINE *l = NULL; static char prompt[256] = ""; static char patstr[256] = ""; char tempstr[40]; char totalmsg[12]; /* to allow for upto 4,294,967,295 msgs */ msg *m = NULL; unsigned long tmp = 0; int done = 0; unsigned int boxwidth; /* boxwidth of search display */ unsigned int mlength; if (!GetString(" Keyword Search (message header) ", "Search for:", prompt, 64)) { return; } if (strlen(prompt) == 0) { return; } sprintf(tempstr, " Searching for \"%.30s\" ", prompt); sprintf(totalmsg, "%ld", CurArea.messages); mlength = (2 * strlen(totalmsg)) + 26; if (mlength <= (strlen(tempstr) + 18)) { boxwidth = (strlen(tempstr) + 18); } else { boxwidth = mlength; } if (!OpenMsgWnd(boxwidth, 6, tempstr, NULL, 0, 0)) { return; } message = KillMsg(message); /* * We use this to ensure the rcvd bit doesn't get set. Possibly a * mistake if the msg found is addressed to the user... */ set_rcvd = 0; SendMsgWnd("Press Esc to stop", 0); if ((stricmp(prompt, patstr) == 0) && (lastfound == CurArea.current) && (CurArea.current < CurArea.messages)) { if (direction == RIGHT) { tmp = CurArea.current + 1; } else { if (CurArea.current > 1) { tmp = CurArea.current - 1; } } bmg_setsearch(prompt); } else { tmp = CurArea.current; lastfound = 0; strcpy(patstr, prompt); bmg_setsearch(prompt); } for (;;) { if ((direction == RIGHT) && (tmp > CurArea.messages)) { break; } else if (tmp == 0) { break; } /* check for event here */ if (done == TRUE || (KeyHit() && GetKey() == Key_Esc)) { break; } l = NULL; if ((m = MsgReadHeader(tmp, RD_HEADER_BRIEF)) != NULL) { sprintf(tempstr, "Reading message #%ld of #%ld", tmp, CurArea.messages); SendMsgWnd(tempstr, 1); if (bmg_search(m->isto) != NULL) { done = TRUE; break; } if (bmg_search(m->isfrom) != NULL) { done = TRUE; break; } if (bmg_search(m->subj) != NULL) { done = TRUE; break; } if (done == TRUE) { break; } dispose(m); m = NULL; } if (direction == RIGHT) { tmp++; } else { tmp--; } } CloseMsgWnd(); set_rcvd = 1; /* readmsg() is now free to set it */ if (done) { dispose(m); m = readmsg(tmp); message = m; CurArea.current = tmp; lastfound = tmp; oldmsg = tmp; TTBeginOutput(); ShowMsgHeader(message); if (l != NULL) { l->block = TRUE; RefreshMsg(l, 6); } else { RefreshMsg(message->text, 6); } TTEndOutput(); } else { dispose(m); } } /* spmail - searchs for personal mail in a single area */ static void spmail(void) { LINE *l = NULL; static char prompt[256] = ""; static char patstr[256] = ""; char tempstr[40]; char totalmsg[12]; /* to allow for upto 4,294,967,295 msgs */ msg *m = NULL; unsigned long tmp; int done = 0; int boxwidth; /* boxwidth for search display */ int mlength; strcpy(prompt, ST->username); if (strlen(prompt) == 0) { return; } sprintf(tempstr, " Searching for \"%.30s\" ", prompt); sprintf(totalmsg, "%ld", CurArea.messages); mlength = (2 * strlen(totalmsg)) + 26; if (mlength <= (strlen(tempstr) + 18)) { boxwidth = (strlen(tempstr) + 18); } else { boxwidth = mlength; } if (!OpenMsgWnd(boxwidth, 6, tempstr, NULL, 0, 0)) { return; } message = KillMsg(message); /* * We use this to ensure the rcvd bit doesn't get set. Possibly a * mistake if the msg found is addressed to the user... */ set_rcvd = 0; SendMsgWnd("Press Esc to stop", 0); if ((stricmp(prompt, patstr) == 0) && (lastfound == CurArea.current)) { tmp = CurArea.current + 1; } else { tmp = CurArea.current + 1; lastfound = 0; strcpy(patstr, prompt); bmg_setsearch(prompt); } for (; tmp <= CurArea.messages; tmp++) { /* check for event here */ if (done == TRUE || (KeyHit() && (GetKey()) == Key_Esc)) { break; } l = NULL; if ((m = readmsg(tmp)) != NULL) { sprintf(tempstr, "Area: %s", CurArea.tag); SendMsgWnd(tempstr, 1); sprintf(tempstr, "Reading message #%ld of #%ld", tmp, CurArea.messages); SendMsgWnd(tempstr, 2); bmg_setsearch(ST->username); if (bmg_search(m->isto) != NULL) { done = TRUE; break; } if (done == TRUE) { break; } dispose(m); m = NULL; } } CloseMsgWnd(); set_rcvd = 1; /* readmsg() is now free to set it */ if (done) { CurArea.current = tmp; lastfound = tmp; oldmsg = tmp; message = m; TTBeginOutput(); ShowMsgHeader(message); if (l != NULL) { l->block = TRUE; RefreshMsg(l, 6); } else { RefreshMsg(message->text, 6); } TTEndOutput(); } else { dispose(m); } } /* pmail - personal mail scan */ static int x = -1; static int firstarea = 0; static void pmail(void) { LINE *l = NULL; static char prompt[256] = ""; static char patstr[256] = ""; static char username[256] = ""; char tempstr[40]; char totalmsg[12]; /* to allow upto 4,294,967,295 msgs */ msg *m = NULL; unsigned long tmp; int done = 0; int boxwidth; /* boxwidth for search display */ int mlength; unsigned int chkkey = 0; int again = 1; while (again) { again = 0; if (x == -1) { firstarea = SW->grouparea; strcpy(username, ST->username); } x += 1; strcpy(prompt, username); if (strlen(prompt) == 0) { return; } sprintf(tempstr, " Searching for \"%.30s\" ", prompt); sprintf(totalmsg, "%ld", CurArea.messages); mlength = (2 * strlen(totalmsg)) + 26; if (mlength <= (strlen(tempstr) + 18)) { boxwidth = (strlen(tempstr) + 18); } else { boxwidth = mlength; } if ((long)arealist[SW->area].messages != (long)arealist[SW->area].lastread) { if (!OpenMsgWnd(boxwidth, 6, tempstr, NULL, 0, 0)) { return; } } message = KillMsg(message); /* * We use this to ensure the rcvd bit doesn't get set. Possibly a * mistake if the msg found is addressed to the user... */ set_rcvd = 0; if ((long)arealist[SW->area].messages != (long)arealist[SW->area].lastread) { SendMsgWnd("Press Esc to stop", 0); } if (stricmp(prompt, patstr) == 0 && lastfound == CurArea.current) { tmp = CurArea.current + 1; } else { tmp = CurArea.current + 1; lastfound = 0; strcpy(patstr, prompt); bmg_setsearch(prompt); } for (; tmp <= CurArea.messages; tmp++) { /* check for event here */ if (done == TRUE || (KeyHit() && (chkkey = GetKey()) == Key_Esc)) { break; } l = NULL; if ((m = readmsg(tmp)) != NULL) { sprintf(tempstr, "Area: %s", CurArea.tag); if ((long)arealist[SW->area].messages != (long)arealist[SW->area].lastread) { SendMsgWnd(tempstr, 1); } sprintf(tempstr, "Reading message #%ld of #%ld", tmp, CurArea.messages); if ((long)arealist[SW->area].messages != (long)arealist[SW->area].lastread) { SendMsgWnd(tempstr, 2); } bmg_setsearch(username); if (bmg_search(m->isto) != NULL) { done = TRUE; break; } if (done == TRUE) { break; } dispose(m); m = NULL; } } if ((long)arealist[SW->area].messages != (long)arealist[SW->area].lastread) { CloseMsgWnd(); } set_rcvd = 1; /* readmsg() is now free to set it */ if (done) { CurArea.current = tmp; lastfound = tmp; oldmsg = tmp; message = m; ShowMsgHeader(message); if (l != NULL) { l->block = TRUE; RefreshMsg(l, 6); } else { RefreshMsg(message->text, 6); } } else { dispose(m); pm_next_area(); if (SW->grouparea != firstarea && chkkey != Key_Esc) { again = 1; } else { set_area(firstarea); SetupArea(); firstarea = 0; x = -1; } } } } static void gotomsg0(void) { gotomsg(0L); } /* confirm - Ask if the user is doing something on purpose (Never! :-) */ int confirm(char *option) /* Allows more meaningful confirm messages */ { if (!SW->confirmations) { return TRUE; } return ChoiceBox("", option, "Yes", "No", NULL) == ID_ONE ? TRUE : FALSE; } /* SetupArea - Sets up the operating vars for the current area */ void SetupArea(void) { lastfound = 0; direction = RIGHT; back = CurArea.current; root = CurArea.current; areastart = CurArea.current; /* * Set this area up with it's info: The template, The username, * lastread (fido areas) name, useroffset (squish areas). */ if (SW->areadefinesuser) { release(ST->username); release(ST->template); release(ST->lastread); ST->username = xstrdup(user_list[CurArea.username].name); SW->useroffset = user_list[CurArea.username].offset; ST->lastread = xstrdup(user_list[CurArea.username].lastread); } if (templates != NULL) { ST->template = xstrdup(templates[CurArea.template]); } } static void set_area_backend(int show_all, int newgrouparea, int newarea) { int done = 0; int ret; int temp; if (CurArea.status) /* close current area, if open */ { highest(); AreaSetLast(&CurArea); MsgAreaClose(); } message = KillMsg(message); SW->area = newarea; if (!show_all) { SW->grouparea = newgrouparea; } while (!CurArea.status && !done) { CurArea.messages = MsgAreaOpen(&CurArea); if (!CurArea.status) { ret = ChoiceBox(" Error ", "Error opening area;", "Retry", "New area", "Cancel"); switch (ret) { case ID_TWO: /* UH, OH, FIXME! */ temp = SW->grouparea; temp = selectarea("Pic New Area", temp); SW->grouparea = temp; SW->area = group_getareano(temp); break; case ID_THREE: done = TRUE; break; default: break; } } else { done = TRUE; } } ShowNewArea(); /* display the new area */ } /* set_area - Opens a new area & sets all the vars */ void set_area(int newgrouparea) { set_area_backend(0, newgrouparea, group_getareano(newgrouparea)); } void set_nongrouped_area(int newarea) { set_area_backend(1, 0, newarea); } /* * Scans all the areas for new mail, calling scan_areas(), but is * callable from other modules. */ void area_scan(int all) { scan_areas(all); } /* * Scans all the areas for new mail, calling al_scan_areas(), but is * callable from other modules. Calls from Arealist. */ void arealist_area_scan(int all) { al_scan_areas(all); } /* * void ...(void) wrapper functions for void scan_areas(int) */ void scan_all_areas(void) { scan_areas(1); } void scan_unscanned_areas(void) { scan_areas(0); } /* * Scans all the areas for new messages. Saves the current area and * returns to it. * Parameter: int all: =1: scan all areas, =0: scan unscanned areas */ static void scan_areas(int all) { char line[255]; char temp[20]; int a, ga; int l; int x,y; TTgetxy(&x,&y); a = SW->area; ga = SW->grouparea; l = strlen(PROG) + strlen(VERNUM VERPATCH); if (!SW->dmore) { l += sprintf(temp, "%ld of %ld", CurArea.current, CurArea.messages); } if (!SW->statbar) { if (!OpenMsgWnd(50, 6, " Scanning areas for new messages ", NULL, 0, 0)) { return; } SendMsgWnd("Press Esc to stop", 0); } if (CurArea.status) { highest(); AreaSetLast(&CurArea); MsgAreaClose(); } if (all) /* reset the "scanned" flag of all areas */ { for (SW->grouparea = 0; SW->grouparea < SW->groupareas; SW->grouparea++) { if (group_getareano(SW->grouparea) >= 0) arealist[group_getareano(SW->grouparea)].scanned = 0; } } for (SW->grouparea = 0; SW->grouparea < SW->groupareas; SW->grouparea++) { SW->area = group_getareano(SW->grouparea); if (SW->area < 0 || ((!all) && (arealist[SW->area].scanned))) { continue; } if (SW->statbar) { TTBeginOutput(); sprintf(line, "%.40s", CurArea.description); if (!SW->dmore) { line[maxx - l - 20] = '\0'; WndPutsn(l + 9, maxy - 1, maxx - l - 10, cm[CM_ITXT], "Scanning:"); WndWriteStr(l + 19, maxy - 1, cm[CM_ITXT], line); } else { line[79 - strlen(PROG) - strlen(VERNUM VERPATCH) - 16] = '\0'; WndPutsn(l + 6, maxy - 1, maxx - l - 7, cm[CM_ITXT], "Scanning:"); WndWriteStr(l + 16, maxy - 1, cm[CM_ITXT], line); } TTEndOutput(); } else { sprintf(line, "%.40s", CurArea.description); SendMsgWnd(line, 1); } if (KeyHit() && GetKey() == Key_Esc) { break; } CurArea.messages = MsgAreaOpen(&CurArea); if (CurArea.status) { MsgAreaClose(); } } TTBeginOutput(); if (!SW->statbar) { CloseMsgWnd(); } SW->area = a; SW->grouparea = ga; CurArea.messages = MsgAreaOpen(&CurArea); if (SW->statbar) { if (!SW->dmore) { WndPutsn(l + 9, maxy - 1, maxx - l - 10, cm[CM_ITXT], " "); } else { WndPutsn(l + 6, maxy - 1, maxx - l - 7, cm[CM_ITXT], " "); } } #if defined(MSDOS) && !defined(__FLAT__) if (SW->statbar) { sprintf(line, "%c %3ldK ", SC7, (long)(corerem() / 1024)); WndPutsn(maxx - 7, maxy - 1, 1, cm[CM_ITXT] | F_ALTERNATE, line + 1); WndPutsn(maxx - 6, maxy - 1, 6, cm[CM_ITXT], line + 1); } #endif if (SW->statbar && !SW->dmore) { int l; sprintf(line, "%ld of %ld %c", CurArea.current, CurArea.messages, SC7); l = strlen(line); WndPutsn((strlen(PROG) + strlen(VERNUM VERPATCH) + 6), maxy - 1, l - 1, cm[CM_ITXT], line); WndPutsn((strlen(PROG) + strlen(VERNUM VERPATCH) + 6 + l - 1), maxy - 1, 18 - (l - 1), cm[CM_ITXT] | F_ALTERNATE, line + l - 1); } TTgotoxy(x,y); TTEndOutput(); } /* * Scans all areas for new messages, from Arealist screen. Saves the * current area and returns to it. */ static void al_scan_areas(int all) { char line[255]; int a = SW->area; int ga = SW->grouparea; if (!OpenMsgWnd(50, 6, " Scanning areas for new messages ", NULL, 0, 0)) { return; } SendMsgWnd("Press Esc to stop", 0); if (CurArea.status) { highest(); AreaSetLast(&CurArea); MsgAreaClose(); } if (all) { for (SW->grouparea = 0; SW->grouparea < SW->groupareas; SW->grouparea++) { if (group_getareano(SW->grouparea) >= 0) arealist[group_getareano(SW->grouparea)].scanned = 0; } } for (SW->grouparea = 0; SW->grouparea < SW->groupareas; SW->grouparea++) { SW->area = group_getareano(SW->grouparea); if (SW->area < 0 || ((!all) && arealist[SW->area].scanned)) { continue; } sprintf(line, "%.40s", CurArea.description); SendMsgWnd(line, 1); if (KeyHit() && GetKey() == Key_Esc) { break; } CurArea.messages = MsgAreaOpen(&CurArea); if (CurArea.status) { MsgAreaClose(); } } CloseMsgWnd(); SW->area = a; SW->grouparea = ga; } /* next_area - goes to the next area with unread messages */ static void next_area(void) { int NewArea; int OldArea; if (SW->groupareas < 2) { return; } OldArea = SW->grouparea; do { NewArea = (SW->grouparea + 1) % SW->groupareas; } while (group_getareano(NewArea) < 0); while (((long)arealist[group_getareano(NewArea)].messages <= (long)arealist[group_getareano(NewArea)].lastread) && arealist[group_getareano(NewArea)].scanned) { NewArea = (NewArea + 1) % SW->groupareas; if (NewArea == OldArea) { ChoiceBox(" Notice ", "There are no more unread messages in this area", "Ok", NULL, NULL); break; } } set_area(NewArea); SetupArea(); } static void pm_next_area(void) { int NewArea; if (SW->groupareas < 2) { return; } NewArea = SW->grouparea; do { /* prevent group separators from being selected */ NewArea = (NewArea + 1) % SW->groupareas; } while (group_getareano(NewArea) < 0); set_area(NewArea); SetupArea(); } /* prev_area - goes to the previous area with unread messages */ static void prev_area(void) { int OldArea; int NewArea; if (SW->groupareas < 2) { return; } OldArea = SW->grouparea; NewArea = SW->grouparea; do { NewArea--; NewArea = (NewArea < 0) ? SW->groupareas - 1 : NewArea; } while (group_getareano(NewArea)< 0); while ((((long)arealist[group_getareano(NewArea)].messages - (long)arealist[group_getareano(NewArea)].lastread) <= 0) && arealist[group_getareano(NewArea)].scanned) { NewArea--; NewArea = (NewArea < 0) ? SW->groupareas - 1 : NewArea; if (NewArea == OldArea) { NewArea--; NewArea = (NewArea < 0) ? SW->groupareas - 1 : NewArea; break; } } set_area(NewArea); SetupArea(); } /* highest - sets the higest read message */ static void highest(void) { CurArea.lastread = min(CurArea.current, CurArea.messages); root = CurArea.current; } /* left - goes one message to the left */ static void left(void) { direction = LEFT; if (CurArea.current > 1) { CurArea.current--; } root = CurArea.current; } /* right - goes one message to the right */ static void right(void) { direction = RIGHT; if (CurArea.current < CurArea.messages) { CurArea.current++; } else if (SW->rightnextunreadarea) { next_area(); } highest(); } /* * Gets a number from the user and goes to that message * number (if valid). */ static void gotomsg(unsigned long i) { EVT e; WND *hWnd, *hCurr; char buf[10]; int done = 0; int disp = 1; int ret; int pos; TTBeginOutput(); hCurr = WndTop(); hWnd = WndPopUp(30, 6, INSBDR | SHADOW, cm[IP_BTXT], cm[IP_NTXT]); WndTitle(" Jump to Message ", cm[IP_NTXT]); WndWriteStr(1, 1, cm[IP_NTXT], "Message #"); TTEndOutput(); if (i != 0) { sprintf(buf, "%lu", i); } else { strcpy(buf, ""); } pos = strlen(buf); while (!done) { ret = WndGetLine(17, 1, 6, buf, cm[IP_ETXT], &pos, 0, 0, disp, &e); switch (e.msgtype) { case WND_WM_CHAR: switch (ret) { case Key_Ent: i = atoi(buf); if (i == 0) { done = 1; i = CurArea.current; } else { if (i > 0 && i <= CurArea.messages) { done = 1; } } break; case Key_Esc: done = 1; i = CurArea.current; break; default: break; } break; default: break; } disp = 0; } WndClose(hWnd); WndCurr(hCurr); CurArea.current = i; highest(); } /* newarea - gets a new area from the user and goes to it */ static int newarea(void) { int new; new = mainArea(); if (new >= 0) { if (CurArea.status) { highest(); AreaSetLast(&CurArea); MsgAreaClose(); } set_area(new); SetupArea(); } return new; } /* start - begins reading the initial configuration file */ static int start(char *cfg, char *afn) { opening(cfg, afn); SW->grouparea = -1; do { /* don't allow a separator to be come current area */ SW->area = group_getareano(++(SW->grouparea)); } while (SW->area < 0); message = NULL; return 0; } /* go_last - goes to the highest-read message in the msgbase */ static void go_last(void) { CurArea.current = min(CurArea.lastread, CurArea.messages); root = CurArea.current; } /* slast - goes to the very last msg in the msgbase */ static void slast(void) { CurArea.current = CurArea.messages; highest(); } /* astart - goes to the first read msg in the msgbase (for this session) */ static void astart(void) { CurArea.current = min(areastart, CurArea.messages); highest(); } /* link_from - goes to the message that the current message is a reply to */ static void link_from(void) { if (!message || !message->replyto) { return; } CurArea.current = message->replyto; } /* view - toggles the showing of hidden information */ static void view(void) { SW->shownotes = !SW->shownotes; message = KillMsg(message); } /* * Links up the msgbase, putting up a small menu if there is more * than one thread to go to. Quite complicated. */ static void link_to(void) { msg *m; unsigned long cnt, k = 0; char txtbuf[40]; char *replies[11]; if (!message) { return; } for (cnt = 0; cnt < 10; cnt++) { if (message->replies[(size_t) cnt] != 0) { k++; } } if (k < 1) { return; } else { if (k == 1) { for (cnt = 0; cnt <= 10; cnt++) { if (message->replies[(size_t) cnt] != 0) { break; } } CurArea.current = message->replies[(size_t) cnt]; CurArea.lastread = max(CurArea.lastread, CurArea.current); return; } } k = 0; for (cnt = 0; cnt < 10; cnt++) { if (message->replies[(size_t) cnt] != 0) { if ((m = MsgReadHeader(message->replies[(size_t) cnt], RD_HEADER)) != NULL) { sprintf(txtbuf, "%5ld : %.22s", message->replies[(size_t) cnt], m->isfrom); replies[(size_t) k++] = xstrdup(txtbuf); KillMsg(m); } } } replies[(size_t) k] = NULL; cnt = DoMenu(maxx - 43, 9, maxx - 6, 9 + (int)k - 1, replies, 0, SELBOX_LINKTO, ""); if (cnt != (unsigned long)-1) { CurArea.current = atoi(replies[(size_t) cnt]); CurArea.lastread = max(CurArea.lastread, CurArea.current); } for (k = 0; k < 10; k++) { if (replies[(size_t) k] == NULL) { break; } xfree(replies[(size_t) k]); } } /* * go_next; Does the same as above but ignores all but the first reply * chain. */ static void go_next(void) { if (!message || !message->replies[0]) { return; } back = CurArea.current; CurArea.current = message->replies[0]; CurArea.lastread = max(CurArea.lastread, CurArea.current); } /* * Goes to the first msg in a reply chain (you must have navigated * through it already). */ static void go_root() { back = CurArea.current; CurArea.current = root; highest(); } /* go_back - goes back to where you were in the reply chain (untested) */ static void go_back() { CurArea.current = back; highest(); } /* rotate - Sets the ROT13 char */ static void rotate() { rot13 = (rot13 + 1) % 3; message = KillMsg(message); } void shell_to_dos(void) { #if defined(MSDOS) && !defined(NODOSSWAP) && !defined(__FLAT__) int rc; char swapfn[PATHLEN]; char **envp = environ, **env, *envbuf, *envptr, *ep; int swapping = USE_ALL | HIDE_FILE | DONT_SWAP_ENV; int envlen = 0; if (envp != NULL) { for (env = envp; *env != NULL; env++) { envlen += strlen(*env) + 1; } } if (envlen) { envlen = (envlen + 32) & 0xfff0; envbuf = xmalloc(envlen); envptr = envbuf; if (FP_OFF(envptr) & 0x0f) { envptr += 16 - (FP_OFF(envptr) & 0x0f); } ep = envptr; for (env = envp; *env != NULL; env++) { strcpy(ep, *env); ep = ep + strlen(*env) + 1; } *ep = 0; } if (ST->swap_path) { sprintf(swapfn, "%s\\%s", ST->swap_path, SWAP_FILENAME); } else { strcpy(swapfn, SWAP_FILENAME); } rc = prep_swap(swapping, swapfn); if (rc > -1) { rc = do_spawn(swapping, ST->comspec, "", envlen, envptr); } else { fprintf(stderr, "\nError occured during do_spawn(); rc=%d. Press Enter to return...", rc); while (GetKey() != 13) { } } #elif defined(PACIFIC) spawnl(ST->comspec, NULL); #elif defined(UNIX) system(ST->comspec); #elif defined(__FLAT__) || defined(OS2) spawnl(0, ST->comspec, ST->comspec, NULL); #else system(""); #endif } static void go_dos(void) { char tmp[PATHLEN]; mygetcwd(tmp, PATHLEN); setcwd(ST->home); WndClose(hMnScr); KillHotSpots(); TTgotoxy(term.NRow - 1, 0); TTclose(); cursor(1); fputs("\nEnter the command \"EXIT\" to return to " PROG ".\n", stderr); shell_to_dos(); cursor(0); InitScreen(); BuildHotSpots(); DrawHeader(); ShowNewArea(); message = KillMsg(message); cursor(0); setcwd(tmp); } static void rundos(void) { WND *hCurr, *hWnd; char curdir[PATHLEN]; char cmd[64], tmp[40]; int ret; mygetcwd(curdir, PATHLEN); memset(cmd, 0, sizeof cmd); #if defined(MSDOS) if (!GetString(" System Command ", "Enter DOS command to execute:", cmd, 64)) { return; } #elif defined(OS2) if (!GetString(" System Command ", "Enter OS/2 command to execute:", cmd, 64)) { return; } #else if (!GetString(" System Command ", "Enter system command to execute:", cmd, 64)) { return; } #endif if (cmd[0] == '\0') { strcpy(cmd, ST->comspec); } hCurr = WndTop(); hWnd = WndOpen(0, 0, maxx - 1, maxy - 1, NBDR, 0, cm[CM_NTXT]); cursor(1); ret = system(cmd); cursor(0); #if defined(MSDOS) sprintf(tmp, "DOS command returned %d", ret); #elif defined(OS2) sprintf(tmp, "OS/2 command returned %d", ret); #else sprintf(tmp, "System command returned %d", ret); #endif ChoiceBox(" Info ", tmp, " Ok ", NULL, NULL); WndClose(hWnd); WndCurr(hCurr); setcwd(curdir); message = KillMsg(message); } static void nada(void) { /* do nothing */ } void dolist(void) { if (SW->direct_list) endMain = 1; else { if (message != NULL) { do_list(); ClearScreen(); DrawHeader(); ShowNewArea(); } } } static void list(void) { dolist(); } int CKey(int ch) { return ((int)ConvertKey(ch)); } void show_usage(void) { printf( "%-30s; %s\n" "-------------------------------------------------------------------------------\n" "\n" "Usage: MSGED [options]\n" "\n" "-a Use instead of SQUISH.CFG.\n" "-c Use instead of MSGED.CFG.\n" "-I Display debug information at startup, then exit.\n" "-? Display this help.\n" "-h Display this help.\n" "-hc Compile help file.\n" "-hi Decompile compiled help file.\n" "-k Display keyboard scan codes.\n", PROG " " VERPROJECT " " VERNUM VERPATCH VERBRANCH "; Mail Reader", "Compiled on " __DATE__ " at " __TIME__ ); } int cmd_dbginfo = 0; int cmd_usage = 0; int cmd_helpcmp = 0; int cmd_helpinfo = 0; int cmd_keycode = 0; static char cmd_cfgfnm[250]; static char cmd_areafnm[250]; opt_t opttable[] = { {"?", OPTBOOL, &cmd_usage}, {"i", OPTBOOL, &cmd_dbginfo}, {"I", OPTBOOL, &cmd_dbginfo}, {"hc", OPTBOOL, &cmd_helpcmp}, {"HC", OPTBOOL, &cmd_helpcmp}, {"hi", OPTBOOL, &cmd_helpinfo}, {"HI", OPTBOOL, &cmd_helpinfo}, {"h", OPTBOOL, &cmd_usage}, {"H", OPTBOOL, &cmd_usage}, {"k", OPTBOOL, &cmd_keycode}, {"K", OPTBOOL, &cmd_keycode}, {"c", OPTSTR, cmd_cfgfnm}, {"C", OPTSTR, cmd_cfgfnm}, {"a", OPTSTR, cmd_areafnm}, {"A", OPTSTR, cmd_areafnm}, {NULL, 0, NULL} }; static void message_reading_mode(void) { EVT event; int newmsg; if (window_resized) { window_resized = 0; /* ack! */ WndClose(hMnScr); KillHotSpots(); TTclose(); InitScreen(); adapt_margins(); BuildHotSpots(); ShowNewArea(); } DrawHeader(); endMain = 0; message = KillMsg(message); message = readmsg(CurArea.current); if (!CurArea.status || message == NULL || !CurArea.messages) { ClearMsgScreen(); ShowMsgHeader(message); ChoiceBox(" Notice ", "There are no messages stored in this area", " Ok ", NULL, NULL); } newmsg = 1; while (!endMain) { if (!CurArea.messages || newmsg || !CurArea.status || message == NULL) { TTBeginOutput(); if (!CurArea.status || message == NULL || !CurArea.messages) { ClearMsgScreen(); } ShowMsgHeader(message); newmsg = 0; if (message != NULL) { RefreshMsg(message->text, 6); } TTEndOutput(); } oldmsg = CurArea.current; command = MnuGetMsg(&event, hMnScr->wid); switch (event.msgtype) { case WND_WM_RESIZE: window_resized = 1; /* we'll exit to redraw the screen */ break; case WND_WM_COMMAND: switch (command) { case LMOU_CLCK: switch (event.id) { case ID_LNDN: link_from(); break; case ID_LNUP: link_to(); break; default: break; } break; case MOU_LBTDN: case LMOU_RPT: switch (event.id) { case ID_SCRUP: Go_Up(); break; case ID_SCRDN: Go_Dwn(); break; case ID_MGLFT: left(); break; case ID_MGRGT: right(); break; default: break; } break; default: break; } break; case WND_WM_MOUSE: switch (command) { case MOU_LBTDN: if (event.x >= (maxx - MNU_LEN - 1) && event.y == 0) { command = ProcessMenu(&MouseMnu, &event, 0); } if (command == ID_QUIT) { quit(); } break; default: break; } break; case WND_WM_CHAR: switch (command) { case Key_PgUp: Go_PgUp(); break; case Key_PgDn: case Key_Spc: Go_PgDwn(); break; case Key_Up: Go_Up(); break; case Key_Dwn: Go_Dwn(); break; default: if (command & 0xff) { if (isdigit(command & 0xff)) { gotomsg((command & 0xff)- 0x30); } else { if (mainckeys[command & 0xff]) { (*mainckeys[command & 0xff]) (); } } } else { if (mainakeys[command >> 8]) { (*mainakeys[command >> 8]) (); } } break; } break; } if (window_resized && endMain == 0) endMain = 2; if (CurArea.messages > 0 && (!message || oldmsg != CurArea.current || CurArea.current == 0)) { message = KillMsg(message); if (CurArea.current == 0) { CurArea.current = 1; } if (CurArea.status) { message = readmsg(CurArea.current); newmsg = 1; } } } } int main(int argc, char *argv[]) { int optup; optup = getopts(argc, argv, opttable); if (cmd_keycode || cmd_helpcmp || cmd_helpinfo || cmd_usage) { if (cmd_usage) { show_usage(); } else if (cmd_keycode) { keycode(); } else if (cmd_helpcmp) { helpcmp(argc - optup + 1, &argv[optup - 1]); } else if (cmd_helpinfo) { helpinfo(argc - optup + 1, &argv[optup - 1]); } return (0); } #ifdef USE_CRITICAL /* Removed by cleanup() */ install24h(); #endif #if !defined(PACIFIC) && !defined(__MINGW32__) tzset(); #endif randomize(); #ifdef USE_MSGAPI MsgApiInit(); #endif if (*cmd_cfgfnm && *cmd_areafnm) { command = start(cmd_cfgfnm, cmd_areafnm); } else if (*cmd_cfgfnm) { command = start(cmd_cfgfnm, NULL); } else if (*cmd_areafnm) { command = start(NULL, cmd_areafnm); } else { command = start(NULL, NULL); } if (ST->helpfile) { HelpInit(ST->helpfile); } BuildHotSpots(); if (scan) { arealist_area_scan(1); } /* DrawHeader(); */ RegisterKeyProc(CKey); /* to allow for macros in the system */ while (endMain == 2 || newarea() >= 0) { if (SW->direct_list) { while (do_list()) { message_reading_mode(); } } else { message_reading_mode(); } if (CurArea.status && endMain != 2) { highest(); AreaSetLast(&CurArea); MsgAreaClose(); } } cleanup(NULL); return errorlevel; }