/**************************************************************** * Metal Message System read message functions * * File: MEREAD.C * * Metal Message System and Z-Msg are Trademarked and * Copyright (c) 1984,1985,1986 Tim Gary * All Rights Reserved. * **************************************************************** * * 1.50xx 03/05/86 Allow read options in any order (MNQK...) * 1.50xx 03/05/86 Record time message was read if its to this user.. * 1.50xx 03/01/86 New routines for read. Tagged message and Mail support.. * 1.40xx 02/28/86 Modified references to msg[].reply to call msg_reply() * 1.40xx 02/10/86 Help stuff moved to METHELP library * 1.40xx 01/26/86 New format stuff. * 1.31a 10/13/85 Release version. New overlay method allows * overlay to overlay calling, with return, so read * routines were moved here to regain needed memory space. * Modify, write, and print message functions from * selective read act on PREVIOUS message in all cases. * 1.31xx 08/22/85 File created, for use with new overlay to * overlay support (one level).. * ****************************************************************/ #include "xpm.h" /* CPMIO header file for i/o operations.*/ #include "megen.h" /* general defines */ #include "meglob.h" /* global variable definitions */ #include "meovfn.h" /* overlay function numbers */ #include "mefiles.h" /* file names */ #include "ctype.h" #define READHELP "MSGREAD HLP" #define SELRDHELP "MSGSRD HLP" #define SELRDSOHELP "MSGSRDSOHLP" static unsigned temp_lmsg=0; /* so reply to a message entered during read is not shown */ ovmain(func,parm) int func; /* not used in this overlay */ unsigned parm; { readmsgs(parm); /* all that's here for now */ } /*************************** * help for read functions * ***************************/ read_help(mode) unsigned mode; { if (!(mode&READ_NEW)) ltype(METHELP,READHELP); if (mode & 0x8000) search_help(); } selrd_help() { if (ltype(METHELP,SELRDHELP)!=ERROR) if (user.status==SYSOP) ltype(METHELP,SELRDSOHELP); } /****************************************** * Read messages routine. * * Code is much clearer, and is not * * dependent on message structure. * ******************************************/ readmsgs(mode) unsigned mode; { int flag; /* flag for various things ERROR=abort */ int read_flag; /* flag for something read or not */ unsigned *msg_tbl,*ms_t; /* table of message numbers to read */ #ifdef MULTI_USER mu_update(); #endif if (!msgcount) { send("\nSorry, there are currently no messages posted.\n"); return; } novhelp(); if (strloc==0 && !(user.flags&EXPERT) ) read_help(mode); /* allocate space for selected msgs */ msg_tbl=malloc( mindex * sizeof(unsigned) ); /* * Loop the Loop... */ do { if (get_range(&mode,msg_tbl)==ERROR) { mode=0; /* clear to default mode(s) */ break; } read_flag=FALSE; /* nothing read yet...enable read loop */ if ((messages=open(MESSAGES,F_RD | F_UNLOCK))==NULL) { send("\nUnable to open messages file.\n"); read_flag=TRUE; /* Don't go through loop,.. error */ break; } temp_lmsg=lmsg; /* save lmsg before read (readm uses this) */ for (flag=FALSE, ms_t=msg_tbl; *ms_t && flag!=ERROR; ms_t++) { flag=readm(*ms_t,mode,FALSE); if (flag!=0) read_flag=TRUE; } if (!read_flag) send("\n[Nothing found. Message(s) either private or deleted.]"); close(messages); } while ( (flag != ERROR) && (mode & READ_ONE) ); free(msg_tbl); /* make sure we get back memory */ putchar('\n'); } /* readmsgs */ /************************************ * Get range function for read msgs * ************************************/ get_range(pmode,ms_t) unsigned *pmode,*ms_t; { register struct msg_ind *mfast; /* pointer to main message table */ char *tp; /* temp string pointer */ unsigned msgnum=0; /* current msg num */ unsigned orig_mode; /* copy of original mode*/ int sreq; /* search request flag */ orig_mode=(*pmode); /* save original mode */ do { /* silly do concept */ (*pmode)=orig_mode; /* make sure mode is reset */ setmem(buffer,MAXLINE,0); /* make sure this is empty!! */ /* see if READ_NEW or READ_MAI are set, no prompt if so */ if ( !( (*pmode) & (READ_NEW | READ_MAI) ) ) { do { if (user.flags&EXPERT) sprintf(buffer,"\nRead (first=%u, last=%u)? ",fmsg,lmsg); else sprintf(buffer,"\nRead which message (first=%u, last=%u, '?' for help)? ",fmsg,lmsg); ask(buffer,buffer,MAXLINE,UP); if (*buffer=='?') read_help(*pmode | 0x8000); } while (*buffer=='?'); } while (*buffer && index("KQNMT",*buffer)) { switch (*buffer) { case 'K': (*pmode)|=READ_KIL; /* Read Killed msgs */ break; case 'Q': (*pmode)|=READ_Q; /* Read quietly */ break; case 'N': (*pmode)|=(READ_NEW | READ_ONE); /* Read New */ break; case 'M': (*pmode)|=READ_MAI; /* Read mail to you */ break; case 'T': if (buffer[1]!=':') (*pmode)|=READ_TAG; /* Tagged */ break; } /* switch */ sprintf(buffer,"%s",buffer+1); /* trash char now.. */ } /* while */ if (strlen(buffer)>0) sreq=msearch(buffer); else sreq=0; if ( (*pmode) & READ_NEW ) msgnum=user.lastread+1; else { if (isdigit(*buffer)) { if ( ((msgnum=atoi(buffer))>lmsg) || (msgnum<0)) { if (*buffer!='\0') send("\n[Message out of range]\n"); strloc=0; /* clear this global pointer */ return ERROR; } } else if (sreq || ((*pmode) & (READ_MAI | READ_TAG)) ) { msgnum=1; /* and start at msg 1 */ *buffer='+'; } else if ( !((*pmode) & (READ_MAI | READ_TAG) ) ) return ERROR; } /* READ_NEW else test */ if (msgnum==0) ++msgnum; /* convert a zero to a one */ if (msgnum>lmsg) { send("\n[Message out of range]"); strloc=0; return ERROR; } /* Set direction acordingly... */ if ( ( (tp=index(buffer,'-')) < buffer+8) && tp ) (*pmode) = READ_REV | ( (*pmode) & (0xffff ^ READ_ONE) ); else if ( ( (tp=index(buffer,'+')) < buffer+8) && tp ) (*pmode)&= (0xffff ^ READ_ONE); /* Turn off READ_ONE */ /* All options and modes are reduced by now, so start setting * up messages in table */ /* if only one message, put it in table if found.. 0 otherwise */ if ( (*pmode) & READ_ONE ) { if (getindex(msgnum)!=ERROR) *(ms_t++)=msgnum; else { printf("\n[Message %u not found]",msgnum); continue; } break; } /* if read reverse simple loop from end of msg[] to beginning */ if ( (*pmode) & READ_REV ) { for (mfast=msg+(mindex-1); mfast>=msg; mfast--) { if (mfast->number && (mfast->number)<=msgnum) { *(ms_t++)=mfast->number; if ( ( (*pmode & READ_TAG) && !((mfast->flags)&TAGGED) ) || ( (*pmode & READ_MAI) && !((mfast->flags)&MAIL) ) ) ms_t--; } } /* for */ break; } /* It's not READ_ONE or READ_REV, has to be forward read. * NOTE: this ONLY places messages without parentsnumber && (mfast->number)>=msgnum) { if ( (*pmode) & (READ_TAG | READ_MAI) ) { if ( ( (*pmode&READ_TAG) && ((mfast->flags)&TAGGED) ) || ( (*pmode&READ_MAI) && ((mfast->flags)&MAIL) ) ) *(ms_t++)=mfast->number; /* place in table */ } else if ((mfast->parent)number; /* place in table */ } } /* for */ break; /* I know.. weird */ } while (1); if ( !((*pmode) & READ_SEL) && !(user.flags & EXPERT) ) send("\nControl-O skips to the next message.\n"); *ms_t=0; /* terminate table */ return NULL; /* done, ok */ } /* get_range */ /* This thing is recursive so the entire reply chain can be read */ readm(num,mode,flag) unsigned num; int mode,flag; { unsigned tnum; if (flag==ERROR) return ERROR; if ( (flag=rm(num,mode)) == ERROR) return flag; tnum=num; if ( !( mode & (READ_REV | READ_ONE | READ_TAG | READ_MAI) ) ) while ( (tnum=msg_reply(num,tnum)) && flag!=ERROR && tnum<=temp_lmsg) flag=readm(tnum,mode,flag); return flag; } extern char prevsender[],status; extern unsigned prevmsg; /* This routine displays the message, it's header, from current place in file * expects message number, and read mode given to it. * returns ERROR if break hit, or can't read a record * returns NULL if not addressed to user and private, or not selected * returns #of lines if read..... */ rm(msgnum,rd_mode) unsigned msgnum; int rd_mode; { int lns; unsigned saveseek; char str[3],ttt[80]; int c; unsigned ind; char tsender[NAMELEN+1]; ind=getindex(msgnum); /* this should always be passed correct valid number */ if (breakkey()) return ERROR; rd_mode&=(READ_KIL | READ_Q | READ_SEL); /* Silly loop to avoid goto.. Allows for single exit point */ do { setarec(messages,(saveseek=msg[ind].seek)); /* seek to message, save pos */ lns=msgheader(messages,0,2 | rd_mode); /* read message struct hdr */ status=message.status; if (lns==ERROR || lns==NULL) break; strcpy(tsender,message.sender); if (rd_mode & READ_SEL) /* selective read */ { if (user.status==SYSOP) send("[y/n/r/t/u/p/w/e/d/k/m/q]"); else if (user.flags&EXPERT) send("[Read? y/n/r/t/u/m/q/?]"); else send("[Read? Yes/No/Reply/Tag/Untag/Mod/Quit/Help]"); while ((c=toupper(getd()))<' '); /* ignore control chars */ send(" ["); if (c=='N') { send("no]\n"); break; } if (c=='Q') { send("quit]"); lns=ERROR; break; } if (prevmsg!=0) { if (c=='M') { send("modify]\n\n"); do_ov("[Modify PREVIOUS message header information (subject, etc..) (y/n)? ]",OVSTUFF,EDITMSG,prevmsg,0); continue; } if (c=='R') { send("reply]\n\n"); do_ov("[Reply to PREVIOUS message (y/n)? ]",OVSEND,0,prevmsg,0); continue; } if (user.status==SYSOP) { int flag; flag=TRUE; switch(c) { case 'K': send("kill]\n\n"); sprintf(ttt,"[Kill PREVIOUS message #%u (y/n)? ]",prevmsg); do_ov(ttt,OVKILL,KILL,prevmsg,0); break; case 'E': send("edit user]\n\n"); do_ov(0,OVUSER,EDITUSER,0,prevsender); break; case 'D': send("delete user]\n\n"); sprintf(ttt,"[Delete PREVIOUS sender (%s) from users file (y/n)? ]",prevsender); do_ov(ttt,OVUSER,DELETEUSER,0,prevsender); break; case 'P': case 'W': if (out_msg(c,prevmsg)) break; else continue; break; default: flag=FALSE; break; } /* switch */ if (flag) continue; /* go back and get header stuff */ } /* sysop test */ } /* previous message available */ if (c=='H' || c=='?') { send(" ? ]\n\n"); selrd_help(); /* selective read help */ continue; } if (c=='T') { send("tag]\n"); msg[ind].flags|=TAGGED; break; } if (c=='U') { send("untag]\n"); msg[ind].flags&=(0xff ^ TAGGED); break; } send("yes]\n\n"); } /* mode & READ_SEL */ /* else read the thing */ read(messages,1); /* read next record */ if (showmsg(lns)==ERROR) { lns=ERROR; break; } putchar('\n'); if (thisis(message.receiver)) { /* message was to this guy.. does he want to reply? */ if (message.date_read[0]==0x00) /* not tagged read yet */ { msg_record *tmp; if (O.RTC) readclock(); send("\n[Marking message read]\n"); movmem(date,message.date_read,3); movmem(time,message.time_read,2); setarec(messages,saveseek); movmem(&message,bufloc(messages),128); write(messages,0); /* write new header */ summary=open(SUMMARY,F_RW | F_UNLOCK); setarec(summary,((msg[getindex(message.number)].flags&SUMSEEK)<<5)); tmp=bufloc(summary); do read(summary,1); while (tmp->number!=msgnum); setrrec(summary,-1); movmem(&message,tmp,128); write(summary,0); close(summary); } /* message already tagged as read flag */ if (!(rd_mode & READ_Q)) { if (do_ov("\n[Reply to this msg (y/n)? ]",OVSEND,0,msgnum,0)==TRUE) sprintf(ttt,"\n[Kill Message you've just replied to (y/n)? ]"); else sprintf(ttt,"[Delete this Message (y/n)? ]"); do_ov(ttt,OVKILL,KILL,msgnum,0); putchar('\n'); } /* quiet read mode */ } break; /* continue gets by this crock */ } while (1); if (lns!=NULL && ((rd_mode & READ_KIL) || status!=DEADMSG)) { prevmsg=msgnum; strcpy(prevsender,tsender); } return lns; /* return lines.. */ } /* do overlay from selective read... special kill msg handling */ do_ov(atext,ovname,ovfn,uparm,cparm) char *atext,*ovname,*cparm; int ovfn; unsigned uparm; { int c; int ret_flag=YES; char t_glbstr[MAXLINE+1]; int t_strloc; strcpy(t_glbstr,glbstr+strloc); t_strloc=strloc; strloc=0; /* clear this so READ 123;456;789;1011 things work */ /* prompt and get single letter yes=y, no=other char */ if (atext) ret_flag=yes_no(atext); if (ret_flag) { if (!strcmp(OVKILL,ovname)) close(messages); /* killmsg req. */ if (cparm) ovovload(ovname,ovfn,cparm); /* do char overlay func */ else { ovovload(ovname,ovfn,uparm); /* do unn overlay func */ messages=open(MESSAGES,F_RD | F_UNLOCK); } putchar('\n'); } strloc=t_strloc; strcpy(glbstr+strloc,t_glbstr); /* restore this stuff */ return ret_flag; /* true=we did the overlay */ } /* This routine prints the message from file ffd, for nl lines */ showmsg(nl) register int nl; { register int lc; globalchar='\0'; /* clear this before and after showing msg! */ for (lc=0; lc