/* @(#)p.c 1.75 21/08/20 Copyright 1985-2021 J. Schilling */ #include #ifndef lint static UConst char sccsid[] = "@(#)p.c 1.75 21/08/20 Copyright 1985-2021 J. Schilling"; #endif /* * Print some files on screen * * Copyright (c) 1985-2021 J. Schilling */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * A copy of the CDDL is also available via the Internet at * http://www.opensource.org/licenses/cddl1.txt * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define ungetch dos_ungetch /* Avoid DOS/curses ungetch() type clash */ #include #undef ungetch /* Restore our old value */ #include #include /* for MB_LEN_MAX */ #include /* For isprint() */ #include /* wchar_t */ #include /* For iswprint() */ #include #define GT_COMERR /* #define comerr gtcomerr */ #define GT_ERROR /* #define error gterror */ #include #define SEARCHSIZE 256 #define DEF_PSIZE 24; #define DEF_LWIDTH 80; #if MB_LEN_MAX > 1 /* * We are on a platform that supports multi byte characters. */ #define nextc() nextwc() /* Read next char */ #define peekc() peekwc() /* Peek next char */ #define getnextch() getnextwch() /* Consume last peeked char */ #define ungetch(c) ungetwch(c) /* Unread char back to buffer */ #else #define nextc() (--len >= 0 ? (int) *bp++ : \ (fill_buf() <= 0 ? (len == 0 ? EOF : -2) : \ (len--, (int) *bp++))) #define peekc() (len > 0 ? (int) *bp : \ (fill_buf() <= 0 ? (len == 0 ? EOF : -2) : \ ((int) *bp))) #define getnextch() (len--, bp++) #define __peekc() (ungetch(nextc())) #endif # ifdef USE_V7_TTY struct sgttyb old; struct sgttyb new; # else /* USE_V7_TTY */ #ifdef USE_TERMIOS struct termios old; struct termios new; #endif # endif /* USE_V7_TTY */ int tty = -1; FILE *f; /* the file being printed */ off_t ofpos; /* saved filepos for searching */ int lineno; /* current lineno (used for editing) */ int colcnt; /* column on screen we are going to print */ int linecnt; /* # of lines actually printed on screen */ int lwidth; /* length of a line on screen */ int lines; /* # of lines until we print a more prompt */ int psize; /* # of lines on a page */ int supressblank; /* supress multiple blank lines on output */ int clear; /* clear screen before displaying page */ int dosmode; /* wether to supress ^M^J sequences */ BOOL autodos = TRUE; /* whether to automatically detect DOS mode */ int endline; /* print a $ at each end of line */ int raw; /* do not expand control characters */ BOOL raw8; /* Ausgabe ohne ~ */ int silent; /* in silent mode there is no more prompt */ int tab; /* do nut expand tabs to spaces but to ^I */ int visible; /* _^H sequences are visible */ int underline; /* do underlining */ int ununderline; /* remove underlining */ int help; /* print online help */ int prvers; /* print version information */ BOOL debug; /* misc debug flag */ BOOL nobeep; /* be silent on errors */ char *filename; /* Filename fuer Ausgabezwecke */ int direction; /* direction to step through file list */ #define FORWARD 0 char *editor; char *shell; int searchnext; #if MB_LEN_MAX > 1 wchar_t searchbuf[SEARCHSIZE]; #else char searchbuf[SEARCHSIZE]; #endif int alt; int *aux; int *state; char *so; /* start standout */ char *se; /* end standout */ char *xso; /* start abstract bold/standout */ char *xse; /* end abstract bold/standout */ char *us; /* start underline */ char *ue; /* end underline */ char *md; /* start bold */ char *me; /* end attributes */ char *ce; /* clear endline */ char *cl; /* clear screen */ int li; /* lines on screen */ int co; /* columns on screen */ BOOL am = TRUE; /* automatic margins */ BOOL xn; /* newline ignored > 80 */ BOOL has_standout; BOOL has_bold; BOOL has_ul; BOOL has_termcap; int standout; int underl; /* * As an incomplete multi byte character after a buffer refill * is partially before the begin of the buffer, we need to be * able to store a second character before that. This happens * when peekc() triggered a buffer refill and then ungetch() * is called. */ #define P_BUFSIZE ((8*1024)+(2*MB_LEN_MAX)) unsigned char mybuf[P_BUFSIZE]; /* Fuer nextc */ unsigned char *bp; /* ditto */ int len = 0; /* ditto */ int clen = 0; /* # of octects in nextwc() multi byte char */ int pclen = 0; /* # of octects in peekwc() multi byte char */ unsigned char *cp; /* Beginning of multi byte char in buffer */ unsigned char *pcp; /* Beginning of peeked multi byte char in buf */ #ifdef BUFSIZ char buffer[BUFSIZ]; /* our buffer for stdout */ #endif char dcl[] = ":::::::::::::::"; /* BEGIN CSTYLED */ char options[] = "help,version,debug,nobeep,length#,l#,width#,w#,blank,b,clear,c,dos,nodos%0,end,e,raw,r,raw8,silent,s,tab,t,unul,visible,v"; /* END CSTYLED */ BOOL nameprint = FALSE; extern unsigned char csize[]; extern unsigned char *ctab[]; extern void init_charset __PR((void)); LOCAL void tstp __PR((int sig)); LOCAL void usage __PR((int exitcode)); EXPORT int main __PR((int ac, char **av)); LOCAL void page __PR((void)); LOCAL int outchar __PR((int c)); LOCAL void moreprompt __PR((void)); LOCAL BOOL more __PR((void)); LOCAL int get_action __PR((void)); LOCAL void onlinehelp __PR((void)); LOCAL void redraw __PR((void)); LOCAL int inchar __PR((void)); #if MB_LEN_MAX > 1 LOCAL int inwchar __PR((void)); #else #define inwchar inchar #endif #ifndef nextc LOCAL int nextc __PR((void)); #endif #if MB_LEN_MAX > 1 LOCAL int nextwc __PR((void)); LOCAL int peekwc __PR((void)); #endif #ifndef ungetch LOCAL int ungetch __PR((int c)); #endif #if MB_LEN_MAX > 1 LOCAL int ungetwch __PR((int c)); LOCAL void getnextwch __PR((void)); #endif LOCAL int fill_buf __PR((void)); LOCAL BOOL read_pattern __PR((void)); LOCAL int do_search __PR((void)); LOCAL int unul __PR((Uchar *ob, Uchar *ib, int amt)); LOCAL void init_termcap __PR((void)); LOCAL int oc __PR((int c)); LOCAL void start_standout __PR((void)); LOCAL void end_standout __PR((void)); #ifdef __needed__ LOCAL void start_bold __PR((void)); #endif LOCAL void end_attr __PR((void)); LOCAL void start_xstandout __PR((void)); LOCAL void end_xstandout __PR((void)); LOCAL void start_ul __PR((void)); LOCAL void end_ul __PR((void)); LOCAL void clearline __PR((void)); LOCAL void clearscreen __PR((void)); LOCAL void init_tty_size __PR((void)); LOCAL int get_modes __PR((void)); LOCAL void set_modes __PR((void)); LOCAL void reset_modes __PR((void)); LOCAL void fixtty __PR((int sig)); #ifdef SIGTSTP LOCAL void tstp(sig) int sig; { /* ignore SIGTTOU so we don't get stopped if the shell modifies pgrp */ signal(SIGTTOU, SIG_IGN); end_standout(); end_attr(); end_ul(); clearline(); reset_modes(); signal(SIGTTOU, SIG_DFL); signal(SIGTSTP, SIG_DFL); #ifdef OLD #ifdef HAVE_SIGRELSE sigrelse(SIGTSTP); #else (void) sigsetmask(0); #endif #else /* NEW */ unblock_sig(SIGTSTP); #endif kill(getpid(), SIGTSTP); /* Hier stoppt 'p' */ set_modes(); moreprompt(); } #endif LOCAL void usage(exitcode) int exitcode; { error("Usage: p [options] [file1...filen]\n"); error("Options:\n"); error("\t-help\t\tprint this online help\n"); error("\t-version\tprint version number\n"); error("\t-debug\t\tprint additional debug output\n"); error("\tlength=#,l=#\tlength of screen (default 24)\n"); error("\twidth=#,w=#\twidth of screen (default 80)\n"); error("\t-blank,-b\tsupress multiple blank lines on output\n"); error("\t-clear,-c\tclear screen before displaying new page\n"); error("\t-dos\t\tsupress '\\r' in '\\r\\n'\n"); error("\t-nodos\t\tsupress auto detecting the dos mode\n"); error("\t-end,-e\t\tprint a $ at each end of line\n"); error("\t-raw,-r\t\tdo not expand chars\n"); error("\t-raw8\t\tdo not expand 8bit chars\n"); error("\t-silent,-s\tdo not prompt for more stuff\n"); error("\t-tab,-t\t\tdo not expand tabs to spaces but to ^I\n"); #ifdef __needed__ error("\t-unul\t\tremove underlining and bold sequences\n"); #endif error("\t-visible,-v\tunderlining/bold sequences become visible\n\n"); error(" When asked for more: confirm=page, n=no more, h=half page\n"); error(" q=quarter page, l=single line, 1-9=# lines.\n"); error(" When asked for next file: confirm=yes, n=skip to next\n"); error(" s=stop (exit), h,q,l,1-9=yes with that # lines.\n"); exit(exitcode); } EXPORT int main(ac, av) int ac; char *av[]; { int i; int cac; char *const *cav; int fac; char **fav; save_args(ac, av); (void) setlocale(LC_ALL, ""); #ifdef USE_NLS #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "p" /* Use this only if it weren't */ #endif { char *dir; dir = searchfileinpath("share/locale", F_OK, SIP_ANY_FILE|SIP_NO_PATH, NULL); if (dir) (void) bindtextdomain(TEXT_DOMAIN, dir); else #if defined(PROTOTYPES) && defined(INS_BASE) (void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale"); #else (void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale"); #endif (void) textdomain(TEXT_DOMAIN); } #endif /* USE_NLS */ cac = --ac; cav = ++av; if (getallargs(&cac, &cav, options, &help, &prvers, &debug, &nobeep, &psize, &psize, &lwidth, &lwidth, &supressblank, &supressblank, &clear, &clear, &dosmode, &autodos, &endline, &endline, &raw, &raw, &raw8, &silent, &silent, &tab, &tab, &ununderline, &visible, &visible) < 0) { errmsgno(EX_BAD, "Bad flag: %s.\n", cav[0]); usage(EX_BAD); } if (help) usage(0); if (prvers) { /* BEGIN CSTYLED */ gtprintf("p %s %s (%s-%s-%s)\n\n", "2.4", "2021/08/20", HOST_CPU, HOST_VENDOR, HOST_OS); gtprintf("Copyright (C) 1985, 87-92, 95-99, 2000-2021 %s\n", _("Jörg Schilling")); gtprintf("This is free software; see the source for copying conditions. There is NO\n"); gtprintf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); /* END CSTYLED */ exit(0); } shell = getenv("BEEP"); if (shell != NULL && streql(shell, "off")) nobeep = TRUE; if ((editor = getenv("EDITOR")) == NULL) editor = "vi"; if ((shell = getenv("SHELL")) == NULL) shell = "sh"; underline = !raw && !visible; cac = ac; cav = av; for (i = 0; getfiles(&cac, &cav, options) > 0; i++, cac--, cav++); if (0 /*fav = (char **)malloc(i*sizeof(char *))*/) { cac = ac; cav = av; fac = i; for (i = 0; getfiles(&cac, &cav, options) > 0; i++, cac--, cav++) { fav[i] = *cav; } } else { cac = ac; cav = av; getfiles(&cac, &cav, options); } if (cac == 0 && isatty(fdown(stdin))) usage(EX_BAD); if (get_modes() < 0) { silent++; if (!ununderline) underline = FALSE; } /* if (underline || !silent)*/ init_termcap(); init_tty_size(); if (!silent) { usleep(150000); /* XXX Hack bis Jobcontrol im bsh geht !!! */ set_modes(); } init_charset(); #ifdef BUFSIZ /* XXX #ifdef HAVE_SETBUF ??? */ setbuf(stdout, buffer); #endif /* * If we evaluate "has_termcap" here, we could avoid to reduce the width * of the terminal in case that "am" but not "xn" is present. This * however would result in missing line breaks in files created by the * screen(1) utility. */ if (am && !xn && lwidth > 2) lwidth--; lines = psize-2; fac = cac; #ifdef DEBUG printf("%d\n", cac); for (i = 0; i < cac; i++) printf("%s\n", cav[i]); #endif if (cac == 0) { filename = ""; linecnt = 0; f = stdin; page(); } else { if (cac > 1) nameprint++; for (;;) { if ((f = fileopen(*cav, "r")) == (FILE *) NULL) { errmsg("Can not open '%s'.\n", *cav); } else { filename = *cav; if (nameprint) { printf("%s\n%s\n%s\n", dcl, filename, dcl); linecnt = 3; } else linecnt = 0; page(); fclose(f); f = (FILE *)0; } for (i = 0; ; ) { if (direction == FORWARD) { cac--, cav++; if (cac <= 0) reset_modes(), exit(0); } else { if (cac < fac) { cac++, cav--; } } if (silent) break; if (i++ == 0) putchar('\n'); err: start_standout(); gtprintf("NEXT FILE (%s)?", *cav); end_standout(); fflush(stdout); lines = psize-2; switch (get_action()) { case -1: goto err; case 1: continue; } break; } clearline(); } } reset_modes(); exit(0); return (0); /* Keep lint happy */ } LOCAL void page() { register int c; register int cnt; register unsigned char *s; register unsigned char **rctab; int ocolcnt = -1; rctab = ctab; ofpos = (off_t)0; len = 0; /* fill_buf() on next nextc() */ file_raise(f, FALSE); #ifdef __nonono__ /* FreeBSD would read single bytes */ setbuf(f, NULL); #endif lineno = 0; colcnt = 0; if (searchnext && do_search() < 0) { if (!more()) return; } while ((c = nextc()) != EOF) { if (c < 0) { /* -2 on error */ errmsg("Error reading '%s'.\n", filename); return; } if (c == '_' && underline) { /* underlining */ if (peekc() != '\b') { if (outchar('_')) return; continue; } getnextch(); /* it _is_ a ^H ! */ if (peekc() == '_') goto bold; underl++; } else if (c == '+' && underline && peekc() == '\b') { getnextch(); } else if (c == '\r' && (autodos || dosmode) && peekc() == '\n') { /* EMPTY */ } else if (c == '\n') { if (ocolcnt == 0 && colcnt == 0 && supressblank) continue; ocolcnt = colcnt; lineno++; if (endline) { if (outchar('$')) return; } if (outchar('\n')) return; } else if (c == '\t' && !tab) { cnt = 8 - (colcnt&7); while (--cnt >= 0) if (outchar(' ')) return; } else if (peekc() == '\b' && underline) { getnextch(); /* ^H */ bold: while (peekc() == c) { getnextch(); if (peekc() == '\b') getnextch(); else break; } ungetch(c); standout++; } else { if (raw || iswprint(c)) { if (outchar(c)) return; } else { #if MB_LEN_MAX > 1 int i = clen; while (--i >= 0) { s = rctab[*cp++]; while (*s) if (outchar(*s++)) return; } #else s = rctab[c]; while (*s) if (outchar(*s++)) return; #endif } } } fflush(stdout); } LOCAL int outchar(c) int c; { #if MB_LEN_MAX > 1 unsigned char b[MB_LEN_MAX]; unsigned char *p = b; int l; l = wctomb((char *)b, c); if (l < 0) { l = 1; b[0] = '?'; } #endif if (c == '\n') { if (len <= lwidth) fflush(stdout); if (++linecnt >= lines) { if (!more()) return (1); clearline(); linecnt = 0; } else { putc('\n', stdout); } colcnt = 0; return (0); } else { int w = wcwidth(c); if (w < 0) w = 1; colcnt += w; if (colcnt > lwidth) { if (outchar('\n')) return (1); colcnt = 1; } if (standout) { standout--; if (underl) { underl--; start_ul(); start_xstandout(); #if MB_LEN_MAX > 1 while (--l >= 0) putc(*p++, stdout); #else putc(c, stdout); #endif end_xstandout(); end_ul(); } else { start_xstandout(); #if MB_LEN_MAX > 1 while (--l >= 0) putc(*p++, stdout); #else putc(c, stdout); #endif end_xstandout(); } } else if (underl) { underl--; start_ul(); #if MB_LEN_MAX > 1 while (--l >= 0) putc(*p++, stdout); #else putc(c, stdout); #endif end_ul(); } else { #if MB_LEN_MAX > 1 while (--l >= 0) putc(*p++, stdout); #else putc(c, stdout); #endif } return (0); } } LOCAL void moreprompt() { float percent; off_t size = -1; off_t pos = -1; lines = psize-2; if (f) { size = filesize(f); pos = filepos(f) - len; } start_standout(); if (!*filename || !f || size < 0 || pos < 0) gtprintf("%s%c MORE?", filename, *filename ? ':' : '-'); else { percent = (float)pos; percent /= (float)size; percent *= 100.0; gtprintf("%s%c %.1f %% MORE?", filename, *filename ? ':' : '-', percent); } end_standout(); fflush(stdout); } LOCAL BOOL more() { if (silent) return (TRUE); putchar('\n'); for (;;) { moreprompt(); switch (get_action()) { case 1: return (FALSE); case -1: continue; } ofpos = filepos(f)-len; break; } return (TRUE); } /* * Return: * -1 error * 0 continue this file * 1 stop on this file * EXIT: * on demand */ LOCAL int get_action() { int c; char buf[128]; direction = FORWARD; searchnext = 0; switch (c = inwchar()) { case 'p': case 'P': case 'n': case 'N': direction = (c == 'P' || c == 'p'); clearline(); return (1); case 's': case 'S': case 003 : /* ^C */ case 004 : /* ^D */ case 034 : /* ^\ */ case 0177: /* DEL */ case EOF : fixtty(0); /* NOTREACHED */ case 'Y': case 'y': case '\r': case '\n': case ' ': if (clear) clearscreen(); break; case 'H': case 'h': lines = lines/2; break; case 'Q': case 'q': lines = lines/4; break; case 'L': case 'l': lines = 1; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': lines = c-'0'; break; case '/': clearline(); printf("/"); fflush(stdout); if (!read_pattern()) return (-1); /* FALLTHRU */ case 'r': case 'R': if (f == (FILE *)0) searchnext = 1; else if (do_search() < 0) return (-1); break; case '\f': redraw(); /* XXX eigentlich nicht ?? */ break; case '?': clearline(); onlinehelp(); return (-1); case '!': clearline(); sprintf(buf, "%s -t", shell); (void) system(buf); return (-1); case 'v': case 'V': if (f != stdin) { clearline(); sprintf(buf, "%s +%d %s", editor, lineno, filename); printf("%s", buf); fflush(stdout); (void) system(buf); return (-1); } /* FALLTHRU */ default: if (!nobeep) putchar('\007'); clearline(); return (-1); } return (0); } LOCAL void onlinehelp() { /* BEGIN CSTYLED */ gtprintf("\n---------------------------------------------------------------\n"); gtprintf("N, n Next File\n"); gtprintf("P, p Previous File\n"); gtprintf("S, s, ^C, ^D, ^\\, DEL Exit (stop)\n"); gtprintf("Y, y, , Display next screenfull of text\n"); gtprintf("H, h Display next half screenfull of text\n"); gtprintf("Q, q Display next quarter screenfull of text\n"); gtprintf("L, l Display next line of text\n"); gtprintf("1-9 Display next lines of text\n"); gtprintf("/pattern Search for \n"); gtprintf("R, r Re-search for \n"); gtprintf("^L Redraw screen\n"); gtprintf("? Display this message\n"); gtprintf("! Execute command\n"); gtprintf("V, v Edit file\n"); gtprintf("---------------------------------------------------------------\n"); /* END CSTYLED */ } LOCAL void redraw() { if (f && fileseek(f, ofpos) < 0) { if (!nobeep) putchar('\007'); clearline(); return; } len = 0; clearscreen(); } LOCAL int inchar() { char c; int ret = 0; c = '\004'; /* return ^D on EOF */ do { # ifdef USE_GETCH c = getch(); /* DOS console input */ # else ret = read(tty, &c, 1); # endif } while (ret < 0 && geterrno() == EINTR); if (ret == 0) return (EOF); return (c); } #if MB_LEN_MAX > 1 /* * Get next wide character from tty */ LOCAL int inwchar() { register int cur_max = MB_CUR_MAX; register int i; int mlen; wchar_t c = -1; char cstr[MB_LEN_MAX]; char *csp; int ic; (void) mbtowc(NULL, NULL, 0); for (i = 1, csp = cstr; i <= cur_max; i++) { ic = inchar(); if (ic == EOF) break; *csp++ = (char)ic; mlen = mbtowc(&c, cstr, i); if (mlen >= 0) break; (void) mbtowc(NULL, NULL, 0); } #ifdef _NEXTWC_DEBUG fprintf(stderr, "C %d %x\n", c, c); #endif return ((int)c); } #endif #ifndef nextc LOCAL int nextc() { if (--len >= 0) { return ((int) *bp++); } else { if (fill_buf() <= 0) return (len == 0 ? EOF : -2); } len--; return ((int) *bp++); } #endif #if MB_LEN_MAX > 1 /* * Read the next multi byte character from the buffer. * If the buffer is empty or if less than a multi byte character is inside, * it is refilled. When an illegal byte sequence is discovered, a single * byte is removed from the buffer in hope to be able to resync. */ LOCAL int nextwc() { BOOL eof = FALSE; int mlen; wchar_t c; if (len <= 0) { int olen; again: olen = len; if (fill_buf() <= 0) { return (len == 0 ? EOF : -2); } else if (len > 0 && olen == len) { eof = TRUE; } } mlen = mbtowc(&c, (char *)bp, len); if (mlen >= 0) { if (mlen == 0) mlen = 1; clen = mlen; cp = bp; bp += mlen; len -= mlen; return (c); } else { mbtowc(NULL, NULL, 0); if (len < MB_CUR_MAX && !eof) { seterrno(0); goto again; } clen = 1; cp = bp; bp++; len--; #ifdef EILSEQ if (geterrno() == EILSEQ) { return (*cp); } #endif } return (-2); } /* * The same as nextwc(), but the buffer is untouched. */ LOCAL int peekwc() { BOOL eof = FALSE; int mlen; wchar_t c; if (len <= 0) { int olen; again: olen = len; if (fill_buf() <= 0) { return (len == 0 ? EOF : -2); } else if (len > 0 && olen == len) { eof = TRUE; } } mlen = mbtowc(&c, (char *)bp, len); if (mlen >= 0) { if (mlen == 0) mlen = 1; pclen = mlen; pcp = bp; return (c); } else { mbtowc(NULL, NULL, 0); if (len < MB_CUR_MAX && !eof) { seterrno(0); goto again; } pclen = 1; pcp = bp; #ifdef EILSEQ if (geterrno() == EILSEQ) { return (*pcp); } #endif } return (-2); } #endif #ifndef ungetch LOCAL int ungetch(c) int c; { if (c == EOF) return (c); /* * If bp is at the beginning of the buffer, this may go * to one character before the buffer (see below). */ len++; return (*--bp = c); } #endif #if MB_LEN_MAX > 1 /* * Return the character back to the buffer. */ LOCAL int ungetwch(c) int c; { unsigned char b[MB_LEN_MAX]; unsigned char *p; int l; if (c == EOF) return (c); l = wctomb((char *)b, c); if (l < 0) { l = 1; b[0] = '?'; } len += l; p = &b[l]; while (--l >= 0) *--bp = *--p; return (c); } /* * Consume the last character fetched by peekwc() */ LOCAL void getnextwch() { len -= pclen; bp += pclen; cp = pcp; pclen = 0; } #endif LOCAL int fill_buf() { int i; /* * Allow ungetch() to always put back one character. * This is done by reserving one char before the normal * space in "mybuf". */ if (len > 0) { unsigned char *p = bp; /* * Move a partial character left at the end of the buffer * to the space before the beginning of the buffer. */ if (len > MB_LEN_MAX) len = MB_LEN_MAX; p = &mybuf[(2*MB_LEN_MAX) - len]; for (i = len; --i >= 0; ) { *p++ = *bp++; } bp = &mybuf[(2*MB_LEN_MAX) - len]; } else { len = 0; bp = &mybuf[(2*MB_LEN_MAX)]; } i = fileread(f, &mybuf[2*MB_LEN_MAX], sizeof (mybuf) - 2*MB_LEN_MAX); if (i < 0) return (i); return (len += i); } LOCAL BOOL read_pattern() { int patlen; register int c; register int count = 0; #if MB_LEN_MAX > 1 register wchar_t *s = searchbuf; unsigned char b[MB_LEN_MAX]; #else register char *s = searchbuf; #endif register unsigned char *p; register int i; while ((c = inwchar()) != EOF && count++ < (SEARCHSIZE - 1) && c != '\004' && c != '\r' && c != '\n') { if (c == 0177) { /* DEL */ if (s == searchbuf) { if (!nobeep) putchar('\007'); } else { --count; --s; #if MB_LEN_MAX > 1 && defined(HAVE_WCWIDTH) if (iswprint(*s)) i = wcwidth(*s); else i = csize[*s & 0xFF]; #else i = csize[*s & 0xFF]; #endif for (; --i >= 0; ) printf("\b \b"); } fflush(stdout); } else { #if MB_LEN_MAX > 1 && defined(HAVE_WCWIDTH) int l; if (iswprint(c)) { l = wctomb((char *)b, c); if (l < 1) { p = ctab[c & 0xFF]; } else { p = b; b[l] = '\0'; } } else { p = ctab[c & 0xFF]; } #else p = ctab[c & 0xFF]; #endif *s++ = c; while (*p) putchar(*p++); fflush(stdout); } } *s = '\0'; if (aux) free((char *)aux); #if MB_LEN_MAX > 1 patlen = wcslen(searchbuf); #else patlen = strlen(searchbuf); #endif aux = (int *) malloc(patlen * sizeof (int)); state = (int *) malloc((patlen+1) * sizeof (int)); #if MB_LEN_MAX > 1 alt = patwcompile(searchbuf, patlen, aux); #else alt = patcompile((unsigned char *)searchbuf, patlen, aux); #endif if (!alt) { gtprintf("Bad Pattern.\r"); fflush(stdout); sleep(1); return (FALSE); } return (TRUE); } LOCAL int do_search() { register unsigned char *lp; register unsigned char *rbp; unsigned char *sp; register int rest = len; off_t curpos; BOOL skipping = FALSE; int newlines = 0; unsigned char sbuf[P_BUFSIZE]; if (!alt) { if (!nobeep) putchar('\007'); gtprintf("No previous search.\r"); fflush(stdout); #ifdef DEBUG sleep(1); #endif return (-1); } curpos = filepos(f)-len; #ifdef DEBUG fprintf(stderr, "Efilepos: %lld len: %d bp: 0x%X\n", (Llong)filepos(f), len, bp); #endif for (;;) { if (rest < MB_LEN_MAX) { int olen; /* * We may be operating on a copy and thus * need to set "len" to the remaining characters to * tell fill_buf() that it needs to do a refill. */ olen = len = rest; if (fill_buf() <= 0 || olen == len) { if (!nobeep) putchar('\007'); gtprintf("Pattern not found.\r"); fflush(stdout); if (f && fileseek(f, curpos) >= 0) len = 0; return (-1); } newlines = 0; } rbp = lp = sp = bp; rest = len; if (underline && findbytes(lp, rest, '\b') != NULL) { rest = unul(sbuf, rbp, len); rbp = lp = sp = sbuf; } for (; rest > 0; rest--) { #if MB_LEN_MAX > 1 if (patmbmatch(searchbuf, #else if (patmatch((unsigned char *)searchbuf, #endif aux, rbp, 0, rest, alt, state)) { #ifdef DEBUG fprintf(stderr, "Afilepos: %lld rest: %d rbp: 0x%X lp: 0x%X\n", (Llong)filepos(f), rest, rbp, lp); #endif if (skipping) { printf("\n"); if (sp == bp) { bp = lp; len = rest + (rbp - lp); } else { lp = bp; rbp = &bp[len]; while (--newlines >= 0) { unsigned char *olp = lp; if ((lp = (Uchar *) findbytes(lp, rbp - lp, '\n')) == NULL) { lp = olp; break; } lp++; } bp = lp; len = rbp - lp; } } #ifdef DEBUG fprintf(stderr, "Afilepos: %lld len: %d bp: 0x%X\n", (Llong)filepos(f), len, bp); #endif return (0); } if (*rbp++ == '\n') { /* * Remember last line start pointer. */ lp = rbp; newlines++; if (!skipping) { skipping = TRUE; gtprintf("skipping...\r"); fflush(stdout); } #ifdef DEBUG fprintf(stderr, "."); #endif } } } } /* * Remove underlining and overstriking sequences. * Put the result into "ob". */ LOCAL int unul(ob, ib, amt) register Uchar *ob; register Uchar *ib; register int amt; { register Uchar *oob = ob; wchar_t c; register ssize_t wclen; mbtowc(NULL, NULL, 0); while (amt > 0) { wclen = mbtowc(&c, (char *)ib, amt); if (wclen < 1) { *ob++ = *ib++; amt--; continue; } amt -= wclen; ib += wclen; if (c == '_') { /* underlining */ if (ib[0] != '\b') { ib -= wclen; while (--wclen >= 0) *ob++ = *ib++; continue; } ib++; /* eat ^H */ amt--; wclen = mbtowc(&c, (char *)ib, amt); if (wclen > 0 && c == '_') { /* _ ^H _ */ goto bold; } } else if (c == '+' && ib[0] == '\b') { /* overstriking */ /* + ^H o */ ib++; /* eat ^H */ amt--; } else if (ib[0] == '\b') { wchar_t c2; ssize_t wclen2; Uchar *oib; /* N ^H N ^H N ^H N */ ib++; /* eat ^H */ amt--; bold: oib = ib - (wclen +1); while (amt > 0) { wclen2 = mbtowc(&c2, (char *)ib, amt); if (wclen2 < 1) break; if (c2 != c) break; ib += wclen2; /* eat c2 */ amt -= wclen2; if (ib[0] == '\b') { /* Check for ^H */ ib++; /* eat ^H */ amt--; } else break; } while (--wclen >= 0) *ob++ = *oib++; } else { ib -= wclen; while (--wclen >= 0) *ob++ = *ib++; } } *ob = '\0'; return (ob - oob); } char stbuf[1024]; /* Bufer for termcap array (i.e. so and se) */ LOCAL void init_termcap() { char *tname; char *sbp; sbp = stbuf; if ((tname = getenv("TERM")) && tgetent(NULL, tname) == 1) { has_termcap = TRUE; so = tgetstr("so", &sbp); /* start standout */ se = tgetstr("se", &sbp); /* end standout */ us = tgetstr("us", &sbp); /* start underline */ ue = tgetstr("ue", &sbp); /* end underline */ md = tgetstr("md", &sbp); /* start bold */ me = tgetstr("me", &sbp); /* end attributes */ ce = tgetstr("ce", &sbp); /* clear endline */ cl = tgetstr("cl", &sbp); /* clear screen */ li = tgetnum("li"); /* lines on screen */ co = tgetnum("co"); /* columns on screen */ am = tgetflag("am"); /* automatic margins */ xn = tgetflag("xn"); /* newline ignored > 80 */ if (so != NULL && se != NULL) { has_standout = TRUE; } else { so = se = NULL; } if (md != NULL && me != NULL) { has_bold = TRUE; } else { md = NULL; } if (us != NULL && ue != NULL) { has_ul = TRUE; } else { us = ue = NULL; } if (has_bold) { xso = md; xse = me; } else if (has_standout) { xso = so; xse = se; } else { xso = xse = NULL; } if (debug) { printf("so: %d bo: %d ul: %d\n", has_standout, has_bold, has_ul); sleep(1); } } } LOCAL int oc(c) int c; { return (putchar(c)); } LOCAL void start_standout() { tputs(so, 1, oc); } LOCAL void end_standout() { tputs(se, 1, oc); } #ifdef __needed__ LOCAL void start_bold() { tputs(md, 1, oc); } #endif LOCAL void end_attr() { tputs(me, 1, oc); } LOCAL void start_xstandout() { tputs(xso, 1, oc); } LOCAL void end_xstandout() { tputs(xse, 1, oc); } LOCAL void start_ul() { if (has_ul) tputs(us, 1, oc); } LOCAL void end_ul() { if (has_ul) tputs(ue, 1, oc); } LOCAL void clearline() { int i; if (silent) { putchar('\n'); return; } putchar('\r'); if (ce) { tputs(ce, 1, oc); } else { for (i = 1; i < lwidth; i++) putchar(' '); putchar('\r'); } fflush(stdout); } LOCAL void clearscreen() { if (cl) tputs(cl, 1, oc); } LOCAL void init_tty_size() { #ifdef no_TIOCGSIZE #if defined(TIOCGSIZE) || defined(TIOCGWINSZ) #ifdef TIOCGWINSZ struct winsize ws; ws.ws_rows = 0; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) >= 0) { if (ws.ws_rows) { psize = ws.ws_rows; lwidth = ws.ws_cols; } } #else struct ttysize ts; ts.ts_lines = 0; if (ioctl(STDOUT_FILENO, TIOCGSIZE, (char *)&ts) >= 0) { if (ts.ts_lines) { psize = ts.ts_lines; lwidth = ts.ts_cols; } } #endif #endif #else if (psize == 0) { if (li > 0 && li < 1000) psize = li; else psize = DEF_PSIZE; } if (lwidth == 0) { if (co > 0 && co < 1000) lwidth = co; else lwidth = DEF_LWIDTH; } #endif } LOCAL int get_modes() { # ifdef USE_V7_TTY return (ioctl(STDOUT_FILENO, TIOCGETP, &old)); # else /* USE_V7_TTY */ # ifdef USE_TERMIOS # ifdef TCSANOW return (tcgetattr(STDOUT_FILENO, &old)); # else return (ioctl(STDOUT_FILENO, TCGETS, &old)); # endif # else /* USE_TERMIOS */ return (0); # endif /* USE_TERMIOS */ # endif /* USE_V7_TTY */ } LOCAL void set_modes() { #ifdef HAVE__DEV_TTY if (tty < 0 && (tty = open("/dev/tty", O_RDONLY)) < 0) errmsg("Can't open '/dev/tty'\n"); #endif if (tty < 0) tty = fileno(stderr); /* * Do signal handling only if they are not already * ignored. */ if (signal(SIGINT, SIG_IGN) != SIG_IGN) { signal(SIGINT, fixtty); #ifdef SIGQUIT signal(SIGQUIT, fixtty); #endif #ifdef SIGTSTP if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) signal(SIGTSTP, tstp); #endif } # ifdef USE_V7_TTY movebytes(&old, &new, sizeof (old)); # ifdef LPASS8 { int lmode; ioctl(STDOUT_FILENO, TIOCLGET, &lmode); if (lmode & LPASS8) raw8 = TRUE; } # else if (old.sg_flags & RAW) raw8 = TRUE; # endif new.sg_flags |= CBREAK; new.sg_flags &= ~ECHO; if (ioctl(STDOUT_FILENO, TIOCSETN, &new) < 0) comerr("Can not set new modes.\n"); # else /* USE_V7_TTY */ # ifdef USE_TERMIOS movebytes(&old, &new, sizeof (old)); if (!(old.c_iflag & ISTRIP)) raw8 = TRUE; #ifdef __never__ new.c_iflag = ICRNL; new.c_oflag = (OPOST|ONLCR); new.c_lflag = ISIG; #endif new.c_lflag &= ~(ICANON|ECHO); new.c_cc[VMIN] = 1; new.c_cc[VTIME] = 0; # ifdef TCSANOW if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &new) < 0) # else if (ioctl(STDOUT_FILENO, TCSETSW, &new) < 0) # endif comerr("Can not set new modes.\n"); # endif /* USE_TERMIOS */ # endif /* USE_V7_TTY */ #ifdef CATCH_SIGCONT_here #ifdef SIGCONT signal(SIGONT, redraw); /* XXX ??? */ #endif #endif } /* * Reset to previous tty modes. */ LOCAL void reset_modes() { if (!silent) { # ifdef USE_V7_TTY if (ioctl(STDOUT_FILENO, TIOCSETN, &old) < 0) # else /* USE_V7_TTY */ # ifdef USE_TERMIOS # ifdef TCSANOW if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &old) < 0) # else if (ioctl(STDOUT_FILENO, TCSETSW, &old) < 0) # endif # else /* USE_TERMIOS */ if (0) # endif /* USE_TERMIOS */ # endif /* USE_V7_TTY */ comerr("Can not reset old modes.\n"); } } /* * Fix tty and exit. */ LOCAL void fixtty(sig) int sig; { end_standout(); end_attr(); end_ul(); clearline(); reset_modes(); exit(0); }