/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1985-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler * * David Korn * * Phong Vo * * * ***********************************************************************/ #include "sfdchdr.h" #if _PACKAGE_ast #include #include #endif /* * a simple but fast more style pager discipline * if on sfstdout then sfstdin ops reset the page state * * Glenn Fowler * AT&T Research * * @(#)$Id: sfdcmore (AT&T Research) 1998-06-25 $ */ typedef struct { Sfdisc_t disc; /* sfio discipline */ Sfio_t* input; /* tied with this input stream */ Sfio_t* error; /* tied with this error stream */ int rows; /* max rows */ int cols; /* max cols */ int row; /* current row */ int col; /* current col */ int match; /* match length, 0 if none */ char pattern[128]; /* match pattern */ char prompt[1]; /* prompt string */ } More_t; /* * more read * we assume line-at-a-time input */ #if __STD_C static ssize_t moreread(Sfio_t* f, void* buf, size_t n, Sfdisc_t* dp) #else static ssize_t moreread(f, buf, n, dp) Sfio_t* f; void* buf; size_t n; Sfdisc_t* dp; #endif { register More_t* more = (More_t*)dp; more->match = 0; more->row = 2; more->col = 1; return sfrd(f, buf, n, dp); } /* * output label on wfd and return next char on rfd with no echo * return < -1 is -(signal + 1) */ #if __STD_C static int ttyquery(Sfio_t* rp, Sfio_t* wp, const char* label, Sfdisc_t* dp) #else static int ttyquery(rp, wp, label, dp) Sfio_t* rp; Sfio_t* wp; char* label; Sfdisc_t* dp; #endif { register int r; int n; #ifdef TCSADRAIN unsigned char c; struct termios old; struct termios tty; int rfd = sffileno(rp); int wfd = sffileno(rp); if (!label) n = 0; else if (n = strlen(label)) write(wfd, label, n); tcgetattr(rfd, &old); tty = old; tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; tty.c_lflag &= ~(ICANON|ECHO|ECHOK|ISIG); tcsetattr(rfd, TCSADRAIN, &tty); if ((r = read(rfd, &c, 1)) == 1) { if (c == old.c_cc[VEOF]) r = -1; else if (c == old.c_cc[VINTR]) r = -(SIGINT + 1); else if (c == old.c_cc[VQUIT]) r = -(SIGQUIT + 1); else if (c == '\r') r = '\n'; else r = c; } tcsetattr(rfd, TCSADRAIN, &old); if (n) { write(wfd, "\r", 1); while (n-- > 0) write(wfd, " ", 1); write(wfd, "\r", 1); } #else register char* s; if (label && (n = strlen(label))) sfwr(wp, label, n, dp); r = (s = sfgetr(rp, '\n', 0)) ? *s : -1; #endif return r; } /* * more write */ #if __STD_C static ssize_t morewrite(Sfio_t* f, const Void_t* buf, register size_t n, Sfdisc_t* dp) #else static ssize_t morewrite(f, buf, n, dp) Sfio_t* f; Void_t* buf; register size_t n; Sfdisc_t* dp; #endif { register More_t* more = (More_t*)dp; register char* b; register char* s; register char* e; register ssize_t w; register int r; if (!more->row) return n; if (!more->col) return sfwr(f, buf, n, dp); w = 0; b = (char*)buf; s = b; e = s + n; if (more->match) { match: for (r = more->pattern[0];; s++) { if (s >= e) return n; if (*s == '\n') b = s + 1; else if (*s == r && (e - s) >= more->match && !strncmp(s, more->pattern, more->match)) break; } s = b; w += b - (char*)buf; more->match = 0; } while (s < e) { switch (*s++) { case '\t': more->col = ((more->col + 8) & ~7) - 1; /*FALLTHROUGH*/ default: if (++more->col <= more->cols || s < e && *s == '\n') continue; /*FALLTHROUGH*/ case '\n': more->col = 1; if (++more->row < more->rows) continue; break; case '\b': if (more->col > 1) more->col--; continue; case '\r': more->col = 1; continue; } w += sfwr(f, b, s - b, dp); b = s; r = ttyquery(sfstdin, f, more->prompt, dp); if (r == '/' || r == 'n') { if (r == '/') { sfwr(f, "/", 1, dp); if ((s = sfgetr(sfstdin, '\n', 1)) && (n = sfvalue(sfstdin) - 1) > 0) { if (n >= sizeof(more->pattern)) n = sizeof(more->pattern) - 1; memcpy(more->pattern, s, n); more->pattern[n] = 0; } } if (more->match = strlen(more->pattern)) { more->row = 1; more->col = 1; goto match; } } switch (r) { case '\n': case '\r': more->row--; more->col = 1; break; case ' ': more->row = 2; more->col = 1; break; default: more->row = 0; return n; } } if (s > b) w += sfwr(f, b, s - b, dp); return w; } /* * remove the discipline on close */ #if __STD_C static int moreexcept(Sfio_t* f, int type, Void_t* data, Sfdisc_t* dp) #else static int moreexcept(f, type, data, dp) Sfio_t* f; int type; Void_t* data; Sfdisc_t* dp; #endif { register More_t* more = (More_t*)dp; if (type == SF_FINAL || type == SF_DPOP) { if (f = more->input) { more->input = 0; sfdisc(f, SF_POPDISC); } else if (f = more->error) { more->error = 0; sfdisc(f, SF_POPDISC); } else free(dp); } else if (type == SF_SYNC) { more->match = 0; more->row = 1; more->col = 1; } return 0; } /* * push the more discipline on f * if prompt==0 then a default ansi prompt is used * if rows==0 or cols==0 then they are deterimined from the tty * if f==sfstdout then input on sfstdin also resets the state */ #if __STD_C int sfdcmore(Sfio_t* f, const char* prompt, int rows, int cols) #else int sfdcmore(f, prompt, rows, cols) Sfio_t* f; char* prompt; int rows; int cols; #endif { register More_t* more; size_t n; /* * this is a writeonly discipline for interactive io */ if (!(sfset(f, 0, 0) & SF_WRITE) || !isatty(sffileno(sfstdin)) || !isatty(sffileno(sfstdout))) return -1; if (!prompt) prompt = "\033[7m More\033[m"; n = strlen(prompt) + 1; if (!(more = (More_t*)malloc(sizeof(More_t) + n))) return -1; memset(more, 0, sizeof(*more)); more->disc.readf = moreread; more->disc.writef = morewrite; more->disc.exceptf = moreexcept; memcpy(more->prompt, prompt, n); if (!rows || !cols) { #if _PACKAGE_ast astwinsize(sffileno(sfstdin), &rows, &cols); #endif if (!rows) rows = 24; if (!cols) cols = 80; } more->rows = rows; more->cols = cols; more->row = 1; more->col = 1; if (sfdisc(f, &more->disc) != &more->disc) { free(more); return -1; } if (f == sfstdout) { if (sfdisc(sfstdin, &more->disc) != &more->disc) { sfdisc(f, SF_POPDISC); return -1; } more->input = sfstdin; if (sfdisc(sfstderr, &more->disc) != &more->disc) { sfdisc(f, SF_POPDISC); return -1; } more->error = sfstdin; } return 0; }