/*
* 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 <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#ifndef UNIX
#include <process.h>
#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 <dos.h> /* 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<areafile> Use <areafile> instead of SQUISH.CFG.\n"
"-c<configfile> Use <configfile> instead of MSGED.CFG.\n"
"-I Display debug information at startup, then exit.\n"
"-? Display this help.\n"
"-h Display this help.\n"
"-hc <source> <target> Compile help file.\n"
"-hi <source> 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;
}
syntax highlighted by Code2HTML, v. 0.9.1