2.9BSD/usr/contrib/notes/readem.c
static char *sccsid = "%W%";
/*
* this particular collection of junk handles the basic idea
* of what to do when you are showing a note.
* It displays the note, and then manages to collect enough info
* from the terminal to either progress to the next note or
* show some of the responses.
*
* original author : rob kolstad
* modified : ray essick
* yet again : lou salkind
*/
#include "parms.h"
#include "structs.h"
#include "newsgate.h"
#include <signal.h>
#include <sgtty.h>
readem(io, readnum, firstdis, resp)
struct io_f *io;
int *firstdis;
{
struct note_f note;
struct resp_f rsprec;
struct io_f io2;
struct when_f whendump;
FILE *txtfile;
FILE *ptxtfile;
FILE *pipeopen();
int rrecnum, roffset;
char tonf[NNLEN + 1]; /* for forwarding */
char ntitle[TITLEN + 1]; /* scratch space */
char author[NAMESZ + 1];
char system[SYSSZ + 1];
char cmdline[CMDLEN]; /* build-a-command */
char tmpfile[CMDLEN + 1];
int c; /* input char */
int forward; /* scroll forward/backward on deleted note */
int toauth; /* send to author */
int znum; /* forward as resp to this note */
int znote, zresp; /* scratch for asearch */
int i, j;
int wtext; /* send mail with text */
int retcode;
int retstat;
struct notesenv oldenv;
char *delfile;
extern char *myshell;
retcode = -1; /* init so grabs character */
retstat = 0; /* default return code */
forward = 1; /* default to scroll forward */
ignsigs = 0;
delfile = NULL;
txtfile = NULL;
replot = 1;
oldenv = curenv;
if (setjmp(jenv)) {
/* KLUDGE */
if (txtfile) {
fclose(txtfile);
txtfile = NULL;
}
if (delfile) {
unlink(delfile);
delfile = NULL;
}
}
while (1) {
x (readnum < 0, "readem: given bad readnum");
if (readnum > io->descr.d_nnote) {
/* HMM... THIS SHOULD REALLY BE AN ERROR */
readnum = io->descr.d_nnote;
}
if (readnum == 0 && io->descr.d_plcy == 0)
goto out; /* empty notesfile */
getnrec(io, readnum, ¬e);
if (note.n_stat & DELETED) {
if (forward)
goto nextnt; /* forward scroll */
else
goto prevnt; /* backward scroll */
}
if (replot) {
replot = 0; /* set later if want replot */
if (resp) {
/* response */
if (resp > note.n_nresp)
resp = note.n_nresp; /* set to the end */
if (lrsp(io, readnum, resp, &rsprec, &roffset,
&rrecnum) == -1) {
/* bad response chain - print note instead */
resp = 0;
getnrec(io, readnum, ¬e);
retcode = dspnote(io, ¬e, readnum);
} else
retcode = dspresp(io, ¬e, &rsprec, roffset,
resp, readnum);
} else
/* show the note if we need new one */
retcode = dspnote(io, ¬e, readnum);
}
forward = 1;
if (retcode < 0) {
cmdprompt(); /* no previous command */
c = gchar();
} else {
c = retcode; /* previous command */
retcode = -1;
}
switch (c) {
case 'z': /* update sequencer and exit, RLS */
retstat = QUITUPD;
goto out;
case 'u': /* unsubscribe from this notesgroup, RLS */
if (unsubscribe(io->nf) < 0)
continue;
retstat = QUITSEQ;
goto out;
case '?': /* if he doesn't know what to type */
case 'h':
help(RDMHLP); /* print the pseudo-man page */
continue;
case 'D': /* delete this note/response */
if (resp) {
/* check to see if his note */
if ((rsprec.r_auth[roffset].aid & UIDMASK) != globuid && (allow(io,DRCTOK) == 0)) {
warn("Not your response");
continue;
}
} else {
if ((note.n_auth.aid & UIDMASK) != globuid &&
(allow(io,DRCTOK) == 0)) {
warn("Not your note");
continue;
}
if (readnum == 0) {
warn("Use 'Z' to delete policy");
continue;
}
}
prompt("Delete? ");
if (askyn() == 'n')
continue;
lock(io, 'n');
/* this should catch most */
getnrec(io, readnum, ¬e);
/* and an up to date descriptor */
getdscr(io, &io->descr);
if (resp) { /* go about deleting it */
if (resp == note.n_nresp &&
inorder(&io->descr.d_lstxmit,
&rsprec.r_when[roffset])) {
delresp(io, readnum, rrecnum, roffset,
0);
/* adjust note response count */
note.n_nresp--;
unlock(io, 'n');
/* show next response */
replot = 1;
continue;
}
warn("Can't delete: networked, or not last response");
unlock(io, 'n');
continue;
}
/* it's a note */
if (note.n_nresp ||
inorder(¬e.n_date, &io->descr.d_lstxmit)) {
warn("Can't delete; note has responses or is networked");
unlock(io, 'n');
continue;
}
delnote(io, readnum++, 0);
resp = 0;
unlock(io, 'n');
replot = 1;
continue;
case 'E': /* edit an article */
if (resp) { /* check to see if his note */
if ((rsprec.r_auth[roffset].aid & UIDMASK) != globuid && (allow(io,DRCTOK) == 0)) {
warn("Not your response");
continue;
}
} else {
if ((note.n_auth.aid & UIDMASK) != globuid && (allow(io,DRCTOK) == 0)) {
warn("Not your note");
continue;
}
if (readnum == 0) {
warn("Sorry, E doesn't work for policy notes yet");
continue;
}
}
lock(io, 'n');
getnrec(io, readnum, ¬e);
/* this should catch most */
getdscr(io, &io->descr);
/* and an up to date descriptor */
if (resp) { /* go about deleting it */
if (resp == note.n_nresp &&
inorder(&io->descr.d_lstxmit,
&rsprec.r_when[roffset])) {
delresp(io, readnum, rrecnum, roffset, 0);
/* adjust note response count */
note.n_nresp--;
unlock(io, 'n');
sprintf(tmpfile , "/tmp/nfe%d",
getpid());
delfile = tmpfile;
/* build scr file */
x ((txtfile = fopen(tmpfile, "w")) == NULL, "readem: scrfile");
x (chmod(tmpfile, 0666) < 0,
"readem: chmod");
pageout(io, &rsprec.r_addr[roffset],
txtfile);
/* dump it */
fclose(txtfile); /* also flushes it */
x ((txtfile = fopen(tmpfile, "r")) == NULL, "readem: edit reopen");
resp = addresp(io, txtfile, readnum);
fclose(txtfile);
txtfile = NULL;
/* up to date */
getnrec(io, readnum, ¬e);
/* add it back in ! */
x (unlink(tmpfile) < 0,
"readem: edit unlink");
delfile = NULL;
continue; /* show next response */
}
warn("Can't edit: networked, or not last response");
unlock(io, 'n');
continue;
}
/* its a note */
if (note.n_nresp ||
inorder(¬e.n_date, &io->descr.d_lstxmit)) {
warn("Can't edit; note has responses or is networked");
unlock(io, 'n');
continue;
}
delnote(io, readnum++, 0);
resp = 0;
unlock(io, 'n');
sprintf(tmpfile, "/tmp/nfe%d", getpid());
delfile = tmpfile;
/* build scr file */
x ((txtfile = fopen(tmpfile, "w")) == NULL,
"readem: scrfile");
x(chmod(tmpfile, 0666) < 0, "readem: chmod");
pageout(io, ¬e.n_addr, txtfile);
/* dump it */
fclose(txtfile); /* also flushes it */
x((txtfile = fopen(tmpfile, "r")) == NULL,
"readem: edit reopen");
znum = addnote(io, txtfile, "Edit note text:",
"Note title: ");
fclose(txtfile);
txtfile = NULL;
x(unlink(tmpfile) < 0, "readem: edit unlink");
delfile = NULL;
if (znum > 0)
readnum = znum; /* this is the one */
continue;
case 'Z': /* zap notes/responses - directors only */
/* kills any note/response */
getdscr(io, &io->descr); /* up to date descriptor */
if (allow(io, DRCTOK) == 0) {
warn("Not a director");
continue;
}
prompt("Delete? ");
if (askyn() == 'n')
continue;
/*
* should log the deletion here, so the
* "meta-director" can watch for fascist
* directors preying on the peasants.
*/
if (readnum == 0) { /* deleting policy */
lock(io, 'n');
getdscr (io, &io->descr); /* grab up-to-date */
io->descr.d_plcy = 0; /* its gone now */
/* replace descriptor */
putdscr (io, &io->descr);
unlock(io, 'n');
goto out;
}
if (resp) { /* delete a response */
delresp(io, readnum, rrecnum, roffset, 1);
/* kill it */
note.n_nresp--; /* and response count */
} else
delnote(io, readnum++, 1);
replot = 1;
continue;
case 'r': /* replot the current note/response */
case '\014': /* everyone else uses ^L, might as well */
replot = 1;
continue;
case '\r': /* wants the next note */
case '\n':
goto nextnt;
case 'm': /* mail a note/response via Unix mail */
toauth = 0;
wtext = 0; /* to others and no text */
goto sendmail;
case 'M': /* same as 'm' but with text */
toauth = 0;
wtext = 1; /* to others with text */
goto sendmail;
case 'P':
toauth = 1;
wtext = 1; /* to author with text */
goto sendmail;
case 'p':
toauth = 1;
wtext = 0; /* to author, no text */
sendmail:
/* jump to here once set mail parms */
if (resp) {
mailit(io, &rsprec.r_addr[roffset],
&rsprec.r_auth[roffset],
&rsprec.r_id[roffset],
&rsprec.r_when[roffset], io->nf, toauth,
wtext);
} else
mailit(io, ¬e.n_addr, ¬e.n_auth,
¬e.n_id, ¬e.n_date, io->nf, toauth,
wtext);
continue;
case '!': /* wants to fork a shell */
gshell();
continue;
case 'q': /* quit this, maybe whole system */
case 'k':
retstat = QUITSEQ;
goto out;
case '\04': /* control D */
retstat = QUITFAST;
goto out;
case 'Q': /* exit system without updating sequencer */
case 'K':
retstat = QUITNOSEQ;
goto out;
case 'i': /* go back to note index */
*firstdis = readnum;
goto out;
case '\010':
case '-': /* display previous response */
resp--;
if (resp >= 0) {
replot = 1;
continue; /* show the base note */
}
goto prevnt;
case 'x':
case 'X':
if (readnum == 0)
goto out;
retcode = tsearch(io, readnum - 1, c == 'x');
/* look it up */
if (retcode > 0) {
readnum = retcode;
resp = 0;
replot = 1;
}
continue;
case 'a':
case 'A': /* author search from current spot */
if (readnum == 0)
goto out;
znote = readnum;
zresp = resp;
if (zresp == 0)
znote--;
else
zresp++; /* select 'next' */
if (asearch(io, &znote, &zresp, (c == 'a')) > 0) {
readnum = znote;
resp = zresp; /* set returned values */
/* grab right descriptor */
getnrec(io, readnum, ¬e);
replot = 1;
}
continue;
case 'd': /* toggle a notes director status */
if (allow (io, DRCTOK) == 0) {
prompt("Anonymous: %s Networked: %s",
(io->descr.d_stat & ANONOK) ? "YES" : "NO",
(io->descr.d_stat & NETWRKD) ? "YES" : "NO");
continue;
}
if (resp == 0) { /* toggle a note */
lock(io, 'n');
getnrec (io, readnum, ¬e);
if (note.n_stat & DIRMES)
note.n_stat &= NOT DIRMES;
else
note.n_stat |= DIRMES;
putnrec (io, readnum, ¬e); /* replace */
unlock(io, 'n');
replot = 1;
continue;
}
/* toggle a response */
lock(io, 'n');
/* grab that block */
getrrec(io, rrecnum, &rsprec);
if (rsprec.r_stat[roffset] & DIRMES)
rsprec.r_stat[roffset] &= NOT DIRMES;
else
rsprec.r_stat[roffset] |= DIRMES;
putrrec (io, rrecnum, &rsprec); /* replace */
unlock(io, 'n');
replot = 1;
continue;
case 'e': /* allow him to edit his title */
if (readnum == 0)
continue; /* don't touch */
if (resp) {
warn("Not reading note");
continue;
}
if (allow (io, DRCTOK) == 0 &&
/* check uid */
(globuid != (note.n_auth.aid & UIDMASK) ||
strcmp (SYSTEM, note.n_id.sys) != 0)) {
/* other sys */
warn("Not your note");
continue;
}
prompt("New Title: ");
if ((i = gline(ntitle, TITLEN)) == 1)
/* empty title, leave alone */
continue;
lock(io, 'n');
getnrec (io, readnum, ¬e); /* well, update it */
for (j = 0; j < i - 1 && j < TITLEN; j++)
note.ntitle[j] = ntitle[j];
for (; j < TITLEN; j++)
note.ntitle[j] = ' '; /* space fill */
putnrec (io, readnum, ¬e); /* and replace */
unlock(io, 'n');
replot = 1;
continue;
case 'F': /* change author and system, RLS */
if (allow(io, DRCTOK) == 0) {
warn("Not a director");
continue;
}
prompt("New Author: ");
if ((i = gline(author, NAMESZ)) == 1)
continue;
prompt("New System: ");
if ((i = gline(system, SYSSZ)) == 1)
continue;
lock(io, 'n');
if (resp == 0) {
/* well, update it */
getnrec(io, readnum, ¬e);
strcpy(note.n_auth.aname, author);
strcpy(note.n_from, system);
strcpy(note.n_id.sys, system);
note.n_auth.aid = 0;
putnrec(io, readnum, ¬e); /* and replace */
} else {
/* grab that block */
getrrec (io, rrecnum, &rsprec);
strcpy(rsprec.r_auth[roffset].aname, author);
strcpy(rsprec.r_from[roffset], system);
strcpy(rsprec.r_id[roffset].sys, system);
rsprec.r_auth[roffset].aid = 0;
putrrec(io, rrecnum, &rsprec); /* replace */
}
unlock(io, 'n');
replot = 1;
continue;
case 't': /* talk to the author of a note */
if (resp)
replot = talkto(&rsprec.r_auth[roffset],
&rsprec.r_id[roffset]);
else
replot = talkto(¬e.n_auth, ¬e.n_id);
continue;
case 'W': /* write a response with the text */
case 'w': /* let him write a response */
getdscr(io, &io->descr); /* get up to date */
if (allow(io, RESPOK) == 0) {
warn("Sorry, you are not allowed to write");
continue; /* back to key processing */
}
if (readnum == 0) {
warn("No responses allowed to policy note");
continue;
}
if (c == 'w') {
txtfile = NULL; /* no preface text */
} else {
sprintf(tmpfile, "/tmp/nfx%d", getpid ());
delfile = tmpfile;
x((txtfile = fopen(tmpfile, "w")) == NULL,
"readem: bad scrfile");
x(chmod(tmpfile, 0666) < 0,
"readem: chmod failed");
if (resp) {
preptxt(io, txtfile, io->nf,
&rsprec.r_auth[roffset],
&rsprec.r_id[roffset],
&rsprec.r_when[roffset],
&rsprec.r_addr[roffset]);
} else {
preptxt(io, txtfile, io->nf,
¬e.n_auth, ¬e.n_id,
¬e.n_date, ¬e.n_addr);
}
fclose(txtfile);
x ((txtfile = fopen(tmpfile, "r")) == NULL,
"readem: reopen");
}
zresp = addresp(io, txtfile, readnum); /* put it in */
if (zresp > 0) {
/* update descriptor */
getnrec (io, readnum, ¬e);
}
if (txtfile != NULL) {
fclose(txtfile); /* toss out scratch */
txtfile = NULL;
x (unlink(tmpfile) < 0,
"readem: couldnt unlink scratch");
delfile = NULL;
}
if (zresp)
resp = zresp; /* show the new */
if (zresp == 0)
continue;
if ((io->descr.d_stat & NETWRKD) == 0) {
/*
prompt("Not networked");
fflush(stdout);
sleep(2);
*/
continue;
}
#ifdef NEWS
prompt("Send to news? ");
if (askyn() == 'y') {
#ifdef DEMANDNEWS
/* send it to the news */
sprintf(cmdline, "%s/%s/newsoutput", MSTDIR,
UTILITY);
dounix(0, 0, cmdline, io->nf, 0, 0, 0);
#endif DEMANDNEWS
} else {
/* don't send it to the network */
gettime(&whendump);
fixlast(&whendump, io->nf, 1 , NEWSSYS);
}
#endif NEWS
continue;
case 'B': /* bitch, bitch, bitch */
/* check gripe file */
if (init(&io2, GRIPES) < 0) {
warn("No gripe file");
continue;
}
addnote(&io2, NULL, "Edit Gripe text:",
"Gripe Header: ");
/* let him put the note in */
finish(&io2); /* close up the gripe file */
continue;
case 'C': /* copy to other notefile with text */
case 'c': /* copy to other notefile without text */
if (c == 'C')
wtext = 1;
else
wtext = 0; /* determine which */
prompt("Forward to: ");
if (gline(tonf, NNLEN) == 1)
continue;
if (init(&io2, tonf) < 0) {
warn("Can't find notesfile %s", tonf);
continue;
}
if (wtext == 0) {
txtfile = NULL;
} else {
sprintf(tmpfile, "/tmp/nfx%d", getpid());
delfile = tmpfile;
x ((txtfile = fopen(tmpfile, "w")) == NULL,
"readem:creat scratch failed");
x (chmod(tmpfile, 0666) < 0,
"readem: chmod failed");
if (resp) {
preptxt(io, txtfile, io->nf,
&rsprec.r_auth[roffset],
&rsprec.r_id[roffset],
&rsprec.r_when[roffset],
&rsprec.r_addr[roffset]);
} else {
preptxt(io, txtfile, io->nf,
¬e.n_auth, ¬e.n_id, ¬e.n_date,
¬e.n_addr);
}
fclose(txtfile); /* close it */
x ((txtfile = fopen(tmpfile, "r")) == NULL,
"readem: couldnt reopen");
}
c = 'n'; /* default to note */
if (allow(&io2, WRITOK) && allow (&io2, READOK) &&
allow (&io2, RESPOK)) {
prompt("Forward as response? ");
c = askyn();
}
if (c == 'n' && allow(&io2, WRITOK)) {
addnote(&io2, txtfile, "Edit forwarded text:",
"Forwarded Title: ");
} else if (c == 'y') {
if (znum = limindx (&io2)) {
addresp (&io2, txtfile, znum);
}
} else
warn ("You haven't permission");
if (strcmp(io->nf, io2.nf) == 0) {
/* if was this notefile */
/* get new descriptor */
getdscr (io, &io->descr);
}
finish(&io2); /* close up that notefile */
if (txtfile != NULL) {
fclose(txtfile); /* throw it away */
txtfile = NULL;
x (unlink(tmpfile) < 0,
"readem: couldnt unlink scratch");
delfile = NULL;
}
continue;
case 'n': /* nest notesfiles - a stack */
prompt("New notesfile: ");
if (gline(tonf, NNLEN) == 1)
continue;
closenf(io); /* save fids */
i = control(tonf, NOSEQ);
if (opennf(io, io->nf) < 0) {
warn("Couldn't reopen notesfile");
wfchar();
retstat = QUITNOSEQ;
goto out;
}
if (i == QUITBAD || i == QUITNEX) {
warn("Can not open notesfile `%s'", tonf);
continue;
}
if (i == QUITFAST || i == QUITUPD) {
retstat = i;
goto out;
}
replot = 1;
continue;
case 's': /* place text at end of 'nfsave' */
case 'S': /* place the whole string */
case '|': /* pipe into command */
case '^': /* whole string into command */
case '%': /* joke translator */
switch (c) {
case 's':
case 'S':
prompt("File name: ");
if ((znum = gline(tmpfile, CMDLEN)) == 1)
continue;
prompt("Saving...");
sprintf(cmdline, "cat >> %s", tmpfile);
break;
case '|':
case '^':
prompt("Command: ");
if ((znum = gline(cmdline, CMDLEN)) == 1)
continue;
printf("\nStarting up...\n");
break;
case '%':
prompt("Translation...");
putchar('\n');
strcpy(cmdline, "tr 'A-Za-z' 'N-ZA-Mn-za-m'");
break;
}
fflush(stdout);
if ((ptxtfile = pipeopen(cmdline, "w")) == NULL) {
warn("pipe open failed");
continue;
}
if (c == 'S' || c == '^') {
/* save whole string */
znum = savnote(io, ptxtfile, ¬e);
for (i = 1; i <= note.n_nresp; i++) {
if (lrsp(io, readnum, i, &rsprec,
&roffset, &rrecnum) == -1)
/* hit end of chain */
continue;
znum += savresp(io, ptxtfile, &rsprec,
roffset);
}
} else {
/* save single page */
if (resp)
znum = savresp(io, ptxtfile, &rsprec,
roffset);
else
znum = savnote(io, ptxtfile, ¬e);
}
i = pipeclose();
if (i == 0 && (c == 's' || c == 'S')) {
prompt("Saved %d lines", znum);
replot = 0;
} else
wfchar();
continue;
case 'j': /* go on to next note/resp */
case 'l': /* universal seq, RLS */
if (readnum == 0)
goto out;
if (resp != note.n_nresp) {
if ((resp = nxtresp(io, readnum, resp,
&io->stime)) > 0) {
replot = 1;
continue;
}
}
/* fall into ... */
case 'L':
case 'J': /* next unread note */
if (readnum == 0)
goto out;
resp = 0;
if ((readnum = nxtnote(io, readnum, &io->stime)) > 0) {
replot = 1;
continue;
}
if (c == 'l' || c == 'L')
retstat = QUITSEQ;
else
/* put him on last page */
*firstdis = io->descr.d_nnote;
goto out;
case '+':
case ';':
case ' ':
if (readnum == 0)
/* such is the fate of policy notes */
goto out;
resp++;
if (resp > note.n_nresp)
goto nextnt;
replot = 1;
continue;
case '=': /* go back to the base note */
if (resp == 0) {
warn("Already on base note");
continue;
}
resp = 0; /* reset index into responses */
replot = 1;
continue;
case '1': /* skip n responses */
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (note.n_nresp < 1)
goto nextnt;
/* let him skip all over responses */
resp += c - '0';
replot = 1;
continue;
default: /* something we haven't covered */
/* so can jump down here */
warn("q to quit, ? for help");
continue;
}
nextnt:
if (readnum == 0)
goto out;
if (++readnum > io->descr.d_nnote) {
*firstdis = io->descr.d_nnote;
goto out;
}
resp = 0; /* reset response index */
replot = 1;
continue;
prevnt:
/* display previous note */
if (readnum == 0)
goto out;
/* set to scroll backwards on deleted note */
forward = 0;
if (--readnum < 1) {
/* zero is policy, so stop at 1 */
readnum = 1;
forward = 1; /* bounce off bottom end */
}
resp = 0; /* was else resp = 0; */
replot = 1;
continue;
}
out:
ignsigs++;
curenv = oldenv;
ignsigs = 0;
return(retstat);
}