1*4887Schin /*********************************************************************** 2*4887Schin * * 3*4887Schin * This software is part of the ast package * 4*4887Schin * Copyright (c) 1985-2007 AT&T Knowledge Ventures * 5*4887Schin * and is licensed under the * 6*4887Schin * Common Public License, Version 1.0 * 7*4887Schin * by AT&T Knowledge Ventures * 8*4887Schin * * 9*4887Schin * A copy of the License is available at * 10*4887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 11*4887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*4887Schin * * 13*4887Schin * Information and Software Systems Research * 14*4887Schin * AT&T Research * 15*4887Schin * Florham Park NJ * 16*4887Schin * * 17*4887Schin * Glenn Fowler <gsf@research.att.com> * 18*4887Schin * David Korn <dgk@research.att.com> * 19*4887Schin * Phong Vo <kpv@research.att.com> * 20*4887Schin * * 21*4887Schin ***********************************************************************/ 22*4887Schin #include "sfdchdr.h" 23*4887Schin 24*4887Schin #if _PACKAGE_ast 25*4887Schin #include <ast_tty.h> 26*4887Schin #include <signal.h> 27*4887Schin #endif 28*4887Schin 29*4887Schin /* 30*4887Schin * a simple but fast more style pager discipline 31*4887Schin * if on sfstdout then sfstdin ops reset the page state 32*4887Schin * 33*4887Schin * Glenn Fowler 34*4887Schin * AT&T Research 35*4887Schin * 36*4887Schin * @(#)$Id: sfdcmore (AT&T Research) 1998-06-25 $ 37*4887Schin */ 38*4887Schin 39*4887Schin typedef struct 40*4887Schin { 41*4887Schin Sfdisc_t disc; /* sfio discipline */ 42*4887Schin Sfio_t* input; /* tied with this input stream */ 43*4887Schin Sfio_t* error; /* tied with this error stream */ 44*4887Schin int rows; /* max rows */ 45*4887Schin int cols; /* max cols */ 46*4887Schin int row; /* current row */ 47*4887Schin int col; /* current col */ 48*4887Schin int match; /* match length, 0 if none */ 49*4887Schin char pattern[128]; /* match pattern */ 50*4887Schin char prompt[1]; /* prompt string */ 51*4887Schin } More_t; 52*4887Schin 53*4887Schin /* 54*4887Schin * more read 55*4887Schin * we assume line-at-a-time input 56*4887Schin */ 57*4887Schin 58*4887Schin #if __STD_C 59*4887Schin static ssize_t moreread(Sfio_t* f, void* buf, size_t n, Sfdisc_t* dp) 60*4887Schin #else 61*4887Schin static ssize_t moreread(f, buf, n, dp) 62*4887Schin Sfio_t* f; 63*4887Schin void* buf; 64*4887Schin size_t n; 65*4887Schin Sfdisc_t* dp; 66*4887Schin #endif 67*4887Schin { 68*4887Schin register More_t* more = (More_t*)dp; 69*4887Schin 70*4887Schin more->match = 0; 71*4887Schin more->row = 2; 72*4887Schin more->col = 1; 73*4887Schin return sfrd(f, buf, n, dp); 74*4887Schin } 75*4887Schin 76*4887Schin /* 77*4887Schin * output label on wfd and return next char on rfd with no echo 78*4887Schin * return < -1 is -(signal + 1) 79*4887Schin */ 80*4887Schin 81*4887Schin #if __STD_C 82*4887Schin static int ttyquery(Sfio_t* rp, Sfio_t* wp, const char* label, Sfdisc_t* dp) 83*4887Schin #else 84*4887Schin static int ttyquery(rp, wp, label, dp) 85*4887Schin Sfio_t* rp; 86*4887Schin Sfio_t* wp; 87*4887Schin char* label; 88*4887Schin Sfdisc_t* dp; 89*4887Schin #endif 90*4887Schin { 91*4887Schin register int r; 92*4887Schin int n; 93*4887Schin 94*4887Schin #ifdef TCSADRAIN 95*4887Schin unsigned char c; 96*4887Schin struct termios old; 97*4887Schin struct termios tty; 98*4887Schin int rfd = sffileno(rp); 99*4887Schin int wfd = sffileno(rp); 100*4887Schin 101*4887Schin if (!label) 102*4887Schin n = 0; 103*4887Schin else if (n = strlen(label)) 104*4887Schin write(wfd, label, n); 105*4887Schin tcgetattr(rfd, &old); 106*4887Schin tty = old; 107*4887Schin tty.c_cc[VTIME] = 0; 108*4887Schin tty.c_cc[VMIN] = 1; 109*4887Schin tty.c_lflag &= ~(ICANON|ECHO|ECHOK|ISIG); 110*4887Schin tcsetattr(rfd, TCSADRAIN, &tty); 111*4887Schin if ((r = read(rfd, &c, 1)) == 1) 112*4887Schin { 113*4887Schin if (c == old.c_cc[VEOF]) 114*4887Schin r = -1; 115*4887Schin else if (c == old.c_cc[VINTR]) 116*4887Schin r = -(SIGINT + 1); 117*4887Schin else if (c == old.c_cc[VQUIT]) 118*4887Schin r = -(SIGQUIT + 1); 119*4887Schin else if (c == '\r') 120*4887Schin r = '\n'; 121*4887Schin else 122*4887Schin r = c; 123*4887Schin } 124*4887Schin tcsetattr(rfd, TCSADRAIN, &old); 125*4887Schin if (n) 126*4887Schin { 127*4887Schin write(wfd, "\r", 1); 128*4887Schin while (n-- > 0) 129*4887Schin write(wfd, " ", 1); 130*4887Schin write(wfd, "\r", 1); 131*4887Schin } 132*4887Schin #else 133*4887Schin register char* s; 134*4887Schin 135*4887Schin if (label && (n = strlen(label))) 136*4887Schin sfwr(wp, label, n, dp); 137*4887Schin r = (s = sfgetr(rp, '\n', 0)) ? *s : -1; 138*4887Schin #endif 139*4887Schin return r; 140*4887Schin } 141*4887Schin 142*4887Schin /* 143*4887Schin * more write 144*4887Schin */ 145*4887Schin 146*4887Schin #if __STD_C 147*4887Schin static ssize_t morewrite(Sfio_t* f, const Void_t* buf, register size_t n, Sfdisc_t* dp) 148*4887Schin #else 149*4887Schin static ssize_t morewrite(f, buf, n, dp) 150*4887Schin Sfio_t* f; 151*4887Schin Void_t* buf; 152*4887Schin register size_t n; 153*4887Schin Sfdisc_t* dp; 154*4887Schin #endif 155*4887Schin { 156*4887Schin register More_t* more = (More_t*)dp; 157*4887Schin register char* b; 158*4887Schin register char* s; 159*4887Schin register char* e; 160*4887Schin register ssize_t w; 161*4887Schin register int r; 162*4887Schin 163*4887Schin if (!more->row) 164*4887Schin return n; 165*4887Schin if (!more->col) 166*4887Schin return sfwr(f, buf, n, dp); 167*4887Schin w = 0; 168*4887Schin b = (char*)buf; 169*4887Schin s = b; 170*4887Schin e = s + n; 171*4887Schin if (more->match) 172*4887Schin { 173*4887Schin match: 174*4887Schin for (r = more->pattern[0];; s++) 175*4887Schin { 176*4887Schin if (s >= e) 177*4887Schin return n; 178*4887Schin if (*s == '\n') 179*4887Schin b = s + 1; 180*4887Schin else if (*s == r && (e - s) >= more->match && !strncmp(s, more->pattern, more->match)) 181*4887Schin break; 182*4887Schin } 183*4887Schin s = b; 184*4887Schin w += b - (char*)buf; 185*4887Schin more->match = 0; 186*4887Schin } 187*4887Schin while (s < e) 188*4887Schin { 189*4887Schin switch (*s++) 190*4887Schin { 191*4887Schin case '\t': 192*4887Schin more->col = ((more->col + 8) & ~7) - 1; 193*4887Schin /*FALLTHROUGH*/ 194*4887Schin default: 195*4887Schin if (++more->col <= more->cols || s < e && *s == '\n') 196*4887Schin continue; 197*4887Schin /*FALLTHROUGH*/ 198*4887Schin case '\n': 199*4887Schin more->col = 1; 200*4887Schin if (++more->row < more->rows) 201*4887Schin continue; 202*4887Schin break; 203*4887Schin case '\b': 204*4887Schin if (more->col > 1) 205*4887Schin more->col--; 206*4887Schin continue; 207*4887Schin case '\r': 208*4887Schin more->col = 1; 209*4887Schin continue; 210*4887Schin } 211*4887Schin w += sfwr(f, b, s - b, dp); 212*4887Schin b = s; 213*4887Schin r = ttyquery(sfstdin, f, more->prompt, dp); 214*4887Schin if (r == '/' || r == 'n') 215*4887Schin { 216*4887Schin if (r == '/') 217*4887Schin { 218*4887Schin sfwr(f, "/", 1, dp); 219*4887Schin if ((s = sfgetr(sfstdin, '\n', 1)) && (n = sfvalue(sfstdin) - 1) > 0) 220*4887Schin { 221*4887Schin if (n >= sizeof(more->pattern)) 222*4887Schin n = sizeof(more->pattern) - 1; 223*4887Schin memcpy(more->pattern, s, n); 224*4887Schin more->pattern[n] = 0; 225*4887Schin } 226*4887Schin } 227*4887Schin if (more->match = strlen(more->pattern)) 228*4887Schin { 229*4887Schin more->row = 1; 230*4887Schin more->col = 1; 231*4887Schin goto match; 232*4887Schin } 233*4887Schin } 234*4887Schin switch (r) 235*4887Schin { 236*4887Schin case '\n': 237*4887Schin case '\r': 238*4887Schin more->row--; 239*4887Schin more->col = 1; 240*4887Schin break; 241*4887Schin case ' ': 242*4887Schin more->row = 2; 243*4887Schin more->col = 1; 244*4887Schin break; 245*4887Schin default: 246*4887Schin more->row = 0; 247*4887Schin return n; 248*4887Schin } 249*4887Schin } 250*4887Schin if (s > b) 251*4887Schin w += sfwr(f, b, s - b, dp); 252*4887Schin return w; 253*4887Schin } 254*4887Schin 255*4887Schin /* 256*4887Schin * remove the discipline on close 257*4887Schin */ 258*4887Schin 259*4887Schin #if __STD_C 260*4887Schin static int moreexcept(Sfio_t* f, int type, Void_t* data, Sfdisc_t* dp) 261*4887Schin #else 262*4887Schin static int moreexcept(f, type, data, dp) 263*4887Schin Sfio_t* f; 264*4887Schin int type; 265*4887Schin Void_t* data; 266*4887Schin Sfdisc_t* dp; 267*4887Schin #endif 268*4887Schin { 269*4887Schin register More_t* more = (More_t*)dp; 270*4887Schin 271*4887Schin if (type == SF_FINAL || type == SF_DPOP) 272*4887Schin { 273*4887Schin if (f = more->input) 274*4887Schin { 275*4887Schin more->input = 0; 276*4887Schin sfdisc(f, SF_POPDISC); 277*4887Schin } 278*4887Schin else if (f = more->error) 279*4887Schin { 280*4887Schin more->error = 0; 281*4887Schin sfdisc(f, SF_POPDISC); 282*4887Schin } 283*4887Schin else 284*4887Schin free(dp); 285*4887Schin } 286*4887Schin else if (type == SF_SYNC) 287*4887Schin { 288*4887Schin more->match = 0; 289*4887Schin more->row = 1; 290*4887Schin more->col = 1; 291*4887Schin } 292*4887Schin return 0; 293*4887Schin } 294*4887Schin 295*4887Schin /* 296*4887Schin * push the more discipline on f 297*4887Schin * if prompt==0 then a default ansi prompt is used 298*4887Schin * if rows==0 or cols==0 then they are deterimined from the tty 299*4887Schin * if f==sfstdout then input on sfstdin also resets the state 300*4887Schin */ 301*4887Schin 302*4887Schin #if __STD_C 303*4887Schin int sfdcmore(Sfio_t* f, const char* prompt, int rows, int cols) 304*4887Schin #else 305*4887Schin int sfdcmore(f, prompt, rows, cols) 306*4887Schin Sfio_t* f; 307*4887Schin char* prompt; 308*4887Schin int rows; 309*4887Schin int cols; 310*4887Schin #endif 311*4887Schin { 312*4887Schin register More_t* more; 313*4887Schin size_t n; 314*4887Schin 315*4887Schin /* 316*4887Schin * this is a writeonly discipline for interactive io 317*4887Schin */ 318*4887Schin 319*4887Schin if (!(sfset(f, 0, 0) & SF_WRITE) || !isatty(sffileno(sfstdin)) || !isatty(sffileno(sfstdout))) 320*4887Schin return -1; 321*4887Schin if (!prompt) 322*4887Schin prompt = "\033[7m More\033[m"; 323*4887Schin n = strlen(prompt) + 1; 324*4887Schin if (!(more = (More_t*)malloc(sizeof(More_t) + n))) 325*4887Schin return -1; 326*4887Schin memset(more, 0, sizeof(*more)); 327*4887Schin 328*4887Schin more->disc.readf = moreread; 329*4887Schin more->disc.writef = morewrite; 330*4887Schin more->disc.exceptf = moreexcept; 331*4887Schin memcpy(more->prompt, prompt, n); 332*4887Schin if (!rows || !cols) 333*4887Schin { 334*4887Schin #if _PACKAGE_ast 335*4887Schin astwinsize(sffileno(sfstdin), &rows, &cols); 336*4887Schin #endif 337*4887Schin if (!rows) 338*4887Schin rows = 24; 339*4887Schin if (!cols) 340*4887Schin cols = 80; 341*4887Schin } 342*4887Schin more->rows = rows; 343*4887Schin more->cols = cols; 344*4887Schin more->row = 1; 345*4887Schin more->col = 1; 346*4887Schin 347*4887Schin if (sfdisc(f, &more->disc) != &more->disc) 348*4887Schin { 349*4887Schin free(more); 350*4887Schin return -1; 351*4887Schin } 352*4887Schin if (f == sfstdout) 353*4887Schin { 354*4887Schin if (sfdisc(sfstdin, &more->disc) != &more->disc) 355*4887Schin { 356*4887Schin sfdisc(f, SF_POPDISC); 357*4887Schin return -1; 358*4887Schin } 359*4887Schin more->input = sfstdin; 360*4887Schin if (sfdisc(sfstderr, &more->disc) != &more->disc) 361*4887Schin { 362*4887Schin sfdisc(f, SF_POPDISC); 363*4887Schin return -1; 364*4887Schin } 365*4887Schin more->error = sfstdin; 366*4887Schin } 367*4887Schin 368*4887Schin return 0; 369*4887Schin } 370