Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!shadooby!samsung!munnari.oz.au!basser!ultima!nick From: nick@ultima.cs.uts.oz (Nick Andrew) Newsgroups: comp.os.minix Subject: News for Minix (part 6 of 12) Keywords: news Message-ID: <16748@ultima.cs.uts.oz> Date: 7 Dec 89 11:53:01 GMT Organization: Comp Sci, NSWIT, Australia Lines: 2682 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'virtterm.c' <<'END_OF_FILE' X/* X * Virtual terminal handler X * Written by Kenneth Almquist, AGS Computers (HO 4C601, X7105). X * Modified by Stephen Hemminger, to use TERMCAP (without curses) X */ X X#ifdef SCCSID Xstatic char *SccsId = "@(#)virtterm.c 1.13 12/16/86"; X#endif /* SCCSID */ X X/*LINTLIBRARY*/ X X#include X#include X#include X#include X#include X#ifdef USG X#include X#else /* !USG */ X#include X#endif /* !USG */ X X/* X * These values for MAXPLEN and MAXLLEN are used to dimension arrays X * that hold strings of relative cursor motions. The actual arrays that X * are used to hold screen images are malloc'd. X */ X#define MAXPLEN 90 X#define MAXLLEN 160 X X#define BOTLINE (ROWS - 1) X#define DIRTY 01 X X/* terminal escape sequences from termcap */ X#define HO _tstr[0] /* home */ X#define CL _tstr[1] /* clear screen */ X#define CD _tstr[2] /* clear to end of screen */ X#define CE _tstr[3] /* clear to end of line */ X#define xUP _tstr[4] /* up one line */ X#define DO _tstr[5] /* down one line */ X#define US _tstr[6] /* underline */ X#define UE _tstr[7] /* underline end */ X#define BT _tstr[8] /* backtab */ X#define xBC _tstr[9] /* backspace */ X#define AL _tstr[10] /* insert line */ X#define DL _tstr[11] /* delete line */ X#define CM _tstr[12] /* cursor move */ X#define CH _tstr[13] /* cursor horizontal move */ X#define CV _tstr[14] /* cursor vertical move */ X#define CS _tstr[15] /* scrolling region */ X#define SF _tstr[16] /* scroll forwards */ X#define SR _tstr[17] /* scroll backwards */ X#define TI _tstr[18] /* start cursor mode */ X#define TE _tstr[19] /* end cursor mode */ X#define TA _tstr[20] /* tab char (if not \t) */ X#define CR _tstr[21] /* carriage return (if not \r) */ X#define xPC _tstr[22] /* for reading pad character */ Xchar PC; /* pad character */ Xchar *BC, *UP; /* external variables for tgoto */ X Xstatic char sname[] = "hoclcdceupdousuebtbcaldlcmchcvcssfsrtitetacrpc"; Xchar *_tstr[23]; Xint HOlen; /* length of HO string */ X X X/* terminal flags */ X#define BS _tflg[0] /* can backspace */ X#define AM _tflg[1] /* has auto margins */ X#define XN _tflg[2] /* no newline after wrap */ X#define RET !_tflg[3] /* has carriage return */ X#define NS _tflg[4] /* has SF (scroll forward) */ X#define PT _tflg[5] /* has tabs */ X#define XT _tflg[6] /* tabs are destructive */ Xint GT = 1; /* tab stops on terminal are set */ X Xstatic char bname[] = "bsamxnncnsptxt"; Xchar _tflg[7]; X X Xextern char *tgoto(), *tgetstr(); Xextern char *getenv(), *strcpy(); X X#define ULINE 0200 X X/* Constants accessable by user */ Xint hasscroll; /* scrolling type, 0 == no scrolling */ Xint ROWS; /* number of lines on screen */ Xint COLS; /* width of screen */ X Xstruct line { X short len; /* should really be u_char */ X char flags; X char *l; /* pointer to actual line text, NO NULL @ end */ X}; X Xint _row, _col; Xint _srow, _scol; Xstruct line *_virt; /* what we want the screen to look like */ Xstruct line *_actual; /* What it actually looks like */ Xint _uline = 0; Xint _junked = 1; Xint _curjunked; Xint _dir = 1; Xint _shifttop, _shiftbot; Xint _shift; Xint _scratched; Xint vputc(); X X/* X * Tell refresh to shift lines in region upwards count lines. Count X * may be negative. The virtual image is not shifted; this may change X * later. The variable _scratched is set to supress all attempts to X * shift. X */ X Xushift(top, bot, count) X{ X if (_scratched) X return; X if (_shift != 0 && (_shifttop != top || _shiftbot != bot)) { X _scratched++; X return; X } X _shifttop = top; X _shiftbot = bot; X _shift += count; X} X X/* X * generate a beep on the terminal X */ Xbeep() X{ X vputc('\7'); X} X X/* X * Move to one line below the bottom of the screen. X */ Xbotscreen() X{ X _amove(BOTLINE, 0); X vputc('\n'); X vflush(); X} X Xmove(row, col) X{ X if (row < 0 || row >= ROWS || col < 0 || col >= COLS) X return; X _row = row; X _col = col; X} X X X X/* X * Output string at specified location. X */ Xmvaddstr(row, col, str) Xchar *str; X{ X move(row, col); X addstr(str); X} X Xaddstr(s) Xchar *s; X{ X register char *p; X register struct line *lp; X register int col = _col; X X lp = &_virt[_row]; X if (lp->len < col) { X p = &lp->l[lp->len]; X while (lp->len < col) { X *p++ = ' '; X lp->len++; X } X } X for (p = s; *p != '\0'; p++) { X if (*p == '\n') { X lp->len = col; X lp->flags |= DIRTY; X col = 0; X if (++_row >= ROWS) X _row = 0; X lp = &_virt[_row]; X } X else { X lp->l[col] = *p; X lp->flags |= DIRTY; X if (++col >= COLS) { X lp->len = COLS; X col = 0; X if (++_row >= ROWS) X _row = 0; X lp = &_virt[_row]; X } X } X } X if (lp->len <= col) X lp->len = col; X _col = col; X} X Xaddch(c) X{ X register struct line *lp; X register char *p; X X lp = &_virt[_row]; X if (lp->len < _col) { X p = &lp->l[lp->len]; X while (lp->len < _col) { X *p++ = ' '; X lp->len++; X } X } X lp->l[_col] = c; X if (lp->len == _col) X lp->len++; X if (++_col >= COLS) { X _col = 0; X if (++_row >= ROWS) X _row = 0; X } X lp->flags |= DIRTY; X} X X/* X * Clear an entire line. X */ Xclrline(row) X{ X register struct line *lp; X X lp = &_virt[row]; X if (lp->len > 0) { X lp->len = 0; X lp->flags |= DIRTY; X } X} X Xerase() X{ X register i; X X for (i = 0; i < ROWS; i++) { X _virt[i].len = 0; X _virt[i].flags |= DIRTY; X } X} X Xrefresh() X{ X register i; X register char *p, *q; X register int j, len; X X if (checkin()) X return; X i = 1; X if (_junked) { X _sclear(); X _junked = 0; X } else if (! _scratched) { X if (_shift > 0) { X _ushift(_shifttop, _shiftbot, _shift); X } else if (_shift < 0) { X i = _dshift(_shifttop, _shiftbot, -_shift); X } else { X i = _dir; X } X } X _dir = i; X _shift = 0; X if (checkin()) X return; X _fixlines(); X for (i = _dir > 0 ? 0 : BOTLINE; i >= 0 && i < ROWS; i += _dir) { X if ((_virt[i].flags & DIRTY) == 0) X continue; X _ckclrlin(i); /* decide whether to do a clear line */ X /* probably should consider cd too */ X len = _virt[i].len; X if (_actual[i].len < len) X len = _actual[i].len; X p = _virt[i].l; X q = _actual[i].l; X for (j = 0; j < len; j++) { X if (*p != *q) { X /* Inline test for speed */ X if (i != _srow || j != _scol || _curjunked) X _amove(i, j); X _aputc(*p); X *q = *p; X } X p++; X q++; X } X len = _virt[i].len; X if (_actual[i].len > len) { X _clrtoeol(i, len); X } else { X for (; j < len; j++) { X if (*p != ' ') { X /* Inline test for speed */ X if (i != _srow || j != _scol || _curjunked) X _amove(i, j); X _aputc(*p); X } X *q++ = *p++; X } X _actual[i].len = len; X } X if (checkin()) X return; X } X _dir = 1; X _amove(_row, _col); X vflush(); /* flush output buffer */ X _scratched = 0; X} X X_dshift(top, bot, count) X{ X register i; X X if (count >= bot - top || hasscroll < 4) { /* must have CS or AL/DL */ X _scratched++; X return 1; X } X for (i = bot - count; _actual[i].len == 0; i--) X if (i == top) X return 1; X for (i = top; i <= bot; i++) X _virt[i].flags |= DIRTY; X for (i = bot; i >= top + count; i--) { X /* FIXME, this should be done by recirculating the pointers */ X register j; X j = _actual[i].len = _actual[i - count].len; X _actual[i].flags = _actual[i - count].flags; X strncpy(_actual[i].l, _actual[i - count].l, j); X } X for (; i >= top; i--) X _actual[i].len = 0; X X if (hasscroll != 5) { /* can we define scrolling region, and scroll back */ X tputs(tgoto(CS, bot, top), 1, vputc);/* define scroll region */ X _curjunked = 1; X _amove(top, 0); X for (i = count; --i >= 0;) X tputs(SR, 1, vputc);/* scroll back */ X tputs(tgoto(CS, BOTLINE, 0), 1, vputc); X _curjunked = 1; X } else { X _amove(bot - count + 1, 0); X if (CD && bot == BOTLINE) X tputs(CD, 1, vputc); X else { X for (i = count; --i >= 0;) X tputs(DL, ROWS - _srow, vputc); X } X _amove(top, 0); X for (i = count; --i >= 0;) X tputs(AL, ROWS - _srow, vputc); X } X return -1; X} X X X_ushift(top, bot, count) X{ X register i; X X if (count >= bot - top || hasscroll == 0) { X _scratched++; X return; X } X for (i = top + count; _actual[i].len == 0; i++) X if (i == bot) X return; X if (hasscroll == 1 || hasscroll == 3) { X /* we cheat and shift the entire screen */ X /* be sure we are shifting more lines into than out of position */ X if ((bot - top + 1) - count <= ROWS - (bot - top + 1)) X return; X top = 0, bot = BOTLINE; X } X for (i = top; i <= bot; i++) X _virt[i].flags |= DIRTY; X for (i = top; i <= bot - count; i++) { X /* FIXME, this should be done by recirculating the pointers */ X register int j; X j = _actual[i].len = _actual[i + count].len; X _actual[i].flags = _actual[i + count].flags; X strncpy(_actual[i].l, _actual[i + count].l, j); X } X for (; i <= bot; i++) X for (; i <= bot; i++) X _actual[i].len = 0; X X if (hasscroll != 5) { X if (top != 0 || bot != BOTLINE) { X tputs(tgoto(CS, bot, top), 0, vputc); X _curjunked = 1; X } X _amove(bot, 0); /* move to bottom */ X for (i = 0; i < count; i++) { X if (SF) /* scroll forward */ X tputs(SF, 1, vputc); X else X vputc('\n'); X } X if (top != 0 || bot != BOTLINE) { X tputs(tgoto(CS, BOTLINE, 0), 0, vputc); X _curjunked = 1; X } X } else { X _amove(top, 0); X for (i = count; --i >= 0;) X tputs(DL, ROWS - _srow, vputc); X if (bot < BOTLINE) { X _amove(bot - count + 1, 0); X for (i = count; --i >= 0;) X tputs(AL, ROWS - _srow, vputc); X } X } X} X X_sclear() X{ X register struct line *lp; X X tputs(CL, 0, vputc); X _srow = _scol = 0; X for (lp = _actual; lp < &_actual[ROWS]; lp++) { X lp->len = 0; X } X for (lp = _virt; lp < &_virt[ROWS]; lp++) { X if (lp->len != 0) X lp->flags |= DIRTY; X } X} X X_clrtoeol(row, col) X{ X register struct line *lp = &_actual[row]; X register i; X X if (CE && lp->len > col + 1) { X _amove(row, col); X tputs(CE, 1, vputc); X } else { X for (i = col ; i < lp->len ; i++) { X if (lp->l[i] != ' ') { X _amove(row, i); X _aputc(' '); X } X } X } X lp->len = col; X} X X_fixlines() X{ X register struct line *lp; X register char *p; X register int i; X X for (i = 0; i < ROWS; i++) { X lp = &_virt[i]; X if (lp->flags & DIRTY) { X for (p = &lp->l[lp->len]; --p >= lp->l && *p == ' ';) X ; X lp->len = (int) (p - lp->l) + 1; X if (lp->len == _actual[i].len && strncmp(lp->l, _actual[i].l, lp->len) == 0) X lp->flags &= ~DIRTY; X } X } X} X X X/* X * Consider clearing the line before overwriting it. X * We always clear a line if it has underlined characters in it X * because these can cause problems. Otherwise decide whether X * that will decrease the number of characters to change. This X * routine could probably be simplified with no great loss. X */ X X_ckclrlin(i) X{ X int eval; X int len; X int first; X register struct line *vp, *ap; X register int j; X X if (!CE) X return; X ap = &_actual[i]; X vp = &_virt[i]; X len = ap->len; X eval = -strlen(CE); X if (len > vp->len) { X len = vp->len; X eval = 0; X } X for (j = 0; j < len && vp->l[j] == ap->l[j]; j++) X ; X if (j == len) X return; X first = j; X while (j < len) { X if (vp->l[j] == ' ') { X if (ap->l[j] != ' ') { X while (++j < len && vp->l[j] == ' ' && ap->l[j] != ' ') { X eval++; X } X if (j == len) X eval++; X continue; X } X } X else { X if (vp->l[j] == ap->l[j]) { X while (++j < len && vp->l[j] == ap->l[j]) { X eval--; X } X continue; X } X } X j++; X } X if (US) { X for (j = 0 ; j < ap->len ; j++) { X if (ap->l[j] & ULINE) { X eval = 999; X if (first > j) X first = j; X break; X } X } X } X for (j = first; --j >= 0;) X if (vp->l[j] != ' ') X break; X if (j < 0) X first = 0; X if (eval > 0) { X _amove(i, first); X tputs(CE, 0, vputc); X _actual[i].len = first; X } X} X X X X/* X * Move routine X * first compute direct cursor address string and cost X * then relative motion string and cost, X * then home then relative and cost X * choose smallest and do it. X * X * The plod stuff is to build the strings (with padding) then decide X */ Xstatic char *plodstr; /* current location in relmove string */ X Xplodput(c) X{ X *plodstr++ = c; X} X X/* FIXME: speedup 1-char horiz moves: print the char that's there. */ X/* FIXME: avoid funniness if cm works. */ X/* FIXME: Avoid setul(0) if cursor motion OK in standout (XM?) */ X_amove(row, col) X{ X char direct[20]; X char rel[MAXPLEN*10 + MAXLLEN*10]; /* longest move is full screen */ X char ho[MAXPLEN*10 + MAXLLEN*10]; X int cost, newcost; X register char *movstr; X X if (row == _srow && col == _scol && _curjunked == 0) X return; X if (_uline) X _setul(0); /* Inline test for speed */ X X cost = 999; X if (CM) { X plodstr = direct; X tputs(tgoto(CM, col, row), 0, plodput); X cost = plodstr - direct; X movstr = direct; X } X if (_curjunked == 0) { X plodstr = rel; X if (_vmove(_srow, row) >= 0 X && (plodstr - rel) < cost /* after vmove */ X && _hmove(_scol, col, row) >= 0 X && (newcost = plodstr - rel) < cost) { /* after both */ X cost = newcost; X movstr = rel; X } X } X if (cost > HOlen) { /* is it worth calculating */ X plodstr = ho; X tputs(HO, 0, plodput); X if (_vmove(0, row) >= 0 X && (plodstr - ho) < cost /* after ho, vmove */ X && _hmove(0, col, row) >= 0 X && (newcost = plodstr - ho) < cost) { /* after all three */ X cost = newcost; X movstr = ho; X } X } X X if (cost < 999) X while (--cost >= 0) X vputc(*movstr++); X X _srow = row; X _scol = col; X _curjunked = 0; X} X X_vmove(orow, nrow) X{ X char direct[128]; X char *saveplod = plodstr; X X if (CV) { X plodstr = direct; X tputs(tgoto(CV, nrow, nrow), 0, plodput); X *plodstr = '\0'; X plodstr = saveplod; X } X if (orow > nrow) { /* cursor up */ X if (! UP) X return -1; X while (orow > nrow) { X tputs(UP, 1, plodput); X orow--; X } X } X while (orow < nrow) { /* cursor down */ X if (DO) X tputs(DO, 1, plodput); X else X *plodstr++ = '\n'; X orow++; X } X if (CV && plodstr - saveplod >= strlen(direct)) { X register char *p; X plodstr = saveplod; X for (p = direct ; *plodstr = *p++ ; plodstr++) X ; X } X return 0; X} X X_hmove(ocol, ncol, row) X{ X char direct[128]; X char ret[MAXLLEN*10]; X char *saveplod = plodstr; X char *movstr; X int cost, newcost; X X cost = 999; X if (CH) { X plodstr = direct; X tputs(tgoto(CH, ncol, ncol), 0, plodput); X cost = plodstr - direct; X movstr = direct; X plodstr = saveplod; X } X if (RET && ocol > ncol) { /* consider doing carriage return */ X plodstr = ret; X if (CR) X tputs(CR, 1, plodput); X else X *plodstr++ = '\r'; X if (_relhmove(0, ncol, row) >= 0 X && (newcost = plodstr - ret) < cost) { X cost = newcost; X movstr = ret; X } X plodstr = saveplod; X } X if (_relhmove(ocol, ncol, row) < 0) { X if (cost == 999) X return -1; X goto copy; X } X if (plodstr - saveplod > cost) { Xcopy: plodstr = saveplod; X while (--cost >= 0) X *plodstr++ = *movstr++; X } X return 0; X} X X_relhmove(ocol, ncol, row) X{ X int tab; X X if (ocol < ncol && PT && GT) { /* tab (nondestructive) */ X while ((tab = (ocol + 8) & ~07) <= ncol) { X if (TA) X tputs(TA, 1, plodput); X else X *plodstr++ = '\t'; X ocol = tab; X } X if (tab < COLS && tab - ncol < ncol - ocol) { X if (TA) X tputs(TA, 1, plodput); X else X *plodstr++ = '\t'; X ocol = tab; X } X } else if (BT && GT && ocol > ncol) { /* backwards tab */ X while ((tab = (ocol - 1) &~ 07) >= ncol) { X if (BS && tab == ocol - 1) { X if (BC) X tputs(BC, 1, plodput); X else X *plodstr++ = '\b'; X } else X tputs(BT, 1, plodput); X ocol = tab; X } X if (ncol - tab + 1 < ocol - ncol) { X tputs(BT, 1, plodput); X ocol = tab; X } X } X if (ocol > ncol) { /* cursor left */ X if (! BS) X return -1; X while (ocol > ncol) { X if (BC != NULL) X tputs(BC, 1, plodput); X else X *plodstr++ = '\b'; X ocol--; X } X } X if (ocol < ncol) { /* cursor right */ X register struct line *lp = &_actual[row]; X /* X * This code doesn't move over underlined characters properly, X * but in practice this doesn't seem to matter. X */ X while (ocol < ncol) { X if (ocol < lp->len) X *plodstr++ = lp->l[ocol]; X else X *plodstr++ = ' '; X ocol++; X } X } X return 0; X} X X_aputc(c) X{ X if (_uline != (c & ULINE)) /* Inline for speed */ X _setul(c & ULINE); X if (++_scol >= COLS) { X if (_srow == ROWS - 1) { X /* Don't ever paint last char of last line */ X _scol--; X return; X } X _curjunked++; /* Don't assume AM is right */ X } X vputc(c & ~ULINE); X} X X X_setul(on) X{ X if (on) { X if (_uline == 0 && US != NULL) { X tputs(US, 1, vputc); X _uline = ULINE; X } X } X else { X if (_uline != 0 && UE != NULL) { X tputs(UE, 1, vputc); X _uline = 0; X } X } X} X X/* X * Initialize termcap strings for later use. X */ X X/* X * Hacks to help with some Tek terminals X * rad@tek X */ Xint tputs_len; Xcountit(c) { tputs_len++; } X Xinitterm() X{ X static char tcbuf[1024]; /* termcap buffer */ X register char *cp; X#ifdef USG X struct termio tio; X#else /* !USG */ X struct sgttyb ttyb; X#endif /* !USG */ X X if ((cp = getenv("TERM")) == NULL) X xerror("TERM not set in environment"); X X switch (tgetent(tcbuf, cp)) { X case 0: X xerror("Terminal not found in TERMCAP"); X case -1: X xerror("Can't open /etc/termcap"); X case 1: X break; X } X#ifdef TIOCGWINSZ X { X struct winsize ws; X int winch(); X X COLS = ROWS = -1; X if(ioctl(1, TIOCGWINSZ, &ws) == 0) { X ROWS = ws.ws_row; X COLS = ws.ws_col; X } X if(ROWS <= 0) X ROWS = tgetnum("li"); X if(COLS <= 0) X COLS = tgetnum("co"); X if ((ROWS <= 0) || (COLS <= 0)) X xerror("Can't get screen size"); X X signal(SIGWINCH, winch); /* allow for changing window size */ X } X#else /* !TIOCGWINSZ */ X if ((ROWS = tgetnum("li")) == -1 X || (COLS = tgetnum("co")) == -1) X xerror("Can't get screen size"); X#endif /* !TIOCGWINSZ */ X _zap(); X X if (CL == NULL) X xerror ("No clear screen defined"); X X if (HO == NULL && CM == NULL) X xerror("No home or cursor addressing"); X if (HO) X HOlen = strlen(HO); X else X HOlen = 999; X X PC = xPC ? xPC[0] : 0; X BC = xBC; X UP = xUP; X /* X * _vmove() may be called with a full-screen traverse, X * meaning it will put the UP (along with any padding) into X * the buffer as many as MAXPLEN times. This means that X * if the UP string would be more than 10 chars long (defined X * in _amove() ), the buffer might be overflowed (assuming X * CH is also large). X * This actually occurs with the Tek4023 termcap, where :up=1000UP: X * is used to fake vi into using :cm instead, due to the fact X * that a 4023 can't do upline relative motion at all. X * -rdoty@tek X */ X if (UP) { X tputs_len = 0; X tputs(UP, 1, countit); X if (tputs_len > 10 ) X UP = 0; X } X X if (tgetnum("ug") > 0) X US = UE = NULL; X X if (XT) /* Destructive tab code not included */ X PT = 0; /* to keep things simple */ X X#ifdef USG X if (ioctl(0, TCGETA, &tio) == 0) X GT = tio.c_oflag&TAB3; X#else /* !USG */ X if (ioctl(0, TIOCGETP, &ttyb) == 0) X GT = ttyb.sg_flags&XTABS; X#endif /* !USG */ X X { X char *thelines; X int i; X char *malloc(); X X thelines = malloc(2 * ROWS * COLS); X _virt = (struct line *)malloc(2 * ROWS * sizeof (struct line)); X _actual = _virt + ROWS; X for (i = 0; i < ROWS; i++) { X _virt[i].len = 0; X _virt[i].flags = 0; X _actual[i].len = 0; X _actual[i].flags = 0; X _virt[i].l = thelines; X thelines += COLS; X _actual[i].l = thelines; X thelines += COLS; X } X } X X /* Select article scrolling algorithm. We prefer scrolling region X over insert/delete line because it's faster on the HP */ X hasscroll = 0; X if (!NS) { X hasscroll = 1; X if (SR) X hasscroll = 3; X if (CS) X hasscroll++; X } X if (AL && DL && hasscroll != 4) X hasscroll = 5; X} X Xrawterm() X{ X if (TI != NULL) X tputs(TI, 0, vputc); X} X Xcookedterm() X{ X if (TE != NULL) { X tputs(TE, 0, vputc); X vflush(); X } X} X X/* get strings from termcap */ X_zap() X{ X static char tstrbuf[1024]; X static char *tp; X register char *namp, **sp, *bp; X X tp = tstrbuf; X sp = _tstr; X for (namp = sname; *namp; namp += 2) { X *sp++ = tgetstr(namp, &tp); X } X bp = _tflg; X for (namp = bname; *namp; namp += 2) { X *bp++ = tgetflag(namp, &tp); X } X} X#ifdef TIOCGWINSZ X/* X * window changed size -- update ROWS and COLS X * and then redraw screen X */ Xwinch() X{ X struct winsize ws; X int cols, rows; X X cols = rows = -1; X if(ioctl(1, TIOCGWINSZ, &ws) == 0) { X rows = ws.ws_row; X cols = ws.ws_col; X } X if (rows == ROWS && cols == COLS) { /* just redraw it if no change */ X _junked = 1; /* redraw */ X updscr(); X return; X } X X if(rows > 0) X ROWS = rows; X if(cols > 0) X COLS = cols; X X if (ROWS > MAXPLEN) X ROWS = MAXPLEN; X if (COLS > MAXLLEN) { X COLS = MAXLLEN; X AM = XN = 1; X } X X winch_upd(); X} X#endif /* TIOCGWINSZ */ END_OF_FILE if test 20326 -ne `wc -c <'virtterm.c'`; then echo shar: \"'virtterm.c'\" unpacked with wrong size! fi # end of 'virtterm.c' fi if test -f 'rfuncs.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rfuncs.c'\" else echo shar: Extracting \"'rfuncs.c'\" \(18733 characters\) sed "s/^X//" >'rfuncs.c' <<'END_OF_FILE' X/* X * This software is Copyright (c) 1986 by Rick Adams. X * X * Permission is hereby granted to copy, reproduce, redistribute or X * otherwise use this software as long as: there is no monetary X * profit gained specifically from the use or reproduction or this X * software, it is not sold, rented, traded or otherwise marketed, and X * this copyright notice is included prominently in any copy X * made. X * X * The author make no claims as to the fitness or correctness of X * this software for any use whatsoever, and it is provided as is. X * Any use of this software is at the user's own risk. X * X * rfuncs - functions for readnews. X */ X X#ifdef SCCSID Xstatic char *SccsId = "@(#)rfuncs.c 2.40 2/22/87"; X#endif /* SCCSID */ X X/*LINTLIBRARY*/ X X#include "rparams.h" X Xchar lentab[LINES]; /* length of newsgroupname for each rcline */ Xlong nngsize; /* The next upcoming value of ngsize. */ Xlong nminartno; /* Smallest article number in this group */ Xint BITMAPSIZE = 0; X Xnextng() X{ X long curpos; X#ifdef DEBUG X fprintf(stderr, "nextng()\n"); X#endif X curpos = ftell(actfp); X Xnext: X#ifdef DEBUG X fprintf(stderr, "next:\n"); X#endif X if (actdirect == BACKWARD) { X if (back()) { X (void) fseek(actfp, curpos, 0); X return 1; X } X if (back()) { X (void) fseek(actfp, curpos, 0); X return 1; X } X } X if (fgets(afline, BUFLEN, actfp) == NULL) X return 1; X if (sscanf(afline, "%s %ld %ld", bfr, &nngsize, &nminartno) < 3) { X bfr[0] = '\0'; X nngsize = 0; X nminartno = 0; X } X#ifdef DEBUG X fprintf(stderr, "bfr = '%s'\n", bfr); X#endif X X if (!ngmatch(bfr, header.nbuf)) X goto next; X if (xflag) X readmode = SPEC; X else X readmode = NEXT; X if (selectng(bfr, TRUE, FALSE)) X goto next; X return 0; X} X X Xselectng(name, fastcheck, resubscribe) Xchar *name; X{ X register char *ptr, punct = ','; X register int i; X register char *p; X register long cur; X long next = 0; X FILE *af; X long s, sm; X char buf[100], n[100]; X X#ifdef DEBUG X fprintf(stderr,"selectng: groupdir = %s\n", groupdir); X#endif /* DEBUG */ X if (*groupdir) X updaterc(); X last = 1; X if (strcmp(name, bfr)) { X af = xfopen(ACTIVE, "r"); X while (fgets(buf, sizeof buf, af) != NULL) { X if (sscanf(buf, "%s %ld %ld", n, &s, &sm) == 3 && X strcmp(n, name) == 0) { X ngsize = s; X minartno = sm; X break; X } X } X (void) fclose(af); X } else { X ngsize = nngsize; X minartno = nminartno; X } X#ifdef DEBUG X fprintf(stderr, "selectng(%s) sets ngsize to %ld, minartno to %ld\n", X name, ngsize, minartno); X#endif X (void) strcpy(groupdir, name); X if (!xflag) { X i = findrcline(name); X if (i >= 0) { X if (p = index(rcline[i], '!')) { X switch (resubscribe) { X case FALSE: X groupdir[0] = 0; X return 1; X case TRUE: X *p = ':'; X break; X case PERHAPS: X zapng = TRUE; X break; X } X } else X p = index(rcline[i], ':'); X if (!p) /* shouldn't happen */ X p = rcline[i]; X while (*++p == ' ') X ; X (void) sprintf(rcbuf, "%s%s%ld", rcline[i], X *p == '\0' ? " " : ",", ngsize+1); X } X else X (void) sprintf(rcbuf, "ng: %ld", ngsize+1); X } else X (void) sprintf(rcbuf, "ng: %ld", ngsize+1); X#ifdef DEBUG X fprintf(stderr, "rcbuf set to %s\n", rcbuf); X#endif /* DEBUG */ X X /* X * Fast check for common case: 1-### X */ X if (fastcheck) { X p = rcbuf; X while (*p != ' ') X p++; X while (*p == ' ') X p++; X if (*p++ == '1' && *p++ == '-') { X cur = 0; X while (isdigit(*p)) X cur = 10 * cur + *p++ - '0'; X if (*p == ',' && cur == ngsize) { X#ifdef DEBUG X fprintf(stderr, "Group: %s, all read\n", groupdir); X#endif X groupdir[0] = 0; X return 1; X } X if (cur > ngsize) { X /* X * Claim to have read articles X * which "active" believes have X * never existed - we believe "active" X */ X fprintf(stderr, X "%s %s...\r\n\t%s %ld to %ld\r\n", X "Warning: newsgroup", groupdir, X "last article claimed read reset from", X cur, ngsize); X } X } X } X X/* X * The key to understanding this piece of code is that a bit is set iff X * that article has NOT been read. Thus, we fill in the holes when X * commas are found (e.g. 1-20,30-35 will result in filling in the 21-29 X * holes), and so we assume the newsrc file is properly ordered, the way X * we write it out. X */ X if ((ngsize-minartno) > BITMAPSIZE) { X /* resize the bitmap array */ X (void) free (bitmap); X BITMAPSIZE = 8 * (((ngsize - minartno) + 7) / 8); X bitmap = malloc((unsigned)BITMAPSIZE/8); X if (bitmap == NULL) X xerror("Can't malloc bitmap"); X } X X cur = 0; X bzero(bitmap, (int) (ngsize-minartno)/8+1); /* 8 bits per character */ X X /* Decode the .newsrc line indicating what we have read. */ X for (ptr = rcbuf; *ptr && *ptr != ':'; ptr++) X ; X while (*ptr) { X while (!isdigit(*ptr) && *ptr) X ptr++; X if (!*ptr) X break; X (void) sscanf(ptr, "%ld", &next); X if (punct == ',') { X while (++cur < next) { X set(cur); X } X } X cur = next; X while (!ispunct(*ptr) && *ptr) X ptr++; X punct = *ptr; X } X if (rflag) X bit = ngsize+1; X else X bit = minartno -1; X nextbit(); X ngrp = 1; X return 0; X} X X#ifdef TMAIL Xcatchterm() X{ X (void) unlink(infile); X (void) unlink(outfile); X xxit(0); X} X X X/* X * The -M (Mail) interface. This code is a reasonably simple model for X * writing other interfaces. We write out all relevant articles to X * a temp file, then invoke Mail with an option to have it tell us which X * articles it read. Finally we count those articles as really read. X */ XMail() X{ X register FILE *fp = NULL, *ofp; X struct hbuf h; X register char *ptr, *fname; X int news = 0; X register int i; X X for(i=0;i ngsize) { X if (strpbrk(rcbuf, ":!") == NULL) /* bad line, huh?? */ X return; X ptr = index(rcbuf, ' '); X if (ptr == NULL) /* impossible */ X return; X ptr--; X oldptr = *ptr; X ptr[0] = ':'; X ptr[1] = '\0'; X i = findrcline(groupdir); X if (i >= 0) { X ptr[0] = oldptr; X ptr[1] = ' '; X rcline[i] = realloc(rcline[i], (unsigned)(strlen(rcbuf) + 1)); X if (rcline[i] == NULL) X xerror("Cannot realloc"); X (void) strcpy(rcline[i], rcbuf); X#ifdef DEBUG X fprintf(stderr," new rcline = %s\n", rcline[i]); X#endif /* DEBUG */ X return; X } X if (++line > LINES) X xerror("Too many newsgroups"); X ptr[0] = oldptr; X ptr[1] = ' '; X if ((rcline[line] = malloc((unsigned)(strlen(rcbuf) + 1))) == NULL) X xerror("Not enough memory"); X (void) strcpy(rcline[line], rcbuf); X#ifdef DEBUG X fprintf(stderr," new rcline2 = %s\n", rcline[line]); X#endif /* DEBUG */ X return; X } X cur = next; X goto again; X} X Xnewrc(rcname) Xchar *rcname; X{ X register FILE *fp; X X if (close(creat(rcname, 0666))) { X (void) sprintf(bfr, "Cannot create %s", newsrc); X xerror(bfr); X } X X sprintf(bfr, "%s/users", LIB); X if ((fp = fopen(bfr, "a")) != NULL) { X fprintf(fp, "%s\n", username); X (void) fclose(fp); X (void) chmod(bfr, 0666); X } X} X Xnextbit() X{ X#ifdef DEBUG X fprintf(stderr,"nextbit() bit = %ld\n", bit); X#endif /* DEBUG */ X last = bit; X if (readmode == SPEC || xflag) { X if (rflag) X bit--; X else X bit++; X return; X } X if (rflag) X while (--bit, !get(bit) && bit > minartno) X ; X else X while (++bit, !get(bit) && bit <= ngsize) X ; X#ifdef DEBUG X fprintf(stderr,"nextng leaves bit as %ld\n", bit); X#endif /* DEBUG */ X} X X/* X * Return TRUE if the user has not ruled out this article. X */ Xaselect(hp, insist) Xregister struct hbuf *hp; Xint insist; X{ X if (insist) X return TRUE; X if (tflag && !titmat(hp, header.title)) X return FALSE; X if (aflag && cgtdate(hp->subdate) < atime) X return FALSE; X if (index(hp->nbuf, ',') && !rightgroup(hp)) X return FALSE; X if (fflag && (hp->followid[0] || prefix(hp->title, "Re:"))) X return FALSE; X return TRUE; X} X X/* X * Code to avoid showing multiple articles for news. X * Works even if you exit news. X * Returns nonzero if we should show this article. X */ Xrightgroup(hp) Xstruct hbuf *hp; X{ X char ng[BUFLEN]; X register char *p, *g; X int i, flag; X X strcpy(ng, hp->nbuf); X g = ng; X flag = 1; X while (g != NULL) { X p = index(g, ','); X if (p != NULL) { X *p++ = '\0'; X while (*p == ' ') X p++; X } X if (strcmp(g, groupdir) == 0) X return flag; X if (ngmatch(g, header.nbuf) X && ((i = findrcline(g)) >= 0 X && index(rcline[i], '!') == NULL)) X flag = 0; X g = p; X } X /* we must be in "junk" or "control" */ X return TRUE; X} X Xback() X{ X while (fseek(actfp, -2L, 1) != -1 && ftell(actfp) > 0L) { X if (getc(actfp) == '\n') X return 0; X } X if (ftell(actfp) == 0L) X return 0; X return 1; X} X X/* X * Trap interrupts. X */ Xonsig(n) Xint n; X{ X (void) signal(n, onsig); X SigTrap = n; X if (rcreadok < 2) { X fprintf(stderr, "Aborted early\n"); X xxit(0); X } X} X X/* X * finds the line in your .newsrc file (actually the in-core "rcline" X * copy of it) and returns the index into the array where it was found. X * -1 means it didn't find it. X * X * We play clever games here to make this faster. It's inherently X * quadratic - we spend lots of CPU time here because we search through X * the whole .newsrc for each line. The "prev" variable remembers where X * the last match was found; we start the search there and loop around X * to the beginning, in the hopes that the calls will be roughly in order. X */ Xint Xfindrcline(name) Xregister char *name; X{ X register char * p; X register int i; X register int top; X register int len; X static int prev; X static int didthru; X X for ( ; didthru <= line; ++didthru) X if ((p = index(rcline[didthru], '!')) != 0 || X (p = index(rcline[didthru], ':')) != 0) { X lentab[didthru] = (int)(p - rcline[didthru]); X } X len = strlen(name); X top = line; X i = prev; Xloop: X for ( ; i <= top; ++i) X if (lentab[i] == len && rcline[i] != NULL && X strncmp(name, rcline[i], len) == 0) X return prev = i; X if (i > line && line > prev - 1) { X i = 0; X top = prev - 1; X goto loop; X } X return -1; X} X X/* X * sortactive - make a local copy of the active file, sorted according X * to the user's preferences, according to his .newsrc file. X */ X Xstruct table_elt { X int rcindex; X long maxart, minart; X char yn; X}; X X#ifdef SORTACTIVE Xstatic int Xrcsort(a, b) Xchar *a, *b; X{ X return(((struct table_elt *)a)->rcindex - X ((struct table_elt *)b)->rcindex); X} X Xstatic char *newactivename = "/tmp/newsaXXXXXX"; X#endif /* SORTACTIVE */ X Xsortactive() X{ X register struct table_elt *tp; X register char *p; X register FILE *nfp, *afp; X char aline[BUFLEN], ngname[BUFLEN]; X struct table_elt table[LINES]; X int nlines = 0, i, delta, lastline; X X#ifdef SORTACTIVE X /* make a new sorted copy of ACTIVE */ X nfp = fopen(mktemp(newactivename), "w"); X (void) chmod(newactivename, 0600); X if (nfp == NULL) { X perror(newactivename); X return; X } X X /* look up all the lines in ACTIVE, finding their positions in .newsrc */ X p = ACTIVE; X ACTIVE = newactivename; X afp = xfopen(p, "r"); X#else /* !SORTACTIVE */ X afp = xfopen(ACTIVE, "r"); X#endif /* !SORTACTIVE */ X tp = table; X while (fgets(aline, sizeof aline, afp) != NULL) { X if (sscanf(aline,"%s %ld %ld %c", ngname, &tp->maxart, X &tp->minart, &tp->yn) != 4) X xerror("Active file corrupt"); X delta = tp->maxart - tp->minart; X if (delta >= BITMAPSIZE) X BITMAPSIZE = delta + 1; X if (Kflag && tp->maxart > 0 && ngmatch(ngname, header.nbuf)) { X int j; X X j = findrcline(ngname); X if (j >= 0 && index(rcline[j], '!') == NULL) { X char rbuf[BUFLEN]; X if (tp->maxart == 1) X sprintf(rbuf, "%s: 1", ngname); X else X sprintf(rbuf, "%s: 1-%ld", ngname, tp->maxart); X rcline[j] = realloc(rcline[j], X (unsigned)(strlen(rbuf)+1)); X if (rcline[j] == NULL) X xerror("Not enough memory"); X strcpy(rcline[j], rbuf); X } X } X#ifdef SORTACTIVE X tp->rcindex = findrcline(ngname); X if (tp->rcindex < 0) { X if (++line > LINES) X xerror("Too many newsgroups"); X strcat(ngname, ":"); X rcline[line] = malloc((unsigned)(strlen(ngname) + 1)); X if (rcline[line] == NULL) X xerror("Not enough memory"); X strcpy(rcline[line], ngname); X tp->rcindex = line; X } X tp++; X#endif /* SORTACTIVE */ X } X (void) fclose(afp); X BITMAPSIZE = 8 * ((BITMAPSIZE+7) / 8); X bitmap = malloc((unsigned)BITMAPSIZE/8); X if (bitmap == NULL) X xerror("Can't malloc bitmap"); X X#ifdef SORTACTIVE X /* sort by position in user's .newsrc file (new groups come up last) */ X nlines = tp - table; X qsort((char *)table, nlines, sizeof table[0], rcsort); X X tp = table; X lastline = tp->rcindex - 1; X /* copy active to newactive, in the new order */ X for (i = 0; i < nlines; i++) { X while (++lastline < tp->rcindex) { X if (strncmp(rcline[lastline], "options ", 8) == 0) { X fprintf(nfp, "%s\n", rcline[lastline]); X } else { X fprintf(stderr, "Duplicate .newsrc line or bad group %s\n", X rcline[lastline]); X lentab[lastline] = 0; X free(rcline[lastline]); X rcline[lastline] = NULL; X } X } X if (rcline[tp->rcindex] == NULL) X continue; X p = rcline[tp->rcindex]; X while (*p != ':' && *p != '!') X fputc(*p++, nfp); X (void) fprintf(nfp, " %ld %ld %c\n", tp->maxart, tp->minart, X tp->yn); X tp++; X } X (void) fclose(nfp); X#endif /* SORTACTIVE */ X} X X#if defined(BSD4_2) || defined(BSD4_1C) X#include X# else X#include "ndir.h" X#endif X#include X X/* X * Routine to display header lines for all articles in newsgroup. If the flag X * argument is FALSE then only articles which are not marked as read in the X * bitmap will be displayed. This routine makes no attempt to determine if X * the article is in multiple groups and therefore should not be displayed at X * this time. X */ X Xstatic int *lg_array = NULL; Xstatic int *lg_entry; Xstatic int lg_max = 0; Xstatic int int_sig; Xextern int errno; X Xlg_cmp(p1, p2) Xint *p1, *p2; X{ X return *p1 - *p2; X} X Xlist_group(lgroup, displines, flag, pngsize) Xchar *lgroup; Xint displines, flag; Xlong pngsize; X{ X char *briefdate(); X struct hbuf hh; X register DIR *dirp; X register struct direct *dir; X register FILE *fp_art; X int i; X int entries; X unsigned int alloc_size; X int (*old_sig) (); X extern lg_trap(); X char *gets(); X X /* This should get the numbers from the active file XXX */ X if ((dirp = opendir(dirname(lgroup))) == NULL) { X printf("Can't open %s\r\n", dirname(lgroup)); X return; X } X entries = 0; X if (lg_array == NULL) { X lg_max = 50; X alloc_size = lg_max * sizeof(int); X lg_array = (int *) malloc(alloc_size); X } X while ((dir = readdir(dirp)) != NULL) { X if (dir->d_ino == 0) X continue; X i = atoi(dir->d_name); X if ((i < 1) || (i > pngsize)) X continue; X if (flag == FALSE) { X if (get(i) == 0) X continue; X } X if (++entries > lg_max) { X lg_max += 50; X alloc_size = lg_max * sizeof(int); X lg_array = (int *) realloc((char *) lg_array, alloc_size); X } X lg_array[entries - 1] = i; X } X if (entries == lg_max) { X lg_max++; X alloc_size = lg_max * sizeof(int); X lg_array = (int *) realloc((char *) lg_array, alloc_size); X } X qsort(lg_array, entries, sizeof *lg_array, lg_cmp); X lg_array[entries] = 0; X int_sig = 0; X old_sig = signal(SIGINT, lg_trap); X hh.unrec[0] = NULL; X for (lg_entry = lg_array; *lg_entry != 0 && int_sig == 0; lg_entry++) { X (void) sprintf(filename, "%s/%d", dirname(lgroup), *lg_entry); X fp_art = fopen(filename, "r"); X if (fp_art == NULL) X continue; X if (hread(&hh, fp_art, TRUE) == NULL) { X (void) fclose(fp_art); X continue; X } X printf("%5d %-20.20s %-13s %s\r\n", X *lg_entry, hh.from, X briefdate(hh.subdate), hh.title); X for (i = 0; i < displines;) { X if (fgets(bfr, LBUFLEN, fp_art) == NULL) { X break; X } X if ((bfr[0] == '\n') || (bfr[0] == '>')) { X continue; X } X printf("%s", bfr); X i++; X } X (void) fclose(fp_art); X } X (void) fflush(stdout); X X closedir(dirp); X (void) signal(SIGINT, old_sig); /* restore to old value */ X X printf("[Press RETURN to continue]"); X (void) fflush(stdout); X X while (TRUE) { X errno = 0; X i = getchar(); X if (errno == EINTR) X continue; X if (i == '\n' || i == '\r') X break; X if (i == EOF) X break; X if (i == '\4') X break; X } X (void) free(lg_array); X lg_array = NULL; X X} X Xlg_trap(code) Xint code; X{ X X int_sig = 1; X (void) signal(code, lg_trap); /* reset signal */ X X} END_OF_FILE if test 18733 -ne `wc -c <'rfuncs.c'`; then echo shar: \"'rfuncs.c'\" unpacked with wrong size! fi # end of 'rfuncs.c' fi if test -f 'header.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'header.c'\" else echo shar: Extracting \"'header.c'\" \(16672 characters\) sed "s/^X//" >'header.c' <<'END_OF_FILE' X/* X * This software is Copyright (c) 1986 by Rick Adams. X * X * Permission is hereby granted to copy, reproduce, redistribute or X * otherwise use this software as long as: there is no monetary X * profit gained specifically from the use or reproduction or this X * software, it is not sold, rented, traded or otherwise marketed, and X * this copyright notice is included prominently in any copy X * made. X * X * The author make no claims as to the fitness or correctness of X * this software for any use whatsoever, and it is provided as is. X * Any use of this software is at the user's own risk. X * X * header.c - header functions plus some other goodies X */ X X#ifdef SCCSID Xstatic char *SccsId = "@(#)header.c 2.48 3/20/87"; X#endif /* SCCSID */ X X#include X#include "params.h" X#include "patchlevel.h" X Xchar *hfgets(); X Xchar *news_version = NEWS_VERSION; X X/* X * Read header from file fp into *hp. If wholething is FALSE, X * it's an incremental read, otherwise start from scratch. X * Return (FILE *) if header okay, else NULL. X */ XFILE * Xhread(hp, fp, wholething) Xregister struct hbuf *hp; XFILE *fp; Xint wholething; X{ X#ifndef GENERICPATH X register int len; X#endif /* GENERICPATH */ X register int i; X#ifdef OLD X char *p; X#endif /* OLD */ X X if (wholething) { X for(i=0;iunrec[i] != NULL) X free(hp->unrec[i]); X else X break; X bzero((char *)hp, sizeof (*hp)); X for (i=0;iunrec[i] = NULL; X } X X /* Check that it's a B news style header. */ X if (hfgets(bfr, PATHLEN, fp) != NULL && isalpha(bfr[0]) X && index(bfr, ':')) X if (frmread(fp, hp)) X goto strip; X X if (!nstrip(bfr+1)) X return NULL; X X /* It's not. Try A news (begins with PROTO). */ X if (bfr[0] != PROTO) X return NULL; X#ifndef OLD X logerr("Can not process A news format article without OLD defined"); X#else /* OLD */ X /* Read in an A news format article. */ X p = index(bfr+1, '.'); X if (p == NULL) { X (void) strcpy(hp->ident, bfr+1); X return NULL; X } X *p++ = '\0'; X (void) sprintf(hp->ident, "<%s@%s%s>", p, bfr+1, ".UUCP"); X X /* Newsgroup List */ X if (hfgets(hp->nbuf, BUFLEN, fp) == NULL || !nstrip(hp->nbuf)) X return NULL; X /* source path */ X if (hfgets(hp->path, PATHLEN, fp) == NULL || !nstrip(hp->path)) X return NULL; X /* date */ X if (hfgets(hp->subdate, DATELEN, fp) == NULL || !nstrip(hp->subdate)) X return NULL; X /* title */ X if (hfgets(hp->title, BUFLEN, fp) == NULL || !nstrip(hp->title)) X return NULL; X#endif /* OLD */ X Xstrip: /* strip off sys! from front of path. */ X#ifndef GENERICPATH X if (strncmp(PATHSYSNAME, hp->path, (len = strlen(PATHSYSNAME))) == 0 X && index(NETCHRS, hp->path[len])) X (void) strcpy(hp->path, &(hp->path[len+1])); X#endif /* GENERICPATH */ X lcase(hp->nbuf); X X /* Intuit the From: line if only a path was given. */ X if (wholething) { X#ifdef OLD X if (hp->from[0] == '\0') X intuitfrom(hp); X else X#endif /* OLD */ X fixfrom(hp); X } X X return fp; X} X X X/* X * Get header info from mail-format file. X * Return non-zero on success. X */ X#define FROM 1 X#define NEWSGROUP 2 X#define TITLE 3 X#define SUBMIT 4 X#define RECEIVE 5 X#define EXPIRE 6 X#define ARTICLEID 7 X#define MESSAGEID 8 X#define REPLYTO 9 X#define FOLLOWID 10 X#define CONTROL 11 X#define SENDER 12 X#define FOLLOWTO 13 X#define PATH 14 X#define POSTVERSION 15 X#define RELAYVERSION 16 X#define DISTRIBUTION 17 X#define ORGANIZATION 18 X#define NUMLINES 19 X#define KEYWORDS 20 X#define APPROVED 21 X#define NFID 22 X#define NFFROM 23 X#define XREF 24 X#define SUMMARY 25 X#define XPATH 26 X#define OTHER 99 X Xchar *malloc(); X Xfrmread(fp, hp) Xregister FILE *fp; Xregister struct hbuf *hp; X{ X int unreccnt = 0; X register int i; X long curpos; X X i = type(bfr); X do { X curpos = ftell(fp); X switch (i) { X case PATH: X getfield(hp->path, sizeof(hp->path)); X break; X case FROM: X getfield(hp->from, sizeof(hp->from)); X break; X case NEWSGROUP: X getfield(hp->nbuf, sizeof(hp->nbuf)); X break; X case TITLE: X getfield(hp->title, sizeof(hp->title)); X break; X case SUBMIT: X getfield(hp->subdate, sizeof(hp->subdate)); X break; X case EXPIRE: X getfield(hp->expdate, sizeof(hp->expdate)); X break; X#ifdef OLD X case ARTICLEID: X /* Believe Message-ID in preference to Article-ID */ X if (hp->ident[0] == '\0') { X register char *p; X char msgb[NAMELEN]; X getfield(msgb, sizeof msgb); X p = index(msgb, '.'); X if (p == NULL) { X (void) strcpy(hp->ident, msgb); X } else { X *p++ = '\0'; X (void) sprintf(hp->ident, "<%s@%s%s>", p, msgb, ".UUCP"); X } X } X break; X#endif /* OLD */ X case MESSAGEID: X getfield(hp->ident, sizeof(hp->ident)); X break; X case REPLYTO: X getfield(hp->replyto, sizeof(hp->replyto)); X break; X case FOLLOWID: X getfield(hp->followid, sizeof(hp->followid)); X break; X case SENDER: X getfield(hp->sender, sizeof(hp->sender)); X break; X case FOLLOWTO: X getfield(hp->followto, sizeof(hp->followto)); X break; X case CONTROL: X getfield(hp->ctlmsg, sizeof(hp->ctlmsg)); X break; X case DISTRIBUTION: X getfield(hp->distribution, sizeof(hp->distribution)); X if (strcmp(hp->distribution, "net") == 0) X hp->distribution[0] = '\0'; X break; X case ORGANIZATION: X getfield(hp->organization, sizeof(hp->organization)); X break; X case NUMLINES: X getfield(hp->numlines, sizeof(hp->numlines)); X hp->intnumlines = atoi(hp->numlines); X break; X case KEYWORDS: X getfield(hp->keywords, sizeof(hp->keywords)); X break; X case APPROVED: X getfield(hp->approved, sizeof(hp->approved)); X break; X case NFID: X getfield(hp->nf_id, sizeof(hp->nf_id)); X break; X case NFFROM: X getfield(hp->nf_from, sizeof(hp->nf_from)); X break; X /* discard these lines */ X case XREF: X case XPATH: X case RELAYVERSION: X case POSTVERSION: X case RECEIVE: X break; X case SUMMARY: X getfield(hp->summary, sizeof(hp->summary)); X break; X case OTHER: X if (unreccnt < NUNREC) { X if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) { X (void) strcpy(hp->unrec[unreccnt], bfr); X (void) nstrip(hp->unrec[unreccnt]); X unreccnt++; X } else X xerror("frmread: out of memory"); X } X break; X } X } while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0); X X if (*bfr != '\n') X fseek(fp, curpos, 0); X if ((hp->from[0] || hp->path[0]) && hp->subdate[0] && hp->ident[0]) X return TRUE; X return FALSE; X} X X#ifdef OLD X/* X * There was no From: line in the message (because it was generated by X * an old news program). Guess what it should have been and create it. X */ Xintuitfrom(hp) Xregister struct hbuf *hp; X{ X char *tp; X char *user, *host; X char *tailpath(), *rindex(); X char *at, *dot; X char pathbuf[PATHLEN]; X char fullname[BUFLEN]; X extern char *mydomain(); X X tp = tailpath(hp); X user = rindex(tp, '!'); X if (user == NULL) X user = tp; X else X *user++ = '\0'; X X /* Check for an existing Internet address on the end. */ X at = index(user, '@'); X if (at) { X dot = index(at, '.'); X if (dot) { X (void) strcpy(hp->from, user); X return; X } X /* @ signs are illegal except for the biggie, so */ X *at = '%'; X } X X if (tp[0] == '.') X host = index(tp, '!') + 1; X else if (user == tp) X host = FROMSYSNAME; X else X host = tp; X X tp = index(host, '@'); X if (tp != NULL) X *tp = 0; X if (index(host, '.') != NULL) X (void) sprintf(hp->from, "%s@%s%s", user, host, mydomain()); X else X (void) sprintf(hp->from, "%s@%s", user, host); X X skin(pathbuf, fullname, hp->path); /* remove RFC822-style comments */ X if (fullname[0] != '\0') { X strcat(hp->from, " ("); X (void) strcat(hp->from, fullname); X strcat(hp->from, ")"); X } X strcpy(hp->path, pathbuf); /* and stick it back in */ X} X#endif /* OLD */ X X/* X * Canonicalize the "From:" line into the form X * X * From: (full-name) X * X * RFC822 doesn't require the comment to be at the end of the string X * like that. X */ Xfixfrom(hp) Xregister struct hbuf *hp; X{ X char frombuf[PATHLEN]; X char fullname[BUFLEN]; X X skin(frombuf, fullname, hp->from); /* remove RFC822-style comments */ X if (fullname[0] != '\0') { X strcat(frombuf, " ("); X strcat(frombuf, fullname); X strcat(frombuf, ")"); X } X strcpy(hp->from, frombuf); /* stick the canonicalized "from" back in */ X} X Xskin(name, fullname, hfield) Xchar *name; Xchar *fullname; Xchar *hfield; X{ X register int c; X register char *cp, *cp2; X char *bufend; X int gotlt, parenlev, lastsp; X int seenfullname = FALSE; X X *fullname = '\0'; /* no full name yet */ X if (strpbrk(hfield, "(< ") == NULL) { /* include ',' ?? */ X strcpy(name, hfield); X return; X } X gotlt = 0; X parenlev = 0; X lastsp = 0; X bufend = name; X for (cp = hfield, cp2 = bufend; c = *cp++; ) { X switch (c) { X case '(': X /* X * Start of a "comment". X * Ignore it, or save it in "fullname" if we haven't X * seen a comment yet. X */ X parenlev++; X while ((c = *cp) != 0) { X cp++; X switch (c) { X case '\\': X if ((c = *cp) == 0) X goto outcm; X cp++; X break; X case '(': X parenlev++; X break; X case ')': X parenlev--; X if (parenlev == 0) X goto outcm; X break; X } X if (!seenfullname) X *fullname++ = c; X } X outcm: X parenlev = 0; X lastsp = 0; X if (!seenfullname) { X *fullname = '\0'; X seenfullname = TRUE; /* only extract first comment */ X } X break; X X case '"': X /* X * Start of a "quoted-string". X * Copy it in its entirety. X */ X while ((c = *cp) != 0) { X cp++; X switch (c) { X case '\\': X if ((c = *cp) == 0) X goto outqs; X cp++; X break; X case '"': X goto outqs; X } X *cp2++ = c; X } X outqs: X lastsp = 0; X break; X X case ' ': X if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') X cp += 3, *cp2++ = '@'; X else X if (cp[0] == '@' && cp[1] == ' ') X cp += 2, *cp2++ = '@'; X else X lastsp = 1; X break; X X case '<': X if (!seenfullname) { X *cp2 = '\0'; X strcpy(fullname, name); X seenfullname = TRUE; X } X cp2 = bufend; X gotlt++; X lastsp = 0; X break; X X case '>': X if (gotlt) { X gotlt = 0; X /* X * this doesn't seem reasonable, what about (,) X * or "," ?? X */ X while (*cp != ',' && *cp != 0) X cp++; X if (*cp == 0 ) X goto done; X *cp2++ = ','; X *cp2++ = ' '; X bufend = cp2; X break; X } X X /* Fall into . . . */ X X default: X if (lastsp) { X lastsp = 0; X *cp2++ = ' '; X } X *cp2++ = c; X break; X } X } Xdone: X *cp2 = 0; X} X X X#ifdef OLD Xchar * Xoident(ident) Xchar *ident; X{ X char lbuf[BUFLEN]; X static char oidbuf[BUFLEN]; X register char *p, *q; X X (void) strcpy(lbuf, ident); X p = index(lbuf, '@'); X if (p == NULL) X return ident; X *p++ = '\0'; X q = index(p, '.'); X if (q == NULL) X q = index(p, '>'); X if (q) X *q++ = '\0'; X p[SNLN] = '\0'; X (void) sprintf(oidbuf, "%s.%s", p, lbuf+1); X return oidbuf; X} X#endif /* OLD */ X X/* X * Get the given field of a header (char * parm) from bfr, but only X * if there's something actually there (after the colon). Don't X * bother if we already have an entry for this field. X */ Xgetfield(hpfield, size) Xchar *hpfield; Xint size; X{ X register char *ptr; X X if (hpfield[0]) X return; X for (ptr = index(bfr, ':'); isspace(*++ptr); ) X ; X if (*ptr != '\0') { X (void) strncpy(hpfield, ptr, size - 1); X (void) nstrip(hpfield); X } X} X X X#define its(type) (prefix(ptr, type)) Xtype(ptr) Xregister char *ptr; X{ X register char *colon, *space; X X if (ptr == NULL) X return FALSE; X if (its("From: ")) X if (index(ptr, '@') || !index(ptr, '!')) X return FROM; X else X return PATH; X if (its("Path: ")) X return PATH; X if (its("Newsgroups: ")) X return NEWSGROUP; X if (its("Subject: ")) X return TITLE; X if (its("Date: ")) X return SUBMIT; X if (its("Date-Received: ")) X return RECEIVE; X#ifdef OLD X if (its("Title: ")) X return TITLE; X if (its("Posted: ")) X return SUBMIT; X if (its("Received: ")) X return RECEIVE; X#endif /* OLD */ X if (its("Expires: ")) X return EXPIRE; X if (its("Article-I.D.: ")) X return ARTICLEID; X if (its("Message-ID: ")) X return MESSAGEID; X if (its("Reply-To: ")) X return REPLYTO; X if (its("References: ")) X return FOLLOWID; X if (its("Control: ")) X return CONTROL; X if (its("Sender: ")) X return SENDER; X if (its("Followup-To: ")) X return FOLLOWTO; X if (its("Posting-Version: ")) X return POSTVERSION; X if (its("Relay-Version: ")) X return RELAYVERSION; X if (its("Distribution: ")) X return DISTRIBUTION; X if (its("Organization: ")) X return ORGANIZATION; X if (its("Lines: ")) X return NUMLINES; X if (its("Summary: ")) X return SUMMARY; X if (its("Keywords: ")) X return KEYWORDS; X if (its("Approved: ")) X return APPROVED; X if (its("Nf-ID: ")) X return NFID; X if (its("Nf-From: ")) X return NFFROM; X if (its("Xref: ")) X return XREF; X if (its("Xpath: ")) X return XPATH; X if (!isalpha(*ptr)) X return FALSE; X colon = index(ptr, ':'); X space = index(ptr, ' '); X if (!colon || space && space < colon) X return FALSE; X return OTHER; X} X X/* X * Write header at 'hp' on stream 'fp' in B+ format. Include received date X * if wr is 1. Leave off sysname if wr is 2. X */ Xihwrite(hp, fp, wr) Xregister struct hbuf *hp; Xregister FILE *fp; Xint wr; X{ X int iu; X time_t t; X time_t cgtdate(); X X /* X * We're being tricky with Path/From because of upward compatibility X * issues. The new version considers From and Path to be separate. X * The old one thinks they both mean "Path" but only believes the X * first one it sees, so will ignore the second. X */ X if (prefix(hp->path, PATHSYSNAME) && X index(NETCHRS, hp->path[strlen(PATHSYSNAME)])) X fprintf(fp, "Path: %s\n", hp->path); X else X fprintf(fp, "Path: %s!%s\n", PATHSYSNAME, hp->path); X if (hp->from[0]) X fprintf(fp, "From: %s\n", hp->from); X X fprintf(fp, "Newsgroups: %s\n", hp->nbuf); X fprintf(fp, "Subject: %s\n", hp->title); X if (*hp->summary) X fprintf(fp, "Summary: %s\n", hp->summary); X if (*hp->keywords) X fprintf(fp, "Keywords: %s\n", hp->keywords); X fprintf(fp, "Message-ID: %s\n", hp->ident); X t = cgtdate(hp->subdate); X fprintf(fp, "Date: %s\n", arpadate(&t)); X#ifdef OLD X fprintf(fp, "Article-I.D.: %s\n", oident(hp->ident)); X fprintf(fp, "Posted: %s", ctime(&t)); X#endif /* OLD */ X if (*hp->expdate) X fprintf(fp, "Expires: %s\n", hp->expdate); X if (*hp->followid) { X register char *dp, *cp; X X dp = cp = hp->followid; X while (*cp != '\0') X if (*cp == '<' && *(cp + 1) == '>') X cp += 2; X else X *dp++ = *cp++; X *dp = '\0'; X if (*hp->followid) X fprintf(fp, "References: %s\n", hp->followid); X } X if (*hp->ctlmsg) X fprintf(fp, "Control: %s\n", hp->ctlmsg); X if (*hp->sender) X fprintf(fp, "Sender: %s\n", hp->sender); X if (*hp->replyto) X fprintf(fp, "Reply-To: %s\n", hp->replyto); X if (*hp->followto) X fprintf(fp, "Followup-To: %s\n", hp->followto); X if (*hp->distribution) X fprintf(fp, "Distribution: %s\n", hp->distribution); X if (*hp->organization) X fprintf(fp, "Organization: %s\n", hp->organization); X if (*hp->numlines) X fprintf(fp, "Lines: %s\n", hp->numlines); X if (*hp->approved) X fprintf(fp, "Approved: %s\n", hp->approved); X if (*hp->nf_id) X fprintf(fp, "Nf-ID: %s\n", hp->nf_id); X if (*hp->nf_from) X fprintf(fp, "Nf-From: %s\n", hp->nf_from); X#ifdef DOXREFS X if ( wr ==1 && *hp->xref) X fprintf(fp, "Xref: %s\n", hp->xref); X#endif /* DOXREFS */ X for (iu = 0; iu < NUNREC; iu++) { X if (hp->unrec[iu]) X fprintf(fp, "%s\n", &hp->unrec[iu][0]); X } X putc('\n', fp); X} X X X#ifndef BSD4_2 X/* X * Set nc bytes, starting at cp, to zero. X */ Xbzero(cp, nc) Xregister char *cp; Xregister int nc; X{ X if (nc > 0) X do { X *cp++ = 0; X } while (--nc); X} X#endif /* !BSD4_2 */ X X/* X * hfgets is like fgets, but deals with continuation lines. X * It also ensures that even if a line that is too long is X * received, the remainder of the line is thrown away X * instead of treated like a second line. X */ Xchar * Xhfgets(buf, len, fp) Xchar *buf; Xint len; XFILE *fp; X{ X register int c; X register int n = 0; X register char *cp; X X cp = buf; X while (n < len && (c = getc(fp)) != EOF) { X if (c == '\n') X break; X if (isprint(c) || c == '\b' || c == ' ' || c == '\t') { X *cp++ = c; X n++; X } X } X if (c == EOF && cp == buf) X return NULL; X *cp = '\0'; X X if (c != '\n') { X /* Line too long - part read didn't fit into a newline */ X while ((c = getc(fp)) != '\n' && c != EOF) X ; X } else if (cp == buf) { X /* Don't look for continuation of blank lines */ X *cp++ = '\n'; X *cp = '\0'; X return buf; X } X X while ((c = getc(fp)) == ' ' || c == '\t') { /* for each cont line */ X /* Continuation line. */ X if ((n += 2) < len) { X *cp++ = '\n'; X *cp++ = c; X } X while ((c = getc(fp)) != '\n' && c != EOF) X if ((isprint(c) || c == '\b' || c == ' ' || c == '\t') X && n++ < len) X *cp++ = c; X } X if (n >= len - 1) X cp = buf + len - 2; X *cp++ = '\n'; X *cp = '\0'; X if (c != EOF) X (void) ungetc(c, fp); /* push back first char of next header */ X return buf; X} END_OF_FILE if test 16672 -ne `wc -c <'header.c'`; then echo shar: \"'header.c'\" unpacked with wrong size! fi # end of 'header.c' fi echo shar: End of shell archive. exit 0 -- "Zeta Microcomputer Software" ACSnet: nick@ultima.cs.uts.oz UUCP: ...!uunet!munnari!ultima.cs.uts.oz!nick Fidonet: Nick Andrew on 3:713/602 (Zeta)