#include #include #include "rqfile.h" #include "init_rec.h" #include "send_cmd.h" #define loop for(;;) #define true 1 #define false 0 #define USRBRK 02000 #define RAW 040 #define ECHO 010 #define SCOPE 0400 #define SMALLBUFLEN 256 #define TABSIZE 8 #define controla 001 #define controlb 002 #define controlc 003 #define controld 004 #define controle 005 #define controlf 006 #define controlh 010 #define controli 011 #define controlq 021 #define controlr 022 #define controls 023 #define controlu 025 #define controlv 026 #define controlw 027 #define controlx 030 #define controlz 032 #define escape 033 #define del 0177 #define abortchar 034 #define SIGSPC 16 struct status { char s_minor; char s_major; int s_number; int s_mode; char s_nlinks; char s_uid; char s_gid; char s_size0; int s_size1; int s_addr[8]; int s_actime[2]; int s_modtime[2]; } statb; char *weekdays[] { "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday", "Saturday" }; int pid; int pwfd; int tmpfd; int fd; int namefd; int mbfd; char bigbuf[512]; char pwbuf[512]; char *pwptr; int eomem; int pwcount; int netmsgs; char cc[2*SMALLBUFLEN],to[SMALLBUFLEN],subject[SMALLBUFLEN]; char irt[SMALLBUFLEN],bcc[SMALLBUFLEN]; char user[SMALLBUFLEN]; char answer[10]; char dbuf[64]; char tempfile[64],mailbox[64],from[64],netfile[64], frwdfile[64]; char realname[64]; /* sender's "real" name */ char reqnam[NAMSIZ]; char inbuf[512]; int incount; char *inptr; char filename[50]; char *letter,*date; int allnet 0; int ttyraw[] = { T_SET | T_FLAGS | T0_RAW, T_CLEAR | T_FLAGS | T0_ECHO, -1 }; int ttynorm[] = { T_CLEAR | T_FLAGS | T0_RAW, T_SET | T_FLAGS | T0_ECHO, -1 }; int ttystat[] = { T_GET | T_CANO, -1 }; int oldmode,uid,gid,old_signal,myuid; int unsent false; int file false; int oldfile false; struct init_rec init_rec = { FALSE, TRUE, FALSE, TRUE, ".mail", "mbox", "/bin/emacs", FALSE, "" }; int scopemode; int fflag false; int frflag false; char clrscr[] = "\033H\033J"; char clreol[] = "\033K"; char upline[] = "\033A"; int *old_INT; int *old_QUIT; int *old_TERM; int *old_SPC; char *arpadate(); /* name: send function: A message sending program modeled after the tenex program sndmsg. parameters: Optional, a list of recipients -R -S -F -f returns: Delivers mail by generating a mail request to the mail daemon. history: Originally written by someone at Berkeley (Murray Bowles?) Reorganized and augmented by Mark Kampe 10/75 Added access checks for include files, and distribution-list files 3/27/77 allen stougton. Added -R and -S options (mark kampe, 11/77) Modified for table-driven net software by Lauren Weinstein, 2/6/78 Modified to do all echoing in program so that control chars don't echo. (csk 5/30/78) Modified to add sender's "real" name to "From" field when available, Lauren Weinstein, 3/12/79 Modified to put day of week in "Date" field, Lauren Weinstein, 3/16/79 Modified exec of net mailer to pass username of sender to the mailer program. This is to allow the mailer to selectively deliver messages ONLY from the sender when he/she answers "yes" to the "Send network mail now?" question. Lauren Weinstein, 6/27/79 Modified to change format of "In Reply To" to "In-reply-to" to match the new "msg" format. Lauren Weinstein, 7/1/79 Modified 7/20/79 (Happy Moon Day) to put groupid on .author.copy. Modified 2/8/81 to run with the Delphi UNIX terminal handler, and to use Delphi UNIX terminal control character, Larry Allen Modified 3/1/81 to use the new tftp mail daemon..., Larry Allen Modified 7/7/81 to add message forwarding, Larry Allen Modified 4/4/82 to allow header modifications, Larry Allen */ main(argc,argv) char *argv[]; int argc; { register char *p; char *pp; register char *s; register int i; int vector[2]; char initfile[NAMSIZ]; int ifd; char cano; char tempnet[NAMSIZ]; int rqfd; pid = getpid (); incount = 0; if ((pwfd = open("/etc/passwd",0)) < 0) { printf("Couldn't open password file!\n"); exit(); } getname(from, initfile); stcat ("/", initfile); stcat (INITFILE, initfile); if ((ifd = open (initfile,0)) >= 0) { /* if we can't read it, tough */ read (ifd, &init_rec, sizeof (init_rec)); close (ifd); } time(vector); date = arpadate(vector); p = to; for(i = 1; i < argc; i++) { s = argv[i]; if (*s == '-') { if (*++s == 'R') /* In reply to */ { s++; pp = irt; while(*pp++ = *s++); continue; } if (*s == 'S') /* initial subject */ { s++; pp = subject; while(*pp++ = *s++); continue; } if (*s == 'F') /* mail a file */ { s++; pp = filename; while (*pp++ = *s++); fflag++; continue; } if (*s == 'f') /* include forwarded message */ { s++; pp = frwdfile; while (*pp++ = *s++); frflag++; continue; } printf("Bad flag, %s\n", argv[i]); } else { while( *s ) *p++ = *s++; *p++ = ','; } } if (fflag) { *(p-1) = '\0'; input (fflag); if ((rqfd = netinit (tempnet)) < 0) { printf ("can't open request file\n"); exit (1); } send (to,rqfd); quereq (rqfd); close (tmpfd); unlink (tempfile); exit (0); } /* Change for new console handler, is a gtty for old one */ ttymod (0, ttystat); if ((cano = ttystat[0] & 0377) == TC_VT52 || cano == TC_GT40 || cano == TC_CRT || cano == TC_H19) scopemode = true; else scopemode = false; /* and I don't know what you should do about the scopemode bit */ if (init_rec.in_blkscr && scopemode) printf ("%s", clrscr); if (p == to) { printf("To: "); gather(to); } else *--p = '\000'; if (*to == '\000') { printf("No destination?\n"); exit(); } p = cc; printf("cc: "); p = gather(p); if (init_rec.in_ccme) { if (p != cc) *p++ = ','; pp = from; while (*p++ = *pp++); } printf("Subject: "); for(p = subject; *p; p++); if (p != subject) { write(1, subject, p-subject); printf ("\n"); } else gather(p); input(0); if ((rqfd = netinit (tempnet)) < 0) { printf ("can't open request file\n"); exit (1); } send(to, rqfd); send(cc, rqfd); send(bcc, rqfd); quereq (rqfd); close(tmpfd); unlink (tempfile); } /* name: gather function: read a single line of console input into the user specified buffer. Currently standard unix editing is in effect, but this will later be changed to use the same sexy editing as does the message inputting routine. algorithm: Read a character: If it is an unescaped new line, null terminate the buffer and return. Otherwise, stash the character into the buffer. If the length of the buffer has been reached, echo a new line and return. parameters: *char pointer to user buffer of length SMALLBUFLEN returns: pointer to null at end of gathered message globals: SMALLBUFLEN calls: getchar slashc called by: main send history: Recoded by Mark Kampe 11/22/75 */ gather(sp) char *sp; { register char c; register char *p; p = sp; while( ((c = getchar(0)) != '\n') && (c>0) ) { if (c == '\\') *p++ = slashc(); else *p++ = c; if ((p - sp) >= SMALLBUFLEN) break; } *p = '\000'; return( p ); } /* name: writef function: Write a null terminated string algorithm: Determine the length of the string. Write it out. CAVEAT This call is not buffered, so use it carefully or pay the cost of high I/O expense. It was writted to decrease subroutine linkage global variable referencing and module size. But those wins can easily be lost if writef is consistantly called with small strings. parameters: int file descriptor of output file. *char pointer to null terminated string. returns: int value returned by write. globals: calls: write (system) called by: lotsa people history: Initially coded by Mark Kampe 11/22/75 */ writef(afd,str) int afd; char *str; { register int count; register char *s; s = str; for(count=0; *s++; count++); return(write(afd,str,count)); } /* name: input function: To accept the text of the message while doing all of the sexy sndmsg type editing. algorithm: Allocate a huge buffer. Read characters (editing sexily as I go) If I get an escape, go off to command level Return when I get a ctl-d or ctl-z, or command level returns parameters: none returns: With a null terminated letter in the string pointed to by the global variable letter. globals: letter address of the buffer. calls: rawmode normal writef write system printf getchar command sbreak system ttymod system access system called by: main history: Originally coded by someone at Berkeley Fixed up and augmented by Mark Kampe. Changed for scope mode and user interrupts by Mark Kampe File input routine modified by Lauren Weinstein, 2/6/78 Changed "In Reply To" to "In-reply-to" to match new "msg" format. Lauren Weinstein, 7/1/79 Changed to use "ttymod" system call for Delphi UNIX. Larry Allen, 2/8/81 Hacked up so it no longer needs to run as superuser, Larry Allen, 2/10/81 Hacked to add 'edit' feature, Larry Allen 2/12/81 Hacked to add command level, Larry Allen 4/4/82 */ input(fflag) int fflag; { char *tp; register int c; register char *p; register int i; int slashflag; int editflag = false; int pid; p = letter = sbrk(1000); /* Tried to avoid this! */ eomem = letter+1000; if (fflag) { if ((fd = open (filename, 0)) < 0) exit (1); if ((p = getfil (fd, p)) == -1) goto comnd; goto out; } rawmode(); printf("Type letter; ^Z to send or to go to command level:\n-------\n"); loop { slashflag = 0; c = getchar(0); if (p >= eomem) { if (sbrk(1000) == -1) { /* out of memory? */ printf ("message too large\n"); goto comnd; } else eomem =+ 1000; } switch (c) { case controla: case controlh: case del: /* character delete */ if (p == letter) { writef( 1 , "\007" ); continue; } p--; if (scopemode) { if (*p == '\n') writef( 1, upline); if (*p < '\040') { /* control character... */ *p = '\0'; write (1, "\r", 1); tp = p; while((tp > letter) && (*--tp != '\n')); if (*tp == '\n') tp++; writef (1, tp); writef (1, clreol); } else writef(1, "\b \b"); } else { write(1 , "\\" , 1); write(1 , p , 1); } continue; case controlb: /* input a file at this point */ case controlf: printf("\nInput file: "); normal(); gather(filename); if ((*filename == 0) || (access(filename, 04)<0) || ((fd = open(filename,0))<0)) printf("Couldn't open %s\n",filename); else if ((p = getfil (fd, p)) == -1) goto comnd; printf("%s has been included\n", filename); rawmode (); continue; case controlc: /* breakout */ normal(); exit(); case controle: /* edit current message */ editflag = true; goto comnd; case controld: /* normal end of message */ case -1: /* or end of file... */ case controlz: if ((p == letter) || (*(p-1) != '\n')) { *p++ = '\n'; printf("\n"); /* Guarantee msg ends with a new line */ } *p = '\000'; printf("-------\n"); goto out; case escape: /* escape to command level */ goto comnd; case controlr: /* retype the current line */ *p = '\000'; tp = p; while((tp > letter) && (*--tp != '\n')); if (*tp == '\n') tp++; write( 1 , "\n" , 1); writef( 1 , tp ); continue; case controls: /* retype entire message */ *p = '\000'; printf("\n-------\n"); writef(1,letter); continue; case controlw: /* back up over the last word typed */ --p; while ((p >= letter) && ( (*p == ' ') || (*p == '\t') || (*p == '\n') || (*p == ',') ) ) { if (scopemode && *p == '\n') writef (1, upline); p--; } while((p >= letter) && ((*p != ' ') && (*p != ',') && (*p != '\n') && (*p != '\t') ) ) p--; *++p = '\000'; if (scopemode == 0) write(1,"<-",2); else { write (1, "\r", 1); tp = p; while((tp > letter) && (*--tp != '\n')); if (*tp == '\n') tp++; writef (1, tp); writef (1, clreol); } continue; case controlx: case controlu: /* delete the last input line */ while( ( p > letter ) && (*(p-1) != '\n') ) p--; if (scopemode) { writef (1, "\r"); writef (1, clreol); } else writef( 1 , "XXX\n" ); continue; case abortchar: case controlq: /* sigh */ normal(); exit (); case controlv: case '\\': c = slashc(); case controli: /* this is a cludge for tab */ /* this code must be here */ slashflag = 1; default: if ( (c != '\n') && (!slashflag) && (c < 32) ) continue; *p = c; write( 1, p, 1 ); p++; } /* end switch */ } /* end of while loop */ comnd: /* Go to command level. This may result in header fields or letter */ /* being changed. Returns ready to do the send. */ normal(); command (editflag); out: /* restore console modes and make the tempfile */ normal(); tmpfile(tempfile); /* get a filename */ if ((tmpfd = creat(tempfile,0600)) < 0) { printf("Unable to create %s\n", tempfile); exit(1); } present_data(tmpfd); close(tmpfd); /* whole message is now in temp file */ /* reopen it for input */ tmpfd = open(tempfile,0); } /* end of input */ /* Get contents of a file into input buffer */ char *getfil (fd, p) int fd; char *p; { int i; sbrk(512); eomem =+ 512; for(i=read(fd,p,512); i>0; i=read(fd,p,512)) { p =+ i; if (sbrk(512) == -1) { printf ("message too large\n"); close (fd); return (-1); } eomem =+ 512; } close(fd); return (p); } /* append the contents of one open file to another */ append(from, to) int from, to; { char buffer[512]; int count; while ((count = read(from,buffer,512)) > 0) write(to,buffer,count); } /* name: rawmode and normal function: to put the console into raw mode and normal mode respectively. globals: oldmode calls: ttymod called by: input history: written at berkeley modified by mark kampe for new console driver modified by Larry Allen (2/8/81) for Delphi UNIX console driver */ normal() { int mode[(sizeof (ttynorm) / sizeof (ttynorm[0]))]; int i; for (i = 0; i < (sizeof (ttynorm) / sizeof (ttynorm[0])); i++) mode[i] = ttynorm[i]; ttymod (0, mode); } rawmode() { int mode[(sizeof (ttyraw) / sizeof (ttyraw[0]))]; int i; for (i = 0; i < (sizeof (ttyraw) / sizeof (ttyraw[0])); i++) mode[i] = ttyraw [i]; ttymod (0, mode); } /* name: send function: To take a list of recipients and take steps to deliver the message (now in the temp file) to all of them. algorithm: parse off a name. If it is a name:, open that file and read from it for a while. If it is a local user id, call post If it is a network user id, call netpost parameters: Pointer to a null terminated characterstring which is a list of recipients. returns: globals: unsent user calls: open (system) printf network findlocal netpost post access system called by: main history: Coded by someone at berkely. Minor modifications by Mark Kampe. Minor modification by Lauren Weinstein Bug fixed by Charles Kline. (3/7/77) Rewritten from scratch by Larry Allen 4/6/82 */ send(list, rqfd) char *list; int rqfd; { register char *name,*curp; register char c; int angles, quoted; curp = list; c = *curp++; for (;;) { name = user; while (c == ' ' || sepchar(c)) c = *curp++; if (c == '\0') return; if (c == '"') { while ((c = *curp++) != '\0' && c != '"') *name++ = c; if (c == '"') c = *curp++; } while (c != '\0' && !sepchar(c)) { if (c == '(') { while ((c = *curp++) != ')' && c != '\0') ; if (c == '\0') break; } else if (c == '<') { name = user; angles = TRUE; } else if (c == '>' && angles) { angles = FALSE; while ((c = *curp++) != '\0' && !sepchar(c)) ; break; } else *name++ = c; c = *curp++; } *name = '\0'; while (*--name == ' ') /* trim trailing spaces */ *name = '\0'; if (*user != '\0') netpost (rqfd, user); } } /* end send */ sepchar(ch) register char ch; { if ((ch > '\0' && ch < ' ') || ch == ',') return (TRUE); return (FALSE); } /* netinit - Copy file off to mail daemon's directory, and then open up a mail request file, write out the temp file name into it, and return the file descriptor. */ netinit (tempnet) char *tempnet; { char tempnam[NAMSIZ]; int fd; register char *p, *q; p = tempnet; q = MAILDIR; while (*p++ = *q++); get_temp (tempnam); stcat ("/", tempnet); stcat (tempnam, tempnet); if ((fd = creat (tempnet, 0660)) < 0) { printf ("can't creat request file\n"); unsent++; return (-1); } blt (tmpfd, fd); close (fd); p = reqnam; q = MAILDIR; while (*p++ = *q++); stcat ("/", reqnam); stcat (tempnam, reqnam); stcat (".", reqnam); if ((fd = creat (reqnam, 0660)) < 0) { printf ("can't make request file\n"); unsent++; return (-1); } write (fd, tempnet, NAMSIZ); return (fd); } /* blt - copy all of one file to another */ blt (from, to) int from; int to; { char buf[512]; int size; seek (from, 0, 0); seek (to, 0, 0); while ((size = read (from, buf, 512)) > 0) write (to, buf, size); } /* quereq - change name of request file to name.req so mail daemon * will recognize it. */ quereq (fd) int fd; { char rname[NAMSIZ]; register char *p, *q; int pid; close (fd); p = rname; q = reqnam; while (*p++ = *q++); stcat (RQEXT, rname); link (reqnam, rname); unlink (reqnam); if ((pid = fork ()) == -1) { printf ("unable to fork for sig_maild\n"); return; } else if (pid == 0) execl ("/bin/sig_maild", "/bin/sig_maild", 0); } /* name: tempfile function: To select a unique name for and creat a temporary file. algorithm: Use send as the filename. parameters: *char pointer to user buffer of adequate length. returns: *char pointer to the chosen name and tmpfd is the file descriptor for that file. globals: tmpfd calls: creat getpid called by: input history: Coded by someone at Berkeley */ tmpfile(ubuf) char *ubuf; { int pid; char cpid[10]; struct status buf; pid = getpid (); do { itos (cpid, pid, 10); ubuf[0] = '\000'; stcat( "/tmp/", ubuf ); stcat(cpid, ubuf); pid++; } while (stat (ubuf, &buf) != -1); return( ubuf ); } /* name: netpost function: to attempt to mail off a piece of network mail. algorithm: just write a request record into request file for mail daemon. parameters: int file descriptor of request file *char pointer to null terminated user name *char pointer to null terminated host name returns: boolean whether or not it worked globals: unsent tmpfd bigbuf calls: read write printf creat close stat chown called by: send history: Initially coded by Mark Kampe Modified by Lauren Weinstein, 2/6/78, to make network code table-driven Completely changed by Larry Allen, 2/13/81, to work with TFTP */ netpost(fd,uname) int fd; char *uname; { char *p, *q; struct req_rec recrd; p = recrd.rq_name; q = uname; while (*p++ = *q++); recrd.rq_retries = RETRIES; write (fd, &recrd, sizeof (recrd)); } /* itos (ps, n, b) * * Converts its integer numeric object n into an ASCII string * in the space pointed to by ps, according to the base b. */ char *itos (ps, n, b) register char *ps; int n, b; { register int a; if (a = n / b) ps = itos (ps, a, b); a = n % b; *ps++ = a + (a > 9 ? 'A' - 10 : '0'); *ps = 0; return (ps); } /* get_temp - get a unique temporary name. Uses pid converted to Ascii. */ get_temp (tempnam) char *tempnam; { struct status buf; do { itos (tempnam, pid, 10); pid++; } while (stat (tempnam, &buf) >= 0); } /* name: getname function: To find out the name of the user who forked me. algorithm: Get my real user id and look it up in the password table parameters: *char pointer to buffer where name should be placed. returns: *char pointer to buffer wher callers's name was placed. globals: calls: getuid getpw printf called by: main history: Rewritten by Mark Kampe */ getname(caller, homedir) char *caller; char *homedir; { char linebuf[100]; char *cp,*lp; int i; myuid = getuid() & 0377; if (getpw(myuid, linebuf)) { printf("Your user id is not in the password file?!?\n"); exit(); } cp = caller; lp = linebuf; while(*lp != ':') *cp++ = *lp++; *cp = '\000'; for (i = 0; i < 5; i++) while (*lp++ != ':'); cp = homedir; while (*lp != ':') *cp++ = *lp++; *cp = '\000'; return(caller); } /* name: slashc function: Given that the last input character seen was a backslash, this routine returns the character that is being escaped. algorithm: If terminal is half ascii If next char is lower case, return its upper case counterpart. If it is the preimage of a non-half ascii character, return that character. Else, return the next character. parameters: returns: A character. globals: ttys to check out the console modes. calls: getchar called by: people who do rawmode input (namely input) history: Initial coding by someone at berkeley. */ slashc() { char c; c = getchar(0); return(c); } /* end slashc */ /* name: getchar function: To read a character from a specified file. algorithm: Do a read, and if we got an end of file, return a -1. CAVEAT Input is unbuffered. parameters: int file descriptor of file to be read from. returns: char or -1; globals: calls: read (system) called by: lots of people history: Someone at berkeley. */ getchar(cfd) { char c; register int i; if (incount == 0) { incount = read(cfd, inbuf, 512); inptr = inbuf; } if (incount <= 0) return(-1); incount--; return((*inptr++)&0177); } getpwchar() { tryagain: if (pwcount > 0) { pwcount--; return(*pwptr++); } else { pwcount = read(pwfd,pwbuf,512); if (pwcount <= 0) return(-1); pwptr = pwbuf; goto tryagain; } } stcat(latter,former) char *latter,*former; { register char *fp,*lp; fp = former; lp = latter; while (*fp++); fp--; while (*fp++ = *lp++); } /* name: arpadate function: to get an ARPA format date (format described in rfc 680) algorithm: Get the unix format date and jumble it around to be arpa format Unix format is: day mmm dd hh:mm:ss yyyy Arpa format is: dd mmm yyyy at hhmm-zzz parameters: pointer to the doubleword time returns: pointer to the arpa format date corresponding to that time globals: dbuf calls: ctime localtime called by: main history: Designed and coded by Mark Kampe 1/9/76 Augmented by Lauren Weinstein, 3/16/79 */ char *arpadate(tvec) int tvec[2]; { register int *i; register char *p; register char *t; t = ctime(tvec); p = dbuf; if (t[8] != ' ') *p++ = t[8]; *p++ = t[9]; *p++ = ' '; *p++ = t[4]; *p++ = t[5]; *p++ = t[6]; *p++ = ' '; *p++ = t[20]; *p++ = t[21]; *p++ = t[22]; *p++ = t[23]; *p++ = ' '; if (t[11] != ' ') *p++ = t[11]; *p++ = t[12]; *p++ = t[14]; *p++ = t[15]; *p++ = '-'; *p++ = 'E'; i = localtime(tvec); if (i[8]) *p++ = 'D'; else *p++ = 'S'; *p++ = 'T'; t = weekdays[i[6]]; *p++ = ' '; *p++ = '('; while (*p++ = *t++); p--; *p++ = ')'; *p++ = '\000'; return(dbuf); } /* name: command function: A command interpreter which may be reached from input level. Provides a number of commands to the user to: modify header fields, edit the body of the message, display the current message, etc. algorithm: Prompt the user for the command. Look up his response and perform the requested function. Loop until he quits or sends the message. Note that he only has to specify an unambiguous prefix of the command name. parameters: editflag - true if user typed ^E to edit the message globals: lots, including the message, the header fields, tempfile, etc. called by: input history: coded by Larry Allen 4/4/82 in response to popular demand. */ command(editflag) int editflag; { char comd[SMALLBUFLEN]; char thiscmd[SMALLBUFLEN]; register char *p,*q; register struct cmd_tab *cmd; int tmpfd; char *parse_cmd(); struct cmd_tab *find_cmd(); printf ("\n\n"); if (editflag) editmsg (); for (;;) { printf("send>"); gather(comd); p = parse_cmd (comd, thiscmd); if ((cmd = find_cmd (thiscmd, cmd_tab)) == 0) continue; switch (cmd->cm_tag) { case C_TO: stcat(",", to); stcat(p, to); break; case C_CC: if (*cc != '\0') stcat(",", cc); stcat(p, cc); break; case C_BCC: if (*bcc != '\0') stcat(",", bcc); stcat(p,bcc); break; case C_SUBJ: q = subject; while (*q++ = *p++); break; case C_EDIT: editmsg (); break; case C_PUSH: push (); break; case C_HELP: help (); break; case C_DISP: present_data (1); break; case C_QUIT: normal(); exit(1); case C_SEND: return; } } } char *parse_cmd (line, cmd) /* Parse off the command from the specified line. The command is everything * up to the first space, tab, or newline, or the end-of-string. The * command is copied into the buffer cmd, null-terminated. * Returns a pointer to the first character after the command, or the * null at the end of the string. */ register char *line; register char *cmd; { while (*line != ' ' && *line != '\t' && *line != '\n' && *line != '\0') *cmd++ = *line++; *cmd = '\0'; return (line); } struct cmd_tab *find_cmd (cmd, tab) /* Find the specified command in the specified command table. Only enough * of the command to unambiguously identify it needs to be supplied. If * the command is not found in the table or is ambiguous, an appropriate * error message is printed and 0 is returned. Otherwise the index * of the command in the table is returned. */ register char *cmd; register struct cmd_tab *tab; { register struct cmd_tab *found = 0; for (; *(tab->cm_name) != 0; tab++) if (prefix (cmd, tab->cm_name) >= 0) if (found != 0) { printf ("Ambiguous command; type 'help' for help\n"); return (0); } else found = tab; if (found == 0) { printf ("Unknown command; type 'help' for help\n"); return (0); } return (found); } prefix (s1, s2) /* Returns > 0 iff string s1 is a prefix of string s2; ie if * strlen(s1) <= strlen(s2) and s1 == the first strlen(s1) characters * of s2. Returns == 0 iff s1 == s2. Otherwise returns < 0. */ register char *s1, *s2; { while (*s1 != '\0') if (*s1++ != *s2 || *s2++ == '\0') return (-1); return (*s2 == 0 ? 0 : 1); } push() /* Fork a new shell for the user to talk to. Be sure to save all * signals before waiting and restore them afterwards. */ { register int pid; if ((pid = fork()) < 0) { printf ("Can't fork for new shell\n"); exit (1); } else if (pid == 0) { /* child */ execl ("/bin/sh", "/bin/sh", 0); printf ("Couldn't exec the shell!\n"); exit (1); } else { /* parent */ save_sigs(); wait (0); restore_sigs(); } } editmsg() /* Fork the user's editor to edit the message currently in letter. * His editor is specified in his .mail.init file. This requires * the letter to be written out to a temporary file and then read * back in when the editor exits. */ { int pid, junk; register char *p, *q; char ebuf[128]; char cbuf[16]; tmpfile(tempfile); /* get a filename */ if ((tmpfd = creat(tempfile,0600)) < 0) { printf("Unable to create %s\n", tempfile); exit(1); } writef(tmpfd, letter); close (tmpfd); if ((pid = fork()) == -1) { printf ("Can't fork for edit\n"); exit (1); } else if (pid == 0) { /* child process */ p = &init_rec.in_edit; q = &ebuf; while ((*p != '\0') && (*p != ' ')) *q++ = *p++; *q++ = '\0'; if (*p == '\0') execl (ebuf, ebuf, tempfile, 0); else { q = &cbuf; p++; while (*q++ = *p++); execl (ebuf, ebuf, cbuf, tempfile, 0); } printf ("Can't exec editor\n"); exit (1); } else { save_sigs(); wait (0); restore_sigs(); } if ((tmpfd = open (tempfile, 0)) < 0) { printf ("can't reopen %s\n", tempfile); exit (1); } eomem = brk (letter); /* ugly! Flush previous letter */ getfil (tmpfd, letter); unlink (tempfile); } save_sigs() /* Save the states of the interrupt, quit, special, and terminate signals * after forking to insure that we don't die while asleep. */ { old_INT = signal (SIGINT, SIG_IGN); old_QUIT = signal (SIGQUIT, SIG_IGN); old_TERM = signal (SIGTERM, SIG_IGN); old_SPC = signal (SIGSPC, SIG_IGN); } restore_sigs() /* Restore the saved signal states after returning from a fork. */ { signal (SIGINT, old_INT); signal (SIGQUIT, old_QUIT); signal (SIGTERM, old_TERM); signal (SIGSPC, old_SPC); } help() { printf ("The following commands are valid:\n"); printf (" to add recipients to 'To:' field\n"); printf (" cc add recipients to 'cc:' field\n"); printf (" bcc add blind carbon copy recipients\n"); printf (" subject change subject of message to subj\n"); printf (" edit edit the body of the message\n"); printf (" display display the message\n"); printf (" push call a shell\n"); printf (" ? or help print this text\n"); printf (" quit abort; don't send message\n"); printf (" send send the message as is\n"); } present_data (tmpfd) /* Put the message out into the specified file, ready to be sent. * The file may be the terminal; this is used to display the current * message. */ register int tmpfd; { int fd; register int i; register char *p; write(tmpfd,"Date: ",6); writef(tmpfd,date); write(tmpfd,"\nFrom: ",7); fd = open("/etc/versions/site", 0); if (fd > 0) { bigbuf[0] = '\0'; stcat(from, bigbuf); stcat( "@", bigbuf ); for (p = bigbuf, i = 0; *p != '\0'; p++, i++) ; i += read(fd, p, 512); close( fd ); write (tmpfd, bigbuf, i); } else writef(tmpfd, from); if (realname[0] != 0) { write(tmpfd," (",2); writef(tmpfd,realname); write(tmpfd,")",1); } if (init_rec.in_rflag) { writef (tmpfd, "\nReply-to: "); p = init_rec.in_repto; while (*p) { write (tmpfd, p, 1); if (*p++ == '\n') writef (tmpfd, " "); } } p = subject; if (*p) writef(tmpfd,"\nSubject: "); while(*p) { write(tmpfd, p, 1); if (*p++ == '\n') writef(tmpfd," "); } p = irt; if (*p) writef(tmpfd, "\nIn-reply-to: "); while(*p) { write(tmpfd, p, 1); if (*p++ == '\n') writef(tmpfd," "); } write(tmpfd,"\nTo: ",5); p = to; while(*p) { write(tmpfd, p, 1); if (*p++ == '\n') writef(tmpfd,"to: "); } p = cc; if (*p) writef(tmpfd,"\nCC: "); while(*p) { write(tmpfd, p, 1); if (*p++ == '\n') writef(tmpfd,"cc: "); } write(tmpfd,"\n\n", 2); writef(tmpfd,letter); if (frflag) if ((fd = open(frwdfile, 0)) < 0) printf ("can't open %s\n", frwdfile); else { write(tmpfd,"\n ----------\n",22); append(fd,tmpfd); write(tmpfd,"\n ----------\n",22); close(fd); } write(tmpfd,"-------\n\n",9); }