Subject: v09i011: ELM Mail System, Part11/19 Newsgroups: mod.sources Approved: rs@mirror.TMC.COM Submitted by: Dave Taylor Mod.sources: Volume 9, Issue 11 Archive-name: elm2/Part11 #! /bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # If this archive is complete, you will see the message: # "End of archive 11 (of 19)." # Contents: src/screen.c src/screen3270.q src/showmsg.c src/strings.c PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo shar: Extracting \"src/screen.c\" \(11166 characters\) if test -f src/screen.c ; then echo shar: Will not over-write existing file \"src/screen.c\" else sed "s/^X//" >src/screen.c <<'END_OF_src/screen.c' X/** screen.c **/ X X/** screen display routines for ELM program X X (C) Copyright 1985, Dave Taylor X**/ X X#include "headers.h" X X#define minimum(a,b) ((a) < (b) ? (a) : (b)) X Xstatic int last_current = -1; X Xchar *strcpy(), *strncpy(), *nameof(); X Xshowscreen() X{ X char buffer[SLEN]; X X ClearScreen(); X X if (selected) X sprintf(buffer, X "Mailbox is '%s' with %d shown out of %d [Elm %s]", X nameof(infile), selected, message_count, VERSION); X else X sprintf(buffer, "Mailbox is '%s' with %d message%s [Elm %s]", X nameof(infile), message_count, plural(message_count), VERSION); X Centerline(1, buffer); X X last_header_page = -1; /* force a redraw regardless */ X show_headers(); X X if (mini_menu) X show_menu(); X X show_last_error(); X X if (hp_terminal) X define_softkeys(MAIN); X} X Xupdate_title() X{ X /** display a new title line, probably due to new mail arriving **/ X X char buffer[SLEN]; X X if (selected) X sprintf(buffer, X "Mailbox is '%s' with %d shown out of %d [Elm %s]", X nameof(infile), selected, message_count, VERSION); X else X sprintf(buffer, "Mailbox is '%s' with %d message%s [Elm %s]", X nameof(infile), message_count, plural(message_count), VERSION); X X ClearLine(1); X X Centerline(1, buffer); X} X Xshow_menu() X{ X /** write main system menu... **/ X X if (user_level == 0) { /* a rank beginner. Give less options */ X Centerline(LINES-7, X "You can use any of the following commands by pressing the first character;"); X Centerline(LINES-6, X"D)elete mail, M)ail a message, R)eply to mail, U)ndelete, or Q)uit"); X Centerline(LINES-5, X "To read a message, press . j = move arrow down, k = move arrow up"); X } else { X Centerline(LINES-7, X "|=pipe, !=shell, ?=help, =set current to n, /=search pattern"); X Centerline(LINES-6, X"A)lias, C)hange mailbox, D)elete, E)dit, F)orward, G)roup reply, M)ail,"); X Centerline(LINES-5, X "N)ext, O)ptions, P)rint, R)eply, S)ave, T)ag, Q)uit, U)ndelete, or eX)it"); X } X} X Xint Xshow_headers() X{ X /** Display page of headers (10) if present. First check to X ensure that header_page is in bounds, fixing silently if not. X If out of bounds, return zero, else return non-zero X Modified to only show headers that are "visible" to ze human X person using ze program, eh? X **/ X X register int this_msg = 0, line = 4, last = 0, last_line, X displayed = 0; X char newfrom[SLEN], buffer[SLEN]; X X if (fix_header_page()) { X dprint0(7, "show_headers returned FALSE 'cause of fix-header-page\n"); X return(FALSE); X } X X if (selected) { X if ((header_page*headers_per_page) > selected) { X dprint2(7, "show_headers returned FALSE since selected [%d] < %d\n", X selected, header_page*headers_per_page); X return(FALSE); /* too far! too far! */ X } X X dprint0(6,"** show_headers AND selected...\n"); X X if (header_page == 0) { X this_msg = visible_to_index(1); X displayed = 0; X } X else { X this_msg = visible_to_index(header_page * headers_per_page + 1); X displayed = header_page * headers_per_page; X } X X dprint2(7,"this_msg (index) = %d [header_page = %d]\n", this_msg, X header_page); X X dprint1(7,"we've already displayed %d messages\n", displayed); X X last = displayed+headers_per_page; X X dprint1(7,"and the last msg on this page is %d\n", last); X } X else { X if (header_page == last_header_page) /* nothing to do! */ X return(FALSE); X X /** compute last header to display **/ X X this_msg = header_page * headers_per_page; X last = this_msg + (headers_per_page - 1); X } X X if (last >= message_count) last = message_count-1; X X /** Okay, now let's show the header page! **/ X X ClearLine(line); /* Clear the top line... */ X X MoveCursor(line, 0); /* and move back to the top of the page... */ X X while ((selected && displayed < last) || this_msg <= last) { X tail_of(header_table[this_msg].from, newfrom, TRUE); X X if (selected) { X if (this_msg == current-1) X build_header_line(buffer, &header_table[this_msg], ++displayed, X TRUE, newfrom); X else X build_header_line(buffer, &header_table[this_msg], X ++displayed, FALSE, newfrom); X } X else { X if (this_msg == current-1) X build_header_line(buffer, &header_table[this_msg], this_msg+1, X TRUE, newfrom); X else X build_header_line(buffer, &header_table[this_msg], X this_msg+1, FALSE, newfrom); X } X X Write_to_screen("%s\r\n", 1, buffer); /* avoid '%' probs */ X CleartoEOLN(); X line++; /* for clearing up in a sec... */ X X if (selected) { X if ((this_msg = next_visible(this_msg+1)-1) < 0) X break; /* GET OUTTA HERE! */ X X /* the preceeding looks gross because we're using an INDEX X variable to pretend to be a "current" counter, and the X current counter is always 1 greater than the actual X index. Does that make sense?? X */ X } X else X this_msg++; /* even dumber... */ X } X X dprint0(1,"** out of redraw loop! **\n"); X X if (mini_menu) X last_line = LINES-8; X else X last_line = LINES-3; X X while (line < last_line) { X CleartoEOLN(); X Writechar('\r'); X Writechar('\n'); X line++; X } X X display_central_message(); X X last_current = current; X last_header_page = header_page; X X return(TRUE); X} X Xshow_current() X{ X /** Show the new header, with all the usual checks **/ X X register int first = 0, last = 0, last_line, new_line, i=0, j=0; X char newfrom[SLEN], old_buffer[SLEN], new_buffer[SLEN]; X X (void) fix_header_page(); /* Who cares what it does? ;-) */ X X /** compute last header to display **/ X X first = header_page * headers_per_page; X last = first + (headers_per_page - 1); X X if (last > message_count) X last = message_count; X X /** okay, now let's show the pointers... **/ X X /** have we changed??? **/ X X if (current == last_current) X return; X X if (selected) { X dprint2(2,"\nshow_current\n* last_current = %d, current = %d\n", X last_current, current); X last_line = ((i=compute_visible(last_current-1)-1) % X headers_per_page)+4; X new_line = ((j=compute_visible(current-1)-1) % headers_per_page)+4; X dprint1(2,"* compute_visible(last-1)=%d\n", X compute_visible(last_current-1)); X dprint1(2,"* compute_visible(current-1)=%d\n", X compute_visible(current-1)); X dprint2(2,"* ending up with last_line = %d and new_line = %d\n", X last_line, new_line); X } X else { X last_line = ((last_current-1) % headers_per_page)+4; X new_line = ((current-1) % headers_per_page)+4; X } X X dprint4(7, X "--> show-current: last_current=%d [%d] and current=%d [%d]\n", X last_current, i, current, j); X X dprint2(7," maps to lines %d and %d\n", last_line, new_line); X X if (has_highlighting && ! arrow_cursor) { X /** build the old and new header lines... **/ X X tail_of(header_table[current-1].from, newfrom, TRUE); X build_header_line(new_buffer, &header_table[current-1], X (selected? compute_visible(current-1) : current), X TRUE, newfrom); X X if (last_current > 0) { /* say we went from no mail to new... */ X tail_of(header_table[last_current-1].from, newfrom, TRUE); X build_header_line(old_buffer, &header_table[last_current-1], X (selected? compute_visible(last_current-1) : last_current), X FALSE, newfrom); X X ClearLine(last_line); X PutLine0(last_line, 0, old_buffer); X } X PutLine0(new_line, 0, new_buffer); X } X else { X if (on_page(last_current)) X PutLine0(last_line,0," "); /* remove old pointer... */ X if (on_page(current)) X PutLine0(new_line, 0,"->"); X } X X last_current = current; X} X Xbuild_header_line(buffer, entry, message_number, highlight, from) Xchar *buffer; Xstruct header_rec *entry; Xint message_number, highlight; Xchar *from; X{ X /** Build in buffer the message header ... entry is the current X message entry, 'from' is a modified (displayable) from line, X 'highlight' is either TRUE or FALSE, and 'message_number' X is the number of the message. X **/ X X /** Note: using 'strncpy' allows us to output as much of the X subject line as possible given the dimensions of the screen. X The key is that 'strncpy' returns a 'char *' to the string X that it is handing to the dummy variable! Neat, eh? **/ X X char subj[LONG_SLEN], /* to output subject */ X buff[NLEN]; /* keep start_highlight value */ X X if (strcmp(start_highlight,"->") != 0 && arrow_cursor) { X strcpy(buff, start_highlight); X strcpy(start_highlight, "->"); X } X X strncpy(subj, entry->subject, COLUMNS-45); X X subj[COLUMNS-45] = '\0'; /* insurance, eh? */ X X /* now THIS is a frightening format statement!!! */ X X sprintf(buffer, "%s%s%c%c%c%-3d %3.3s %-2d %-18.18s (%d) %s%s%s", X highlight? ((has_highlighting && !arrow_cursor) ? X start_highlight : "->") : " ", X (highlight && has_highlighting && !arrow_cursor)? " " : "", X show_status(entry->status), X (entry->status & DELETED? 'D' : ' '), X (entry->status & TAGGED? '+' : ' '), X message_number, X entry->month, X atoi(entry->day), X from, X entry->lines, X (entry->lines / 1000 > 0? "" : /* spacing the */ X entry->lines / 100 > 0? " " : /* same for the */ X entry->lines / 10 > 0? " " : /* lines in () */ X " "), /* [wierd] */ X subj, X (highlight && has_highlighting && !arrow_cursor) ? X end_highlight : ""); X X /** Actually, it's rather an impressive feat that we can X do so much in essentially one statement! (Of course, X I'll bet the test suite for the printf routine isn't X THIS rigorous either!!!) (to be honest, though, just X looking at this statement makes me chuckle...) X **/ X X if (arrow_cursor) /* restore! */ X strcpy(start_highlight, buff); X X} X Xint Xfix_header_page() X{ X /** this routine will check and ensure that the current header X page being displayed contains messages! It will silently X fix 'header-page' if wrong. Returns TRUE if changed. **/ X X int last_page, old_header; X X old_header = header_page; X X last_page = (int) ((message_count-1) / headers_per_page); X X if (header_page > last_page) X header_page = last_page; X else if (header_page < 0) X header_page = 0; X X return(old_header != header_page); X} X Xint Xon_page(message) Xint message; X{ X /** Returns true iff the specified message is on the displayed page. **/ X X dprint1(6,"** on_page(%d) returns...", message); X X if (selected) message = compute_visible(message-1); X X if (message >= header_page * headers_per_page) X if (message <= ((header_page+1) * headers_per_page)) { X dprint0(6,"TRUE\n"); X return(TRUE); X } X X dprint0(6,"FALSE\n"); X return(FALSE); X} X Xshow_status(status) Xint status; X{ X /** This routine returns a single character indicative of X the status of this message. The precedence is; X X F = form letter X E = Expired message X P = Priority message X A = Action associated with message X N = New message X _ = (space) default X **/ X X if (status & FORM_LETTER) return( 'F' ); X else if (status & EXPIRED) return( 'E' ); X else if (status & PRIORITY) return( 'P' ); X else if (status & ACTION) return( 'A' ); X else if (status & NEW) return( 'N' ); X else return( ' ' ); X} END_OF_src/screen.c if test 11166 -ne `wc -c src/screen3270.q <<'END_OF_src/screen3270.q' XFrom hpccc!mcgregor@hplabs.ARPA Thu Jun 5 11:41:49 1986 XReceived: from hplabs.ARPA by hpldat ; Thu, 5 Jun 86 11:41:32 pdt XMessage-Id: <8606051841.AA16872@hpldat> XReceived: by hplabs.ARPA ; Thu, 5 Jun 86 11:38:07 pdt XTo: hplabs!taylor@hplabs.ARPA XDate: Thu, 5 Jun 86 11:36:39 PDT XFrom: hpccc!mcgregor@hplabs.ARPA (Scott McGregor) XSubject: revised screen3270.q XTelephone: (415) 857-5875 XPostal-Address: Hewlett-Packard, PO Box 10301, Mail stop 20CH, Palo Alto CA 943X03-0890 XX-Mailer: ELM [version 1.0] X X X/* X * screen3270.q X * X * Created for ELM, 5/86 to work (hopefully) on UTS systems with 3270 X * type terminals, by Scott L. McGregor, HP Corporate Computing Center. X * X */ X X#include "headers.h" X# include X# include X X# include X X X# define TTYIN 0 /* standard input */ X#include X#define MAXKEYS 101 X#define OFF 0 X#define UNKNOWN 0 X#define ON 1 X#define FALSE 0 X#define TRUE 1 X Xchar *error_name(); X Xextern int _line, _col; X X Xpfinitialize() X{ X char cp[80]; X X dprint0(9,"pfinitialize()\n"); X pfinit(); X /* X * load the system defined pfkeys X */ X pfload("/usr/local/etc/.elmpfrc"); X /* X * load the user's keys if any X */ X strcat(cp,home); X strcat(cp,"/.elmpfrc"); X pfload(cp); X pfprint(); X} X X/* X * note, inputs are limited to 80 characters! Any larger amount X * will be truncated without notice! X */ X X X/* X * pfinit() initializes _pftable X */ Xpfinit() X{ X int i,j; X X dprint0(9,"pfinit()\n"); X for (i=0;i [ ] X * X * Pfkeynumber is an integer 1-24. Whitespace can be one X * or more blanks or tabs. Pfkeyvalue is any string NOT X * containing whitespace (however, \b for blank, \t for tab X * and \n for newline can be used instead to indicate that X * the indicated whitespace character is part of a command. X * Note that the EnTER key will NOT be treated as a newline X * command, so defining a newline key is a good idea! X * Anything else appearing on the line after the pfkey is ignored X * and so can be used as a comment. X * X * This may not be the best form for a file used by X * humans to set parms, and if someone comes up with a X * better one and a parser to read it, then this can be X * replaced. X * X */ X X dprint1(1,"%s pfrc opened\n",name); X k = 0; X while ( fscanf(pfdefs,"%d%s",&key,defn) != EOF ) { X dprint2(9,"pfkey definition 1: %d %s\n",key,defn); X if ((key < 0) || (key > MAXKEYS)) { X dprint2(9,"pfkey defn failed: key=%d MAXKEYS=%d\n",key,MAXKEYS); X k++; X } else { X dprint2(9,"pfkey definition 2: %d %s\n",key,defn); X for (i=0,j=0;i= 0)) { X if ((col <= COLUMNS) && (col >=0)) { X return(row*COLUMNS+col); X } X else return(0); X } X else return(0); X} X X/* X * offset2row(offset) takes the offset returnes the row. X * row is assumed to vary from 0 to LINES-1. X */ Xoffset2row(offset) Xint offset; X{ X int i; X X dprint1(9,"offset2row(%d)\n",offset); X i = (int) (offset / COLUMNS); X dprint1(9,"offset2row returns= %d)\n",i); X return(i); X} X X/* X * offset2col(offset) takes the offset returnes the col. X * col is assumed to vary from 0 to COLUMNS-1. X */ Xoffset2col(offset) Xint offset; X{ X int i; X X dprint1(9,"offset2col(%d)\n",offset); X i = (int) (offset % COLUMNS); X dprint1(9,"offset2col returns= %d)\n",i); X return(i); X} X X/* X * Row(row) takes the row in 0 <= row < LINES and returns X * row in 0 < row <= LINES. X */ XRow(row) Xint row; X{ X dprint1(9,"Row(%d)\n",row); X return(row+1); X} X X/* X * Col(Col) takes the col in 0 <= col < COLUMNS and returns X * col in 0 < col <= COLUMNS. X */ XCol(col) Xint col; X{ X dprint1(9,"Col(%d)\n",col); X return(col+1); X} X X Xgethostname(hostname,size) /* get name of current host */ Xint size; Xchar *hostname; X{ X /** Return the name of the current host machine. UTS only **/ X X /** This routine compliments of Scott McGregor at the HP X Corporate Computing Center **/ X X int uname(); X struct utsname name; X X dprint2(9,"gethostname(%s,%d)\n",hostname,size); X (void) uname(&name); X (void) strncpy(hostname,name.nodename,size-1); X if (strlen(name.nodename) > SLEN) X hostname[size] = '\0'; X} X Xint Xisa3270() X{ X /** Returns TRUE and sets LINES and COLUMNS to the correct values X for an Amdahl (IBM) tube screen, or returns FALSE if on a normal X terminal (of course, next to a 3270, ANYTHING is normal!!) **/ X X struct tubiocb tubecb; X X dprint0(9,"isa3270()\n"); X if (ioctl(TTYIN, TUBGETMOD, &tubecb) == -1){ X return(FALSE); /* not a tube! */ X } X LINES = tubecb.line_cnt - 2; X COLUMNS = tubecb.col_cnt; X if (!check_only && !mail_only) { X isatube = TRUE; X return(TRUE); X } X else { X isatube = FALSE; X return(FALSE); X } X} X X/* X * ReadCh3270() reads a character from the 3270. X */ Xint ReadCh3270() X{ X /** read a character from the display! **/ X X register int x; X char tempstr[80]; X char ch; X X dprint0(9,"ReadCh3270()\n"); X if ((_input_buf_ptr > COLUMNS) || X (_input_buffer[_input_buf_ptr] == '\0')) { X WriteScreen3270(); X for (x=0; x < COLUMNS; x++) _input_buffer[x] = '\0'; X panel (noerase, read) { X #@ LINES+1,1# #INC,_input_buffer,COLUMNS# X } X dprint1(9,"ReadCh3270 _input_buffer=%s\n",_input_buffer); X x=strlen(_input_buffer); X pfreturn(qskp,tempstr); X if (!strcmp(tempstr,"\001")) { X if (strlen(_input_buffer) == 1) { X tempstr[0]='\0'; X } X else { X tempstr[0]='\n'; X tempstr[1]='\0'; X } X } X dprint1(9,"ReadCh3270 pfkey=%s\n",tempstr); X strcat(_input_buffer,tempstr); X dprint1(9,"ReadCh3270 _input_buffer+pfkey=%s\n",_input_buffer); X ch = _input_buffer[0]; X dprint1(9,"ReadCh3270 returns(%c)\n",ch); X _input_buf_ptr = 1; X return(ch); X } X else { X ch = _input_buffer[_input_buf_ptr]; X dprint1(9,"ReadCh3270 returns(%c)\n",ch); X _input_buf_ptr = _input_buf_ptr + 1; X return(ch); X } X} X X X/* X * WriteScreen3270() Loads a screen to the buffer. X * X */ XWriteScreen3270() X{ X register int x; X int currcol; X int currrow; X int i; X int state = OFF; X int prevrow = 1; X int prevcol = 1; X int prevptr = 0; X int clear_state = ON; X char tempstr[80]; X char copy_screen[66*132]; X X dprint0(9,"WriteScreen3270()\n"); X prevrow = 1; X prevcol = 1; X prevptr = 0; X state = OFF; X for (x=0; x < LINES*COLUMNS; x++){ X if ((_internal_screen[x] == '\016') X && (state == OFF)) { X currrow = (x / COLUMNS ) + 1; X currcol = (x % COLUMNS ) + 1 ; X i = x - prevptr - 1; X strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i); X panel(erase=clear_state,write,noread) { X #@ prevrow, prevcol # #ON,copy_screen,i # X } X clear_state = OFF; X state = ON; X /* prevrow = currrow; */ X /* prevcol = currcol; */ X prevrow = currrow + 1; X prevcol = 0; X prevptr = x+1; X } X else if ((_internal_screen[x] == '\017') X && (state == ON)) { X currrow = (x / COLUMNS ) + 1; X currcol = (x % COLUMNS ) + 1; X i = x - prevptr - 1; X strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i); X panel(erase = clear_state,write,noread) { X #@ prevrow,prevcol # #OH,copy_screen,i # X } X clear_state = OFF; X state = OFF; X /* prevrow = currrow; */ X /* prevcol = currcol; */ X prevrow = currrow + 1; X prevcol = 0; X } X else if (_internal_screen[x] < ' ') { X _internal_screen[x] = ' '; X prevptr = x + 1; X } X } X /* write remainder of buffer */ X if (state == OFF) { X currrow = (LINES) + 1 ; X currcol = (COLUMNS ) + 1; X i = x - prevptr ; X strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i); X panel(erase=clear_state,write,noread) { X #@ prevrow,prevcol # #ON,copy_screen,i # X } X } X else { X currrow = (LINES ) + 1 ; X currcol = (COLUMNS ) + 1 ; X i = x - prevptr ; X strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i); X panel(erase=clear_state,write,noread) { X #@ prevrow,prevcol # #OH,copy_screen,i # X } X } X} X X X/* X * Clear3270 X */ XClear3270() X{ X int i,j; X X dprint0(9,"Clear3270()\n"); X j = rowcol2offset(LINES,COLUMNS); X for (i = 0; i < j; i++) { X _internal_screen[i] = ' '; X } X return(0); X} X X/* X * WriteChar3270(row,col) writes a character at the row and column. X */ XWriteChar3270(row,col,ch) Xint row, col; Xchar ch; X{ X dprint3(9,"WriteChar3270(%d,%d,%c)\n",row,col,ch); X _internal_screen[rowcol2offset(row,col)] = ch; X} X X/* X * WriteLine3270(row,col) writes a line at the row and column. X */ XWriteLine3270(row,col,line) Xint row, col; Xchar *line; X{ X int i, j, k; X dprint3(9,"WriteLine3270(%d,%d,%s)\n",row,col,line); X _line = row; X _col = col; X k = strlen(line); X i=rowcol2offset(row,col); X for (j=0; j= ' ') || X (line[j] == '\016') || X (line[j] == '\017')) X _internal_screen[i] = line[j]; X else _internal_screen[i] = ' '; X } X /* _line = offset2row(i-1); calling program updates location */ X /* _col = offset2col(i-1); */ X X} X X X/* X * ClearEOLN3270() clears the remainder of the current line on a 3270. X */ XClearEOLN3270() X{ X int i,j ; X X dprint0(9,"ClearEOLN3270()\n"); X j = rowcol2offset(_line,COLUMNS); X for (i=rowcol2offset(_line,_col); i < j; i++) { X _internal_screen[i] = ' '; X } X} X X/* X * ClearEOS3270() clears the remainder of the current screen on a 3270. X */ XClearEOS3270() X{ X int i,j; X X dprint0(9,"ClearEOS3270()\n"); X j = rowcol2offset(LINES,COLUMNS); X for (i = rowcol2offset(_line,_col); i < j; i++) { X _internal_screen[i] = ' '; X } X} X END_OF_src/screen3270.q if test 11593 -ne `wc -c src/showmsg.c <<'END_OF_src/showmsg.c' X/** showmsg.c **/ X X/** This file contains all the routines needed to display the specified X message. X X These routines (C) Copyright 1986 Dave Taylor X X Modified 6/86 to use pager variable!!! Hurrah!!!! X Modified 7/86 to have secure pipes.. *sigh* X**/ X X#include "headers.h" X#include X#include X#include X X#ifdef BSD X# include X# undef tolower X#endif X Xextern int errno; X Xchar *error_name(), *strcat(), *strcpy(); Xvoid _exit(); X Xint memory_lock = FALSE; /* is it available?? */ Xint pipe_abort = FALSE; /* did we receive a SIGNAL(SIGPIPE)? */ X Xint Xshow_msg(number) Xint number; X{ X /*** display number'th message. Get starting and ending lines X of message from headers data structure, then fly through X the file, displaying only those lines that are between the X two! X Returns non-zero iff screen was changed X ***/ X X dprint0(8, "show_msg called\n"); X X if (number > message_count) { X error1("Only %d messages!", message_count); X return(0); X } X else if (number < 1) { X error("you can't read THAT message!"); X return(0); X } X X clearit(header_table[number-1].status, NEW); /* it's been read now! */ X X memory_lock = FALSE; X X /* some explaination for that last one - We COULD use memory locking X to speed up the paging, but the action of "ClearScreen" on a screen X with memory lock turned on seems to vary considerably (amazingly so) X so it's safer to only allow memory lock to be a viable bit of X trickery when dumping text to the screen in scroll mode. X Philosophical arguments should be forwarded to Bruce at the X University of Walamazoo, Australia, via ACSNet *wry chuckle* */ X X return(show_message(header_table[number-1].lines, X header_table[number-1].offset,number)); X} X Xint Xshow_message(lines, file_loc, msgnumber) Xint lines, msgnumber; Xlong file_loc; X{ X /*** Show the indicated range of lines from mailfile X for message 'msgnumber' by using 'display' X Returns non-zero iff screen was altered. X ***/ X X dprint3(9,"show_message(%d,%ld,%d)\n", lines, file_loc, msgnumber); X X if (fseek(mailfile, file_loc, 0) != 0) { X dprint2(1,"Error: seek %d bytes into file, errno %s (show_message)\n", X file_loc, error_name(errno)); X error2("ELM failed seeking %d bytes into file (%s)", X file_loc, error_name(errno)); X return(0); X } X X if (feof(mailfile)) X dprint0(1,"\n*** seek put us at END OF FILE!!! ***\n"); X X /* next read will get 'this' line so must be at end of previous */ X X Raw(OFF); X if (strcmp(pager,"builtin") == 0 || strcmp(pager,"internal") == 0) X display(lines, msgnumber); X else X secure_display(lines, msgnumber); X Raw(ON); X if (memory_lock) EndMemlock(); /* turn it off!! */ X X return(1); /* we did it boss! */ X} X X X/** This next one is the 'pipe' file descriptor for writing to the X pager process... **/ X XFILE *output_pipe, *popen(); X Xint Xdisplay(lines, msgnum) Xint lines, msgnum; X{ X /** Display specified number of lines from file mailfile. X Note: This routine MUST be placed at the first line X of the input file! X Returns the same as the routine above (namely zero or one) X **/ X X char from_buffer[LONG_STRING], buffer[VERY_LONG_STRING], *full_month(); X X int lines_displayed = 0; X int crypted = 0, gotten_key = 0; /* encryption */ X int weed_header, weeding_out = 0; /* weeding */ X int mail_sent, /* misc use */ X form_letter = FALSE, /* Form ltr? */ X form_letter_section = 0, /* section */ X builtin = FALSE; /* our pager? */ X X dprint3(4,"displaying %d lines from message %d using %s\n", X lines, msgnum, pager); X X ClearScreen(); X X if (cursor_control) transmit_functions(OFF); X X pipe_abort = FALSE; X X builtin = (strcmp(pager, "builtin") == 0 || X strcmp(pager,"internal") == 0); X X if (form_letter = (header_table[msgnum-1].status&FORM_LETTER)) { X if (filter) X form_letter_section = 1; /* initialize to section 1 */ X } X X if (builtin) X start_builtin(lines); X else { X if ((output_pipe = popen(pager,"w")) == NULL) { X error2("Can't create pipe to %s [%s]", pager, X error_name(errno)); X dprint2(1,"\n*** Can't create pipe to %s - error %s ***\n\n", X pager, error_name(errno)); X return(0); X } X dprint1(4,"Opened a write-only pipe to routine %s \n", pager); X } X X if (title_messages) { X X mail_sent = (strncmp(header_table[msgnum-1].from, "To:", 3) == 0); X X tail_of(header_table[msgnum-1].from, from_buffer, FALSE); X X sprintf(buffer, "\r%s #%d %s %s%s\t %s %s %s, %d at %s%s\n\r", X form_letter? "Form": "Message", X msgnum, mail_sent? "to" : "from", from_buffer, X (strlen(from_buffer) > 24? "\n\r": X (strlen(from_buffer) > 16 ? "" : "\t")), X "Mailed", X full_month(header_table[msgnum-1].month), X header_table[msgnum-1].day, X atoi(header_table[msgnum-1].year) + 1900, X header_table[msgnum-1].time, X filter? "": "\n\r\n\r"); X X if (builtin) X display_line(buffer); X else X fprintf(output_pipe, "%s", buffer); X X if (! mail_sent && matches_weedlist("To:") && filter && X strcmp(header_table[current-1].to,username) != 0 && X strlen(header_table[current-1].to) > 0) { X sprintf(buffer, "\n\r(message addressed to %s)\n\r", X header_table[current-1].to); X if (builtin) X display_line(buffer); X else X fprintf(output_pipe, "%s", buffer); X } X X /** The test above is: if we didn't originally send the mail X (e.g. we're not reading "mail.sent") AND the user is currently X weeding out the "To:" line (otherwise they'll get it twice!) X AND the user is actually weeding out headers AND the message X wasn't addressed to the user AND the 'to' address is non-zero X (consider what happens when the message doesn't HAVE a "To:" X line...the value is NULL but it doesn't match the username X either. We don't want to display something ugly like X "(message addressed to )" which will just clutter up the X display!). X X And you thought programming was EASY!!!! X **/ X } X X weed_header = filter; /* allow us to change it after header */ X X while (lines > 0 && pipe_abort == FALSE) { X X if (fgets(buffer, VERY_LONG_STRING, mailfile) == NULL) { X if (lines_displayed == 0) { X X /* AUGH! Why do we get this occasionally??? */ X X dprint0(1, X "\n\n** Out of Sync!! EOF with nothing read (display) **\n"); X dprint0(1,"** closing and reopening mailfile... **\n\n"); X X if (!builtin) pclose(output_pipe); /* close pipe NOW! */ X X if (mailfile != NULL) X fclose(mailfile); /* huh? */ X X if ((mailfile = fopen(infile, "r")) == NULL) { X error("Sync error: can't re-open mailbox!!"); X show_mailfile_stats(); X emergency_exit(); X } X return(show_message(lines, X header_table[msgnum-1].offset, X msgnum)); X } X if (!builtin) X pclose(output_pipe); X if (lines == 0 && pipe_abort == FALSE) { /* displayed it all */ X if (!builtin) X PutLine0(LINES,0,"\rPress to return to Elm: "); X else X printf("\n\r\n\rPress to return to Elm: "); X fflush(stdout); X Raw(ON); X (void) ReadCh(); X Raw(OFF); X } X return(TRUE); X } X X if (strlen(buffer) > 0) X no_ret(buffer); X X if (strlen(buffer) == 0) { X weed_header = 0; /* past header! */ X weeding_out = 0; X } X X lines--; X lines_displayed++; X X if (form_letter && weed_header) X /* skip it. NEVER display random headers in forms! */; X else if (weed_header && matches_weedlist(buffer)) X weeding_out = 1; /* aha! We don't want to see this! */ X else if (buffer[0] == '[') { X if (strcmp(buffer, START_ENCODE)==0) X crypted++; X else if (strcmp(buffer, END_ENCODE)==0) X crypted--; X else if (crypted) { X encode(buffer); X show_line(buffer, builtin); X } X else X show_line(buffer, builtin); X } X else if (crypted) { X if (! gotten_key++) getkey(OFF); X encode(buffer); X show_line(buffer, builtin); X } X else if (weeding_out) { X weeding_out = (whitespace(buffer[0])); /* 'n' line weed */ X if (! weeding_out) /* just turned on! */ X show_line(buffer, builtin); X } X else if (form_letter && first_word(buffer,"***") && filter) { X strcpy(buffer, X"\n------------------------------------------------------------------------------\n"); X show_line(buffer, builtin); /* hide '***' */ X form_letter_section++; X } X else if (form_letter_section == 1 || form_letter_section == 3) X /** skip this stuff - we can't deal with it... **/; X else X show_line(buffer, builtin); X } X X if (cursor_control) transmit_functions(ON); X X if (! builtin) pclose(output_pipe); X X if (lines == 0 && pipe_abort == FALSE) { /* displayed it all! */ X if (! builtin) X PutLine0(LINES,0,"\rPress to return to Elm: "); X else X printf("\n\r\n\rPress to return to Elm: "); X fflush(stdout); X Raw(ON); X (void) ReadCh(); X Raw(OFF); X } X return(TRUE); X} X Xshow_line(buffer, builtin) Xchar *buffer; Xint builtin; X{ X /** Hands the given line to the output pipe. 'builtin' is true if X we're using the builtin pager. **/ X X if (builtin) { X strcat(buffer, "\n\r"); X pipe_abort = display_line(buffer); X } X else { X errno = 0; X fprintf(output_pipe, "%s\n", buffer); X X if (errno != 0) X dprint1(1,"\terror %s hit!\n", error_name(errno)); X } X} X Xint Xsecure_display(lines, msgnumber) Xint lines, msgnumber; X{ X /** This is the cheap way to implement secure pipes - spawn a X child process running under the old userid, then open the X pager and feed the message to it. When the subprocess ends X (the routine returns) simply return. Simple and effective. X (too bad it's this much of a hassle to implement secure X pipes, though - I can imagine it being a constant problem!) X **/ X X int pid, w; X#ifdef BSD X union wait status; X#else X int status; X#endif X register int (*istat)(), (*qstat)(); X X#ifdef NO_VM /* machine without virtual memory! */ X if ((pid = fork()) == 0) { X#else X if ((pid = vfork()) == 0) { X#endif X X setgid(groupid); /* and group id */ X setuid(userid); /* back to the normal user! */ X X _exit(display(lines, msgnumber)); X } X X istat = signal(SIGINT, SIG_IGN); X qstat = signal(SIGQUIT, SIG_IGN); X X while ((w = wait(&status)) != pid && w != -1) X ; X X signal(SIGINT, istat); X signal(SIGQUIT, qstat); X X#ifdef BSD X return(status.w_retcode); X#else X return(status); X#endif X} END_OF_src/showmsg.c if test 10629 -ne `wc -c src/strings.c <<'END_OF_src/strings.c' X/** strings.c **/ X X/** This file contains all the string oriented functions for the X ELM Mailer, and lots of other generally useful string functions! X X For BSD systems, this file also includes the function "tolower" X to translate the given character from upper case to lower case. X X (C) Copyright 1985, Dave Taylor X**/ X X#include X#include "headers.h" X#include X X#ifdef BSD X#undef tolower X#undef toupper X#endif X X/** forward declarations **/ X Xchar *format_long(), *strip_commas(), *tail_of_string(), *shift_lower(), X *get_token(), *strip_parens(), *argv_zero(), *strcpy(), *strncpy(); X X#ifdef BSD X Xint Xtolower(ch) Xchar ch; X{ X /** This should be a macro call, but if you use this as a macro X calls to 'tolower' where the argument is a function call will X cause the function to be called TWICE which is obviously the X wrong behaviour. On the other hand, to just blindly translate X assuming the character is always uppercase can cause BIG X problems, so... X **/ X X return ( isupper(ch) ? ch - 'A' + 'a' : ch ); X} X Xint Xtoupper(ch) Xchar ch; X{ X /** see comment for above routine - tolower() **/ X X return ( islower(ch) ? ch - 'a' + 'A' : ch ); X} X X#endif X Xint Xprintable_chars(string) Xchar *string; X{ X /** Returns the number of "printable" (ie non-control) characters X in the given string... Modified 4/86 to know about TAB X characters being every eight characters... **/ X X register int count = 0, index; X X for (index = 0; index < strlen(string); index++) X if (string[index] >= ' ') X if (string[index] == '\t') X count += (7-(count % 8)); X else X count++; X X return(count); X} X Xtail_of(from, buffer, header_line) Xchar *from, *buffer; Xint header_line; X{ X /** Return last two words of 'from'. This is to allow X painless display of long return addresses as simply the X machine!username. Alternatively, if the first three X characters of the 'from' address are 'To:' and 'header_line' X is TRUE, then return the buffer value prepended with 'To '. X X Mangled to know about the PREFER_UUCP hack. 6/86 X **/ X X /** Note: '!' delimits Usenet nodes, '@' delimits ARPA nodes, X ':' delimits CSNet & Bitnet nodes, and '%' delimits X multiple stage ARPA hops... **/ X X register int loc, i = 0, cnt = 0; X char tempbuffer[SLEN]; X X#ifdef PREFER_UUCP X X /** let's see if we have an address appropriate for hacking **/ X X if (chloc(from,'!') != -1 && in_string(from, BOGUS_INTERNET)) X from[strlen(from)-strlen(BOGUS_INTERNET)] = '\0'; X X#endif X X for (loc = strlen(from)-1; loc >= 0 && cnt < 2; loc--) { X if (from[loc] == BANG || from[loc] == AT_SIGN || X from[loc] == COLON) cnt++; X if (cnt < 2) buffer[i++] = from[loc]; X } X X buffer[i] = '\0'; X X reverse(buffer); X X if ((strncmp(buffer,"To:", 3) == 0) && header_line) X buffer[2] = ' '; X else if ((strncmp(from, "To:", 3) == 0) && header_line) { X sprintf(tempbuffer,"To %s", buffer); X strcpy(buffer, tempbuffer); X } X else if (strncmp(buffer, "To:", 3) == 0) { X for (i=3; i < strlen(buffer); i++) X tempbuffer[i-3] = buffer[i]; X tempbuffer[i-3] = '\0'; X strcpy(buffer, tempbuffer); X } X} X Xchar *format_long(inbuff, init_len) Xchar *inbuff; Xint init_len; X{ X /** Return buffer with \n\t sequences added at each point where it X would be more than 80 chars long. It only allows the breaks at X legal points (ie commas followed by white spaces). init-len is X the characters already on the first line... Changed so that if X this is called while mailing without the overhead of "elm", it'll X include "\r\n\t" instead. X Changed to use ',' as a separator and to REPLACE it after it's X found in the output stream... X **/ X X static char ret_buffer[VERY_LONG_STRING]; X register int index = 0, current_length = 0, depth=15, i; X char buffer[VERY_LONG_STRING]; X char *word, *bufptr; X X strcpy(buffer, inbuff); X X bufptr = (char *) buffer; X X current_length = init_len + 2; /* for luck */ X X while ((word = get_token(bufptr,",", depth)) != NULL) { X X /* first, decide what sort of separator we need, if any... */ X X if (strlen(word) + current_length > 80) { X if (index > 0) { X ret_buffer[index++] = ','; /* close 'er up, doctor! */ X if (mail_only) X ret_buffer[index++] = '\r'; X ret_buffer[index++] = '\n'; X ret_buffer[index++] = '\t'; X } X X /* now add this pup! */ X X for (i=(word[0] == ' '? 1:0); i 0) { X ret_buffer[index++] = ','; /* comma added! */ X ret_buffer[index++] = ' '; X current_length += 2; X } X for (i=(word[0] == ' '? 1:0); i 1) X buffer[i--] = string[index--]; X buffer[2] = '.'; X buffer[1] = '.'; X buffer[0] = '.'; X } X X return( (char *) buffer); X} X Xreverse(string) Xchar *string; X{ X /** reverse string... pretty trivial routine, actually! **/ X X char buffer[SLEN]; X register int i, j = 0; X X for (i = strlen(string)-1; i >= 0; i--) X buffer[j++] = string[i]; X X buffer[j] = '\0'; X X strcpy(string, buffer); X} X Xint Xget_word(buffer, start, word) Xchar *buffer, *word; Xint start; X{ X /** return next word in buffer, starting at 'start'. X delimiter is space or end-of-line. Returns the X location of the next word, or -1 if returning X the last word in the buffer. -2 indicates empty X buffer! **/ X X register int loc = 0; X X while (buffer[start] == ' ' && buffer[start] != '\0') X start++; X X if (buffer[start] == '\0') return(-2); /* nothing IN buffer! */ X X while (buffer[start] != ' ' && buffer[start] != '\0') X word[loc++] = buffer[start++]; X X word[loc] = '\0'; X return(start); X} X Xchar *shift_lower(string) Xchar *string; X{ X /** return 'string' shifted to lower case. Do NOT touch the X actual string handed to us! **/ X X static char buffer[LONG_SLEN]; X register int i; X X for (i=0; i < strlen(string); i++) X if (isupper(string[i])) X buffer[i] = tolower(string[i]); X else X buffer[i] = string[i]; X X buffer[strlen(string)] = 0; X X return( (char *) buffer); X} X XCenterline(line, string) Xint line; Xchar *string; X{ X /** Output 'string' on the given line, centered. **/ X X register int length, col; X X length = strlen(string); X X if (length > COLUMNS) X col = 0; X else X col = (COLUMNS - length) / 2; X X PutLine0(line, col, string); X} X Xchar *argv_zero(string) Xchar *string; X{ X /** given a string of the form "/something/name" return a X string of the form "name"... **/ X X static char buffer[NLEN]; X register int i, j=0; X X for (i=strlen(string)-1; string[i] != '/'; i--) X buffer[j++] = string[i]; X buffer[j] = '\0'; X X reverse(buffer); X X return( (char *) buffer); X} X X#define MAX_RECURSION 20 /* up to 20 deep recursion */ X Xchar *get_token(source, keys, depth) Xchar *source, *keys; Xint depth; X{ X /** This function is similar to strtok() (see "opt_utils") X but allows nesting of calls via pointers... X **/ X X register int last_ch; X static char *buffers[MAX_RECURSION]; X char *return_value, *sourceptr; X X if (depth > MAX_RECURSION) { X error1("get_token calls nested greater than %d deep!", X MAX_RECURSION); X emergency_exit(); X } X X if (source != NULL) X buffers[depth] = source; X X sourceptr = buffers[depth]; X X if (*sourceptr == '\0') X return(NULL); /* we hit end-of-string last time!? */ X X sourceptr += strspn(sourceptr, keys); /* skip the bad.. */ X X if (*sourceptr == '\0') { X buffers[depth] = sourceptr; X return(NULL); /* we've hit end-of-string */ X } X X last_ch = strcspn(sourceptr, keys); /* end of good stuff */ X X return_value = sourceptr; /* and get the ret */ X X sourceptr += last_ch; /* ...value */ X X if (*sourceptr != '\0') /** don't forget if we're at end! **/ X sourceptr++; X X return_value[last_ch] = '\0'; /* ..ending right */ X X buffers[depth] = sourceptr; /* save this, mate! */ X X return((char *) return_value); /* and we're outta here! */ X} END_OF_src/strings.c if test 11005 -ne `wc -c