14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1985-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * Phong Vo <kpv@research.att.com> *
204887Schin * *
214887Schin ***********************************************************************/
224887Schin #include "sfdchdr.h"
234887Schin
244887Schin #if _PACKAGE_ast
254887Schin #include <ast_tty.h>
264887Schin #include <signal.h>
274887Schin #endif
284887Schin
294887Schin /*
304887Schin * a simple but fast more style pager discipline
314887Schin * if on sfstdout then sfstdin ops reset the page state
324887Schin *
334887Schin * Glenn Fowler
344887Schin * AT&T Research
354887Schin *
364887Schin * @(#)$Id: sfdcmore (AT&T Research) 1998-06-25 $
374887Schin */
384887Schin
394887Schin typedef struct
404887Schin {
414887Schin Sfdisc_t disc; /* sfio discipline */
424887Schin Sfio_t* input; /* tied with this input stream */
434887Schin Sfio_t* error; /* tied with this error stream */
444887Schin int rows; /* max rows */
454887Schin int cols; /* max cols */
464887Schin int row; /* current row */
474887Schin int col; /* current col */
484887Schin int match; /* match length, 0 if none */
494887Schin char pattern[128]; /* match pattern */
504887Schin char prompt[1]; /* prompt string */
514887Schin } More_t;
524887Schin
534887Schin /*
544887Schin * more read
554887Schin * we assume line-at-a-time input
564887Schin */
574887Schin
584887Schin #if __STD_C
moreread(Sfio_t * f,void * buf,size_t n,Sfdisc_t * dp)594887Schin static ssize_t moreread(Sfio_t* f, void* buf, size_t n, Sfdisc_t* dp)
604887Schin #else
614887Schin static ssize_t moreread(f, buf, n, dp)
624887Schin Sfio_t* f;
634887Schin void* buf;
644887Schin size_t n;
654887Schin Sfdisc_t* dp;
664887Schin #endif
674887Schin {
684887Schin register More_t* more = (More_t*)dp;
694887Schin
704887Schin more->match = 0;
714887Schin more->row = 2;
724887Schin more->col = 1;
734887Schin return sfrd(f, buf, n, dp);
744887Schin }
754887Schin
764887Schin /*
774887Schin * output label on wfd and return next char on rfd with no echo
784887Schin * return < -1 is -(signal + 1)
794887Schin */
804887Schin
814887Schin #if __STD_C
ttyquery(Sfio_t * rp,Sfio_t * wp,const char * label,Sfdisc_t * dp)824887Schin static int ttyquery(Sfio_t* rp, Sfio_t* wp, const char* label, Sfdisc_t* dp)
834887Schin #else
844887Schin static int ttyquery(rp, wp, label, dp)
854887Schin Sfio_t* rp;
864887Schin Sfio_t* wp;
874887Schin char* label;
884887Schin Sfdisc_t* dp;
894887Schin #endif
904887Schin {
914887Schin register int r;
924887Schin int n;
934887Schin
944887Schin #ifdef TCSADRAIN
954887Schin unsigned char c;
964887Schin struct termios old;
974887Schin struct termios tty;
984887Schin int rfd = sffileno(rp);
994887Schin int wfd = sffileno(rp);
1004887Schin
1014887Schin if (!label)
1024887Schin n = 0;
1034887Schin else if (n = strlen(label))
1044887Schin write(wfd, label, n);
1054887Schin tcgetattr(rfd, &old);
1064887Schin tty = old;
1074887Schin tty.c_cc[VTIME] = 0;
1084887Schin tty.c_cc[VMIN] = 1;
1094887Schin tty.c_lflag &= ~(ICANON|ECHO|ECHOK|ISIG);
1104887Schin tcsetattr(rfd, TCSADRAIN, &tty);
1114887Schin if ((r = read(rfd, &c, 1)) == 1)
1124887Schin {
1134887Schin if (c == old.c_cc[VEOF])
1144887Schin r = -1;
1154887Schin else if (c == old.c_cc[VINTR])
1164887Schin r = -(SIGINT + 1);
1174887Schin else if (c == old.c_cc[VQUIT])
1184887Schin r = -(SIGQUIT + 1);
1194887Schin else if (c == '\r')
1204887Schin r = '\n';
1214887Schin else
1224887Schin r = c;
1234887Schin }
1244887Schin tcsetattr(rfd, TCSADRAIN, &old);
1254887Schin if (n)
1264887Schin {
1274887Schin write(wfd, "\r", 1);
1284887Schin while (n-- > 0)
1294887Schin write(wfd, " ", 1);
1304887Schin write(wfd, "\r", 1);
1314887Schin }
1324887Schin #else
1334887Schin register char* s;
1344887Schin
1354887Schin if (label && (n = strlen(label)))
1364887Schin sfwr(wp, label, n, dp);
1374887Schin r = (s = sfgetr(rp, '\n', 0)) ? *s : -1;
1384887Schin #endif
1394887Schin return r;
1404887Schin }
1414887Schin
1424887Schin /*
1434887Schin * more write
1444887Schin */
1454887Schin
1464887Schin #if __STD_C
morewrite(Sfio_t * f,const Void_t * buf,register size_t n,Sfdisc_t * dp)1474887Schin static ssize_t morewrite(Sfio_t* f, const Void_t* buf, register size_t n, Sfdisc_t* dp)
1484887Schin #else
1494887Schin static ssize_t morewrite(f, buf, n, dp)
1504887Schin Sfio_t* f;
1514887Schin Void_t* buf;
1524887Schin register size_t n;
1534887Schin Sfdisc_t* dp;
1544887Schin #endif
1554887Schin {
1564887Schin register More_t* more = (More_t*)dp;
1574887Schin register char* b;
1584887Schin register char* s;
1594887Schin register char* e;
1604887Schin register ssize_t w;
1614887Schin register int r;
1624887Schin
1634887Schin if (!more->row)
1644887Schin return n;
1654887Schin if (!more->col)
1664887Schin return sfwr(f, buf, n, dp);
1674887Schin w = 0;
1684887Schin b = (char*)buf;
1694887Schin s = b;
1704887Schin e = s + n;
1714887Schin if (more->match)
1724887Schin {
1734887Schin match:
1744887Schin for (r = more->pattern[0];; s++)
1754887Schin {
1764887Schin if (s >= e)
1774887Schin return n;
1784887Schin if (*s == '\n')
1794887Schin b = s + 1;
1804887Schin else if (*s == r && (e - s) >= more->match && !strncmp(s, more->pattern, more->match))
1814887Schin break;
1824887Schin }
1834887Schin s = b;
1844887Schin w += b - (char*)buf;
1854887Schin more->match = 0;
1864887Schin }
1874887Schin while (s < e)
1884887Schin {
1894887Schin switch (*s++)
1904887Schin {
1914887Schin case '\t':
1924887Schin more->col = ((more->col + 8) & ~7) - 1;
1934887Schin /*FALLTHROUGH*/
1944887Schin default:
1954887Schin if (++more->col <= more->cols || s < e && *s == '\n')
1964887Schin continue;
1974887Schin /*FALLTHROUGH*/
1984887Schin case '\n':
1994887Schin more->col = 1;
2004887Schin if (++more->row < more->rows)
2014887Schin continue;
2024887Schin break;
2034887Schin case '\b':
2044887Schin if (more->col > 1)
2054887Schin more->col--;
2064887Schin continue;
2074887Schin case '\r':
2084887Schin more->col = 1;
2094887Schin continue;
2104887Schin }
2114887Schin w += sfwr(f, b, s - b, dp);
2124887Schin b = s;
2134887Schin r = ttyquery(sfstdin, f, more->prompt, dp);
2144887Schin if (r == '/' || r == 'n')
2154887Schin {
2164887Schin if (r == '/')
2174887Schin {
2184887Schin sfwr(f, "/", 1, dp);
2194887Schin if ((s = sfgetr(sfstdin, '\n', 1)) && (n = sfvalue(sfstdin) - 1) > 0)
2204887Schin {
2214887Schin if (n >= sizeof(more->pattern))
2224887Schin n = sizeof(more->pattern) - 1;
2234887Schin memcpy(more->pattern, s, n);
2244887Schin more->pattern[n] = 0;
2254887Schin }
2264887Schin }
2274887Schin if (more->match = strlen(more->pattern))
2284887Schin {
2294887Schin more->row = 1;
2304887Schin more->col = 1;
2314887Schin goto match;
2324887Schin }
2334887Schin }
2344887Schin switch (r)
2354887Schin {
2364887Schin case '\n':
2374887Schin case '\r':
2384887Schin more->row--;
2394887Schin more->col = 1;
2404887Schin break;
2414887Schin case ' ':
2424887Schin more->row = 2;
2434887Schin more->col = 1;
2444887Schin break;
2454887Schin default:
2464887Schin more->row = 0;
2474887Schin return n;
2484887Schin }
2494887Schin }
2504887Schin if (s > b)
2514887Schin w += sfwr(f, b, s - b, dp);
2524887Schin return w;
2534887Schin }
2544887Schin
2554887Schin /*
2564887Schin * remove the discipline on close
2574887Schin */
2584887Schin
2594887Schin #if __STD_C
moreexcept(Sfio_t * f,int type,Void_t * data,Sfdisc_t * dp)2604887Schin static int moreexcept(Sfio_t* f, int type, Void_t* data, Sfdisc_t* dp)
2614887Schin #else
2624887Schin static int moreexcept(f, type, data, dp)
2634887Schin Sfio_t* f;
2644887Schin int type;
2654887Schin Void_t* data;
2664887Schin Sfdisc_t* dp;
2674887Schin #endif
2684887Schin {
2694887Schin register More_t* more = (More_t*)dp;
2704887Schin
2714887Schin if (type == SF_FINAL || type == SF_DPOP)
2724887Schin {
2734887Schin if (f = more->input)
2744887Schin {
2754887Schin more->input = 0;
2764887Schin sfdisc(f, SF_POPDISC);
2774887Schin }
2784887Schin else if (f = more->error)
2794887Schin {
2804887Schin more->error = 0;
2814887Schin sfdisc(f, SF_POPDISC);
2824887Schin }
2834887Schin else
2844887Schin free(dp);
2854887Schin }
2864887Schin else if (type == SF_SYNC)
2874887Schin {
2884887Schin more->match = 0;
2894887Schin more->row = 1;
2904887Schin more->col = 1;
2914887Schin }
2924887Schin return 0;
2934887Schin }
2944887Schin
2954887Schin /*
2964887Schin * push the more discipline on f
2974887Schin * if prompt==0 then a default ansi prompt is used
2984887Schin * if rows==0 or cols==0 then they are deterimined from the tty
2994887Schin * if f==sfstdout then input on sfstdin also resets the state
3004887Schin */
3014887Schin
3024887Schin #if __STD_C
sfdcmore(Sfio_t * f,const char * prompt,int rows,int cols)3034887Schin int sfdcmore(Sfio_t* f, const char* prompt, int rows, int cols)
3044887Schin #else
3054887Schin int sfdcmore(f, prompt, rows, cols)
3064887Schin Sfio_t* f;
3074887Schin char* prompt;
3084887Schin int rows;
3094887Schin int cols;
3104887Schin #endif
3114887Schin {
3124887Schin register More_t* more;
3134887Schin size_t n;
3144887Schin
3154887Schin /*
3164887Schin * this is a writeonly discipline for interactive io
3174887Schin */
3184887Schin
3194887Schin if (!(sfset(f, 0, 0) & SF_WRITE) || !isatty(sffileno(sfstdin)) || !isatty(sffileno(sfstdout)))
3204887Schin return -1;
3214887Schin if (!prompt)
3224887Schin prompt = "\033[7m More\033[m";
3234887Schin n = strlen(prompt) + 1;
3244887Schin if (!(more = (More_t*)malloc(sizeof(More_t) + n)))
3254887Schin return -1;
3264887Schin memset(more, 0, sizeof(*more));
3274887Schin
3284887Schin more->disc.readf = moreread;
3294887Schin more->disc.writef = morewrite;
3304887Schin more->disc.exceptf = moreexcept;
3314887Schin memcpy(more->prompt, prompt, n);
3324887Schin if (!rows || !cols)
3334887Schin {
3344887Schin #if _PACKAGE_ast
3354887Schin astwinsize(sffileno(sfstdin), &rows, &cols);
3364887Schin #endif
3374887Schin if (!rows)
3384887Schin rows = 24;
3394887Schin if (!cols)
3404887Schin cols = 80;
3414887Schin }
3424887Schin more->rows = rows;
3434887Schin more->cols = cols;
3444887Schin more->row = 1;
3454887Schin more->col = 1;
3464887Schin
3474887Schin if (sfdisc(f, &more->disc) != &more->disc)
3484887Schin {
3494887Schin free(more);
3504887Schin return -1;
3514887Schin }
3524887Schin if (f == sfstdout)
3534887Schin {
3544887Schin if (sfdisc(sfstdin, &more->disc) != &more->disc)
3554887Schin {
3564887Schin sfdisc(f, SF_POPDISC);
3574887Schin return -1;
3584887Schin }
3594887Schin more->input = sfstdin;
3604887Schin if (sfdisc(sfstderr, &more->disc) != &more->disc)
3614887Schin {
3624887Schin sfdisc(f, SF_POPDISC);
3634887Schin return -1;
3644887Schin }
3654887Schin more->error = sfstdin;
3664887Schin }
3674887Schin
3684887Schin return 0;
3694887Schin }
370