121981Sdist /* 221981Sdist * Copyright (c) 1980 Regents of the University of California. 332736Sbostic * All rights reserved. 432736Sbostic * 532736Sbostic * Redistribution and use in source and binary forms are permitted 634880Sbostic * provided that the above copyright notice and this paragraph are 734880Sbostic * duplicated in all such forms and that any documentation, 834880Sbostic * advertising materials, and other materials related to such 934880Sbostic * distribution and use acknowledge that the software was developed 1034880Sbostic * by the University of California, Berkeley. The name of the 1134880Sbostic * University may not be used to endorse or promote products derived 1234880Sbostic * from this software without specific prior written permission. 1334880Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434880Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534880Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1621981Sdist */ 1721981Sdist 1813615Ssam #ifndef lint 1921981Sdist char copyright[] = 2021981Sdist "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 2121981Sdist All rights reserved.\n"; 2232736Sbostic #endif /* not lint */ 233594Sroot 2421981Sdist #ifndef lint 25*46779Sbostic static char sccsid[] = "@(#)more.c 5.25 (Berkeley) 02/28/91"; 2632736Sbostic #endif /* not lint */ 2721981Sdist 281500Serics /* 291500Serics ** more.c - General purpose tty output filter and file perusal program 301500Serics ** 311500Serics ** by Eric Shienbrood, UC Berkeley 323594Sroot ** 333594Sroot ** modified by Geoff Peck, UCB to add underlining, single spacing 343594Sroot ** modified by John Foderaro, UCB to add -c and MORE environment variable 351500Serics */ 361500Serics 3732736Sbostic #include <sys/param.h> 3846188Sbostic #include <sys/stat.h> 3946188Sbostic #include <sys/file.h> 401500Serics #include <signal.h> 411500Serics #include <errno.h> 421500Serics #include <sgtty.h> 431500Serics #include <setjmp.h> 4432736Sbostic #include <a.out.h> 4532736Sbostic #include <varargs.h> 4646188Sbostic #include <stdio.h> 4746188Sbostic #include <ctype.h> 4837943Sbostic #include "pathnames.h" 491500Serics 501500Serics #define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m)) 511500Serics #define Ftell(f) file_pos 521500Serics #define Fseek(f,off) (file_pos=off,fseek(f,off,0)) 531500Serics #define Getc(f) (++file_pos, getc(f)) 541500Serics #define Ungetc(c,f) (--file_pos, ungetc(c,f)) 551500Serics 561500Serics #define MBIT CBREAK 571500Serics #define stty(fd,argp) ioctl(fd,TIOCSETN,argp) 581500Serics 591500Serics #define TBUFSIZ 1024 601500Serics #define LINSIZ 256 6133232Sbostic #define ctrl(letter) (letter & 077) 621500Serics #define RUBOUT '\177' 631500Serics #define ESC '\033' 641500Serics #define QUIT '\034' 651500Serics 6616582Sleres struct sgttyb otty, savetty; 671500Serics long file_pos, file_size; 681500Serics int fnum, no_intty, no_tty, slow_tty; 69*46779Sbostic int dum_opt, dlines; 70*46779Sbostic void chgwinsz(), end_it(), onquit(), onsusp(); 711500Serics int nscroll = 11; /* Number of lines scrolled by 'd' */ 721500Serics int fold_opt = 1; /* Fold long lines */ 731500Serics int stop_opt = 1; /* Stop after form feeds */ 743594Sroot int ssp_opt = 0; /* Suppress white space */ 753594Sroot int ul_opt = 1; /* Underline as best we can */ 761500Serics int promptlen; 771500Serics int Currline; /* Line we are currently at */ 781500Serics int startup = 1; 791500Serics int firstf = 1; 801500Serics int notell = 1; 8117592Sleres int docrterase = 0; 8217592Sleres int docrtkill = 0; 831500Serics int bad_so; /* True if overwriting does not turn off standout */ 841500Serics int inwait, Pause, errors; 851500Serics int within; /* true if we are within a file, 861500Serics false if we are between files */ 8729907Smckusick int hard, dumb, noscroll, hardtabs, clreol, eatnl; 881500Serics int catch_susp; /* We should catch the SIGTSTP signal */ 891500Serics char **fnames; /* The list of file names */ 901500Serics int nfiles; /* Number of files left to process */ 911500Serics char *shell; /* The name of the shell to use */ 921500Serics int shellp; /* A previous shell command exists */ 931500Serics char ch; 941500Serics jmp_buf restore; 951500Serics char Line[LINSIZ]; /* Line buffer */ 961500Serics int Lpp = 24; /* lines per page */ 971500Serics char *Clear; /* clear screen */ 981500Serics char *eraseln; /* erase line */ 991500Serics char *Senter, *Sexit;/* enter and exit standout mode */ 1003594Sroot char *ULenter, *ULexit; /* enter and exit underline mode */ 1013594Sroot char *chUL; /* underline character */ 1023594Sroot char *chBS; /* backspace character */ 1033455Sroot char *Home; /* go to home */ 1043455Sroot char *cursorm; /* cursor movement */ 1053455Sroot char cursorhome[40]; /* contains cursor movement to home */ 1063594Sroot char *EodClr; /* clear rest of screen */ 1071500Serics char *tgetstr(); 1081500Serics int Mcol = 80; /* number of columns */ 1091500Serics int Wrap = 1; /* set if automargins */ 11016710Sjak int soglitch; /* terminal has standout mode glitch */ 11116710Sjak int ulglitch; /* terminal has underline mode glitch */ 11216710Sjak int pstate = 0; /* current UL state */ 1133455Sroot char *getenv(); 1141500Serics struct { 1151500Serics long chrctr, line; 1161500Serics } context, screen_start; 1171500Serics extern char PC; /* pad character */ 1181500Serics extern short ospeed; 1191500Serics 1201500Serics 1211500Serics main(argc, argv) 1221500Serics int argc; 1231500Serics char *argv[]; 1241500Serics { 1251500Serics register FILE *f; 1261500Serics register char *s; 1271500Serics register char *p; 1281500Serics register char ch; 1291500Serics register int left; 13016582Sleres int prnames = 0; 1311500Serics int initopt = 0; 1321500Serics int srchopt = 0; 1331500Serics int clearit = 0; 1341500Serics int initline; 1351500Serics char initbuf[80]; 1361500Serics FILE *checkf(); 1371500Serics 1381500Serics nfiles = argc; 1391500Serics fnames = argv; 1401500Serics initterm (); 14115813Sralph nscroll = Lpp/2 - 1; 14215813Sralph if (nscroll <= 0) 14315813Sralph nscroll = 1; 1443455Sroot if(s = getenv("MORE")) argscan(s); 1451500Serics while (--nfiles > 0) { 1461500Serics if ((ch = (*++fnames)[0]) == '-') { 1473455Sroot argscan(*fnames+1); 1481500Serics } 1491500Serics else if (ch == '+') { 1501500Serics s = *fnames; 1511500Serics if (*++s == '/') { 1521500Serics srchopt++; 1531500Serics for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';) 1541500Serics *p++ = *s++; 1551500Serics *p = '\0'; 1561500Serics } 1571500Serics else { 1581500Serics initopt++; 1591500Serics for (initline = 0; *s != '\0'; s++) 1601500Serics if (isdigit (*s)) 1611500Serics initline = initline*10 + *s -'0'; 1621500Serics --initline; 1631500Serics } 1641500Serics } 1651500Serics else break; 1661500Serics } 1673594Sroot /* allow clreol only if Home and eraseln and EodClr strings are 1683455Sroot * defined, and in that case, make sure we are in noscroll mode 1693455Sroot */ 1703455Sroot if(clreol) 1713455Sroot { 17218608Sralph if((Home == NULL) || (*Home == '\0') || 17318608Sralph (eraseln == NULL) || (*eraseln == '\0') || 17418608Sralph (EodClr == NULL) || (*EodClr == '\0') ) 17518608Sralph clreol = 0; 1763455Sroot else noscroll = 1; 1773455Sroot } 1781500Serics if (dlines == 0) 1791500Serics dlines = Lpp - (noscroll ? 1 : 2); 1801500Serics left = dlines; 1811500Serics if (nfiles > 1) 1821500Serics prnames++; 1831500Serics if (!no_intty && nfiles == 0) { 18434104Sbostic char *rindex(); 18534104Sbostic 18634104Sbostic p = rindex(argv[0], '/'); 18734104Sbostic fputs("usage: ",stderr); 18834104Sbostic fputs(p ? p + 1 : argv[0],stderr); 1891500Serics fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr); 1901500Serics exit(1); 1911500Serics } 1921500Serics else 1931500Serics f = stdin; 1941500Serics if (!no_tty) { 1951500Serics signal(SIGQUIT, onquit); 1961500Serics signal(SIGINT, end_it); 19727006Sdonn signal(SIGWINCH, chgwinsz); 1981500Serics if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) { 1991500Serics signal(SIGTSTP, onsusp); 2001500Serics catch_susp++; 2011500Serics } 20216582Sleres stty (fileno(stderr), &otty); 2031500Serics } 2041500Serics if (no_intty) { 2051500Serics if (no_tty) 2061500Serics copy_file (stdin); 2071500Serics else { 2081500Serics if ((ch = Getc (f)) == '\f') 2093594Sroot doclear(); 2101500Serics else { 2111500Serics Ungetc (ch, f); 2123594Sroot if (noscroll && (ch != EOF)) { 2133594Sroot if (clreol) 2143594Sroot home (); 2153594Sroot else 2163594Sroot doclear (); 2173455Sroot } 2181500Serics } 2191500Serics if (srchopt) 2203455Sroot { 2211500Serics search (initbuf, stdin, 1); 2223594Sroot if (noscroll) 2233594Sroot left--; 2243455Sroot } 2251500Serics else if (initopt) 2261500Serics skiplns (initline, stdin); 2271500Serics screen (stdin, left); 2281500Serics } 2291500Serics no_intty = 0; 2301500Serics prnames++; 2311500Serics firstf = 0; 2321500Serics } 2331500Serics 2341500Serics while (fnum < nfiles) { 2351500Serics if ((f = checkf (fnames[fnum], &clearit)) != NULL) { 2361500Serics context.line = context.chrctr = 0; 2371500Serics Currline = 0; 2381500Serics if (firstf) setjmp (restore); 2391500Serics if (firstf) { 2401500Serics firstf = 0; 2411500Serics if (srchopt) 2423455Sroot { 2431500Serics search (initbuf, f, 1); 2443594Sroot if (noscroll) 2453594Sroot left--; 2463455Sroot } 2471500Serics else if (initopt) 2481500Serics skiplns (initline, f); 2491500Serics } 2501500Serics else if (fnum < nfiles && !no_tty) { 2511500Serics setjmp (restore); 2521500Serics left = command (fnames[fnum], f); 2531500Serics } 2541500Serics if (left != 0) { 25532736Sbostic if ((noscroll || clearit) && (file_size != LONG_MAX)) 2563594Sroot if (clreol) 2573594Sroot home (); 2583594Sroot else 2593594Sroot doclear (); 2601500Serics if (prnames) { 2611500Serics if (bad_so) 2621500Serics erase (0); 2633594Sroot if (clreol) 2643594Sroot cleareol (); 2651500Serics pr("::::::::::::::"); 2661500Serics if (promptlen > 14) 2671500Serics erase (14); 268*46779Sbostic prtf ("\n"); 2693455Sroot if(clreol) cleareol(); 270*46779Sbostic prtf("%s\n", fnames[fnum]); 2713455Sroot if(clreol) cleareol(); 272*46779Sbostic prtf("::::::::::::::\n"); 2731500Serics if (left > Lpp - 4) 2741500Serics left = Lpp - 4; 2751500Serics } 2761500Serics if (no_tty) 2771500Serics copy_file (f); 2781500Serics else { 2791500Serics within++; 2801500Serics screen(f, left); 2811500Serics within = 0; 2821500Serics } 2831500Serics } 2841500Serics setjmp (restore); 2851500Serics fflush(stdout); 2861500Serics fclose(f); 2871500Serics screen_start.line = screen_start.chrctr = 0L; 2881500Serics context.line = context.chrctr = 0L; 2891500Serics } 2901500Serics fnum++; 2911500Serics firstf = 0; 2921500Serics } 2931500Serics reset_tty (); 2941500Serics exit(0); 2951500Serics } 2961500Serics 2973455Sroot argscan(s) 2983455Sroot char *s; 2993455Sroot { 30032273Sbostic int seen_num = 0; 30132273Sbostic 30232273Sbostic while (*s != '\0') { 30332273Sbostic switch (*s) { 30411604Slayer case '0': case '1': case '2': 30511604Slayer case '3': case '4': case '5': 30611604Slayer case '6': case '7': case '8': 30711604Slayer case '9': 30832273Sbostic if (!seen_num) { 30932273Sbostic dlines = 0; 31032273Sbostic seen_num = 1; 31132273Sbostic } 31211604Slayer dlines = dlines*10 + *s - '0'; 31311604Slayer break; 31411604Slayer case 'd': 31511604Slayer dum_opt = 1; 31611604Slayer break; 31711604Slayer case 'l': 31811604Slayer stop_opt = 0; 31911604Slayer break; 32011604Slayer case 'f': 32111604Slayer fold_opt = 0; 32211604Slayer break; 32311604Slayer case 'p': 32411604Slayer noscroll++; 32511604Slayer break; 32611604Slayer case 'c': 32711604Slayer clreol++; 32811604Slayer break; 32911604Slayer case 's': 33011604Slayer ssp_opt = 1; 33111604Slayer break; 33211604Slayer case 'u': 33311604Slayer ul_opt = 0; 33411604Slayer break; 33511604Slayer } 33632273Sbostic s++; 33711604Slayer } 3383455Sroot } 3393455Sroot 3403455Sroot 3411500Serics /* 3421500Serics ** Check whether the file named by fs is an ASCII file which the user may 3431500Serics ** access. If it is, return the opened file. Otherwise return NULL. 3441500Serics */ 3451500Serics 3461500Serics FILE * 3471500Serics checkf (fs, clearfirst) 34832736Sbostic register char *fs; 34932736Sbostic int *clearfirst; 3501500Serics { 35132736Sbostic struct stat stbuf; 35232736Sbostic register FILE *f; 35332736Sbostic char c; 3541500Serics 35532736Sbostic if (stat (fs, &stbuf) == -1) { 35632736Sbostic (void)fflush(stdout); 35732736Sbostic if (clreol) 35832736Sbostic cleareol (); 35932736Sbostic perror(fs); 36032736Sbostic return((FILE *)NULL); 36132736Sbostic } 36232736Sbostic if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { 363*46779Sbostic prtf("\n*** %s: directory ***\n\n", fs); 36432736Sbostic return((FILE *)NULL); 36532736Sbostic } 36632736Sbostic if ((f = Fopen(fs, "r")) == NULL) { 36732736Sbostic (void)fflush(stdout); 36832736Sbostic perror(fs); 36932736Sbostic return((FILE *)NULL); 37032736Sbostic } 37132736Sbostic if (magic(f, fs)) 37232736Sbostic return((FILE *)NULL); 37332736Sbostic c = Getc(f); 37432736Sbostic *clearfirst = c == '\f'; 3751500Serics Ungetc (c, f); 37632736Sbostic if ((file_size = stbuf.st_size) == 0) 37732736Sbostic file_size = LONG_MAX; 37832736Sbostic return(f); 3791500Serics } 3801500Serics 3811500Serics /* 38232736Sbostic * magic -- 38332736Sbostic * check for file magic numbers. This code would best be shared with 38432736Sbostic * the file(1) program or, perhaps, more should not try and be so smart? 38529906Smckusick */ 38632736Sbostic magic(f, fs) 38732736Sbostic FILE *f; 38832736Sbostic char *fs; 38929906Smckusick { 39032736Sbostic struct exec ex; 39129906Smckusick 39232736Sbostic if (fread(&ex, sizeof(ex), 1, f) == 1) 39332736Sbostic switch(ex.a_magic) { 39432736Sbostic case OMAGIC: 39532736Sbostic case NMAGIC: 39632736Sbostic case ZMAGIC: 39732736Sbostic case 0405: 39832736Sbostic case 0411: 39932736Sbostic case 0177545: 400*46779Sbostic prtf("\n******** %s: Not a text file ********\n\n", fs); 40132736Sbostic (void)fclose(f); 40232736Sbostic return(1); 40332736Sbostic } 40432736Sbostic (void)fseek(f, 0L, L_SET); /* rewind() not necessary */ 40533087Sbostic return(0); 40629906Smckusick } 40729906Smckusick 40829906Smckusick /* 4091500Serics ** A real function, for the tputs routine in termlib 4101500Serics */ 4111500Serics 4121500Serics putch (ch) 4131500Serics char ch; 4141500Serics { 4151500Serics putchar (ch); 4161500Serics } 4171500Serics 4181500Serics /* 4191500Serics ** Print out the contents of the file f, one screenful at a time. 4201500Serics */ 4211500Serics 4221500Serics #define STOP -10 4231500Serics 4241500Serics screen (f, num_lines) 4251500Serics register FILE *f; 4261500Serics register int num_lines; 4271500Serics { 4281500Serics register int c; 4291500Serics register int nchars; 4303594Sroot int length; /* length of current line */ 4313594Sroot static int prev_len = 1; /* length of previous line */ 4321500Serics 4331500Serics for (;;) { 4341500Serics while (num_lines > 0 && !Pause) { 4351500Serics if ((nchars = getline (f, &length)) == EOF) 4363455Sroot { 4373594Sroot if (clreol) 4383594Sroot clreos(); 4391500Serics return; 4403455Sroot } 4413594Sroot if (ssp_opt && length == 0 && prev_len == 0) 4423594Sroot continue; 4433594Sroot prev_len = length; 4441500Serics if (bad_so || (Senter && *Senter == ' ') && promptlen > 0) 4451500Serics erase (0); 4463594Sroot /* must clear before drawing line since tabs on some terminals 4473594Sroot * do not erase what they tab over. 4483594Sroot */ 4493594Sroot if (clreol) 4503594Sroot cleareol (); 4511500Serics prbuf (Line, length); 4521500Serics if (nchars < promptlen) 4531500Serics erase (nchars); /* erase () sets promptlen to 0 */ 4541500Serics else promptlen = 0; 4553594Sroot /* is this needed? 4563594Sroot * if (clreol) 4573594Sroot * cleareol(); /* must clear again in case we wrapped * 4583594Sroot */ 4591500Serics if (nchars < Mcol || !fold_opt) 46016710Sjak prbuf("\n", 1); /* will turn off UL if necessary */ 4611500Serics if (nchars == STOP) 4621500Serics break; 4631500Serics num_lines--; 4641500Serics } 46516710Sjak if (pstate) { 46616710Sjak tputs(ULexit, 1, putch); 46716710Sjak pstate = 0; 46816710Sjak } 4691500Serics fflush(stdout); 4701500Serics if ((c = Getc(f)) == EOF) 4713455Sroot { 4723594Sroot if (clreol) 4733594Sroot clreos (); 4741500Serics return; 4753455Sroot } 4763455Sroot 4773594Sroot if (Pause && clreol) 4783594Sroot clreos (); 4791500Serics Ungetc (c, f); 4801500Serics setjmp (restore); 4811500Serics Pause = 0; startup = 0; 4821500Serics if ((num_lines = command (NULL, f)) == 0) 4831500Serics return; 4841500Serics if (hard && promptlen > 0) 4851500Serics erase (0); 48611123Slayer if (noscroll && num_lines >= dlines) 48716582Sleres { 4883594Sroot if (clreol) 4893594Sroot home(); 4903594Sroot else 4913594Sroot doclear (); 4923455Sroot } 4931500Serics screen_start.line = Currline; 4941500Serics screen_start.chrctr = Ftell (f); 4951500Serics } 4961500Serics } 4971500Serics 4981500Serics /* 4991500Serics ** Come here if a quit signal is received 5001500Serics */ 5011500Serics 502*46779Sbostic void 5031500Serics onquit() 5041500Serics { 5051500Serics signal(SIGQUIT, SIG_IGN); 5061500Serics if (!inwait) { 5071500Serics putchar ('\n'); 5081500Serics if (!startup) { 5091500Serics signal(SIGQUIT, onquit); 5101500Serics longjmp (restore, 1); 5111500Serics } 5121500Serics else 5131500Serics Pause++; 5141500Serics } 5151500Serics else if (!dum_opt && notell) { 5161500Serics write (2, "[Use q or Q to quit]", 20); 5171500Serics promptlen += 20; 5181500Serics notell = 0; 5191500Serics } 5201500Serics signal(SIGQUIT, onquit); 5211500Serics } 5221500Serics 5231500Serics /* 52427006Sdonn ** Come here if a signal for a window size change is received 52527006Sdonn */ 52627006Sdonn 527*46779Sbostic void 52827006Sdonn chgwinsz() 52927006Sdonn { 53027006Sdonn struct winsize win; 53127006Sdonn 53227006Sdonn (void) signal(SIGWINCH, SIG_IGN); 53327006Sdonn if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) { 53427006Sdonn if (win.ws_row != 0) { 53527006Sdonn Lpp = win.ws_row; 53627006Sdonn nscroll = Lpp/2 - 1; 53727006Sdonn if (nscroll <= 0) 53827006Sdonn nscroll = 1; 53927006Sdonn dlines = Lpp - (noscroll ? 1 : 2); 54027006Sdonn } 54127006Sdonn if (win.ws_col != 0) 54227006Sdonn Mcol = win.ws_col; 54327006Sdonn } 54427006Sdonn (void) signal(SIGWINCH, chgwinsz); 54527006Sdonn } 54627006Sdonn 54727006Sdonn /* 5481500Serics ** Clean up terminal state and exit. Also come here if interrupt signal received 5491500Serics */ 5501500Serics 551*46779Sbostic void 5521500Serics end_it () 5531500Serics { 5541500Serics 5551500Serics reset_tty (); 5563594Sroot if (clreol) { 5573594Sroot putchar ('\r'); 5583594Sroot clreos (); 5593594Sroot fflush (stdout); 5603594Sroot } 5613455Sroot else if (!clreol && (promptlen > 0)) { 5621500Serics kill_line (); 5631500Serics fflush (stdout); 5641500Serics } 5651500Serics else 5661500Serics write (2, "\n", 1); 5671500Serics _exit(0); 5681500Serics } 5691500Serics 5701500Serics copy_file(f) 5711500Serics register FILE *f; 5721500Serics { 5731500Serics register int c; 5741500Serics 5751500Serics while ((c = getc(f)) != EOF) 5761500Serics putchar(c); 5771500Serics } 5781500Serics 5791500Serics /* Simplified printf function */ 5801500Serics 581*46779Sbostic prtf (fmt, va_alist) 5821500Serics register char *fmt; 58332737Sbostic va_dcl 5841500Serics { 58532737Sbostic va_list ap; 5861500Serics register char ch; 5871500Serics register int ccount; 5881500Serics 5891500Serics ccount = 0; 59032737Sbostic va_start(ap); 5911500Serics while (*fmt) { 5921500Serics while ((ch = *fmt++) != '%') { 5931500Serics if (ch == '\0') 5941500Serics return (ccount); 5951500Serics ccount++; 5961500Serics putchar (ch); 5971500Serics } 5981500Serics switch (*fmt++) { 5991500Serics case 'd': 60032737Sbostic ccount += printd (va_arg(ap, int)); 6011500Serics break; 6021500Serics case 's': 60332737Sbostic ccount += pr (va_arg(ap, char *)); 6041500Serics break; 6051500Serics case '%': 6061500Serics ccount++; 6071500Serics putchar ('%'); 6081500Serics break; 6091500Serics case '0': 6101500Serics return (ccount); 6111500Serics default: 6121500Serics break; 6131500Serics } 6141500Serics } 61532737Sbostic va_end(ap); 6161500Serics return (ccount); 6171500Serics 6181500Serics } 6191500Serics 6201500Serics /* 6211500Serics ** Print an integer as a string of decimal digits, 6221500Serics ** returning the length of the print representation. 6231500Serics */ 6241500Serics 6251500Serics printd (n) 6261500Serics int n; 6271500Serics { 6281500Serics int a, nchars; 6291500Serics 6301500Serics if (a = n/10) 6311500Serics nchars = 1 + printd(a); 6321500Serics else 6331500Serics nchars = 1; 6341500Serics putchar (n % 10 + '0'); 6351500Serics return (nchars); 6361500Serics } 6371500Serics 6381500Serics /* Put the print representation of an integer into a string */ 6391500Serics static char *sptr; 6401500Serics 6411500Serics scanstr (n, str) 6421500Serics int n; 6431500Serics char *str; 6441500Serics { 6451500Serics sptr = str; 64611604Slayer Sprintf (n); 6471500Serics *sptr = '\0'; 6481500Serics } 6491500Serics 65011604Slayer Sprintf (n) 6511500Serics { 6521500Serics int a; 6531500Serics 6541500Serics if (a = n/10) 65511604Slayer Sprintf (a); 6561500Serics *sptr++ = n % 10 + '0'; 6571500Serics } 6581500Serics 65933232Sbostic static char bell = ctrl('G'); 6601500Serics 6611500Serics strlen (s) 6621500Serics char *s; 6631500Serics { 6641500Serics register char *p; 6651500Serics 6661500Serics p = s; 6671500Serics while (*p++) 6681500Serics ; 6691500Serics return (p - s - 1); 6701500Serics } 6711500Serics 6721500Serics /* See whether the last component of the path name "path" is equal to the 6731500Serics ** string "string" 6741500Serics */ 6751500Serics 6761500Serics tailequ (path, string) 6771500Serics char *path; 6781500Serics register char *string; 6791500Serics { 6801500Serics register char *tail; 6811500Serics 6821500Serics tail = path + strlen(path); 6831500Serics while (tail >= path) 6841500Serics if (*(--tail) == '/') 6851500Serics break; 6861500Serics ++tail; 6871500Serics while (*tail++ == *string++) 6881500Serics if (*tail == '\0') 6891500Serics return(1); 6901500Serics return(0); 6911500Serics } 6921500Serics 6931500Serics prompt (filename) 6941500Serics char *filename; 6951500Serics { 6963594Sroot if (clreol) 6973594Sroot cleareol (); 6983455Sroot else if (promptlen > 0) 6991500Serics kill_line (); 7001500Serics if (!hard) { 7011500Serics promptlen = 8; 70216710Sjak if (Senter && Sexit) { 7031500Serics tputs (Senter, 1, putch); 70416710Sjak promptlen += (2 * soglitch); 70516710Sjak } 7063594Sroot if (clreol) 7073594Sroot cleareol (); 7081500Serics pr("--More--"); 7091500Serics if (filename != NULL) { 710*46779Sbostic promptlen += prtf ("(Next file: %s)", filename); 7111500Serics } 7121500Serics else if (!no_intty) { 713*46779Sbostic promptlen += prtf ("(%d%%)", (int)((file_pos * 100) / file_size)); 7141500Serics } 7151500Serics if (dum_opt) { 71616710Sjak promptlen += pr("[Press space to continue, 'q' to quit.]"); 7171500Serics } 7181500Serics if (Senter && Sexit) 7191500Serics tputs (Sexit, 1, putch); 7203594Sroot if (clreol) 7213594Sroot clreos (); 7221500Serics fflush(stdout); 7231500Serics } 7241500Serics else 7251500Serics write (2, &bell, 1); 7261500Serics inwait++; 7271500Serics } 7281500Serics 7291500Serics /* 7301500Serics ** Get a logical line 7311500Serics */ 7321500Serics 7331500Serics getline(f, length) 7341500Serics register FILE *f; 7351500Serics int *length; 7361500Serics { 7371500Serics register int c; 7381500Serics register char *p; 7391500Serics register int column; 7401500Serics static int colflg; 7411500Serics 7421500Serics p = Line; 7431500Serics column = 0; 7441500Serics c = Getc (f); 7451500Serics if (colflg && c == '\n') { 7461500Serics Currline++; 7471500Serics c = Getc (f); 7481500Serics } 7491500Serics while (p < &Line[LINSIZ - 1]) { 7501500Serics if (c == EOF) { 7511500Serics if (p > Line) { 7521500Serics *p = '\0'; 7531500Serics *length = p - Line; 7541500Serics return (column); 7551500Serics } 7561500Serics *length = p - Line; 7571500Serics return (EOF); 7581500Serics } 7591500Serics if (c == '\n') { 7601500Serics Currline++; 7611500Serics break; 7621500Serics } 7631500Serics *p++ = c; 7641500Serics if (c == '\t') 76529907Smckusick if (!hardtabs || column < promptlen && !hard) { 76629907Smckusick if (hardtabs && eraseln && !dumb) { 7671500Serics column = 1 + (column | 7); 7681500Serics tputs (eraseln, 1, putch); 7691500Serics promptlen = 0; 7701500Serics } 7711500Serics else { 77229907Smckusick for (--p; p < &Line[LINSIZ - 1];) { 7731500Serics *p++ = ' '; 77429907Smckusick if ((++column & 7) == 0) 77529907Smckusick break; 7761500Serics } 7771500Serics if (column >= promptlen) promptlen = 0; 7781500Serics } 7791500Serics } 7801500Serics else 7811500Serics column = 1 + (column | 7); 7829627Ssklower else if (c == '\b' && column > 0) 7831500Serics column--; 7841500Serics else if (c == '\r') 7851500Serics column = 0; 7861500Serics else if (c == '\f' && stop_opt) { 7871500Serics p[-1] = '^'; 7881500Serics *p++ = 'L'; 7891500Serics column += 2; 7901500Serics Pause++; 7911500Serics } 7921500Serics else if (c == EOF) { 7931500Serics *length = p - Line; 7941500Serics return (column); 7951500Serics } 7961500Serics else if (c >= ' ' && c != RUBOUT) 7971500Serics column++; 7981500Serics if (column >= Mcol && fold_opt) break; 7991500Serics c = Getc (f); 8001500Serics } 8011500Serics if (column >= Mcol && Mcol > 0) { 8021500Serics if (!Wrap) { 8031500Serics *p++ = '\n'; 8041500Serics } 8051500Serics } 8061500Serics colflg = column == Mcol && fold_opt; 80729907Smckusick if (colflg && eatnl && Wrap) { 80829907Smckusick *p++ = '\n'; /* simulate normal wrap */ 80929907Smckusick } 8101500Serics *length = p - Line; 8111500Serics *p = 0; 8121500Serics return (column); 8131500Serics } 8141500Serics 8151500Serics /* 8161500Serics ** Erase the rest of the prompt, assuming we are starting at column col. 8171500Serics */ 8181500Serics 8191500Serics erase (col) 8201500Serics register int col; 8211500Serics { 8221500Serics 8231500Serics if (promptlen == 0) 8241500Serics return; 8251500Serics if (hard) { 8261500Serics putchar ('\n'); 8271500Serics } 8281500Serics else { 8291500Serics if (col == 0) 8301500Serics putchar ('\r'); 8311500Serics if (!dumb && eraseln) 8321500Serics tputs (eraseln, 1, putch); 8331500Serics else 8341500Serics for (col = promptlen - col; col > 0; col--) 8351500Serics putchar (' '); 8361500Serics } 8371500Serics promptlen = 0; 8381500Serics } 8391500Serics 8401500Serics /* 8411500Serics ** Erase the current line entirely 8421500Serics */ 8431500Serics 8441500Serics kill_line () 8451500Serics { 8461500Serics erase (0); 8471500Serics if (!eraseln || dumb) putchar ('\r'); 8481500Serics } 8491500Serics 8501500Serics /* 8513455Sroot * force clear to end of line 8523455Sroot */ 8533455Sroot cleareol() 8543455Sroot { 8553594Sroot tputs(eraseln, 1, putch); 8563455Sroot } 8573455Sroot 8583594Sroot clreos() 8593455Sroot { 8603594Sroot tputs(EodClr, 1, putch); 8613455Sroot } 8623455Sroot 8633455Sroot /* 8641500Serics ** Print string and return number of characters 8651500Serics */ 8661500Serics 8671500Serics pr(s1) 8681500Serics char *s1; 8691500Serics { 8701500Serics register char *s; 8711500Serics register char c; 8721500Serics 8731500Serics for (s = s1; c = *s++; ) 8741500Serics putchar(c); 8751500Serics return (s - s1 - 1); 8761500Serics } 8771500Serics 8781500Serics 8791500Serics /* Print a buffer of n characters */ 8801500Serics 8811500Serics prbuf (s, n) 8821500Serics register char *s; 8831500Serics register int n; 8841500Serics { 88516710Sjak register char c; /* next output character */ 8863594Sroot register int state; /* next output char's UL state */ 88716710Sjak #define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_'))) 8883594Sroot 8893594Sroot while (--n >= 0) 8903594Sroot if (!ul_opt) 8913594Sroot putchar (*s++); 8923594Sroot else { 89316710Sjak if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) { 89416710Sjak s++; 89516710Sjak continue; 89616710Sjak } 89716710Sjak if (state = wouldul(s, n)) { 89816710Sjak c = (*s == '_')? s[2] : *s ; 8993594Sroot n -= 2; 90016710Sjak s += 3; 90116710Sjak } else 9023594Sroot c = *s++; 90316710Sjak if (state != pstate) { 90416710Sjak if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1)) 90516710Sjak state = 1; 90616710Sjak else 90716710Sjak tputs(state ? ULenter : ULexit, 1, putch); 9083594Sroot } 90916710Sjak if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0) 91016710Sjak putchar(c); 9113594Sroot if (state && *chUL) { 9123594Sroot pr(chBS); 9133594Sroot tputs(chUL, 1, putch); 9143594Sroot } 91516710Sjak pstate = state; 9163594Sroot } 9171500Serics } 9181500Serics 9191500Serics /* 9201500Serics ** Clear the screen 9211500Serics */ 9221500Serics 9231500Serics doclear() 9241500Serics { 9251500Serics if (Clear && !hard) { 9261500Serics tputs(Clear, 1, putch); 9271500Serics 9281500Serics /* Put out carriage return so that system doesn't 9291500Serics ** get confused by escape sequences when expanding tabs 9301500Serics */ 9311500Serics putchar ('\r'); 9321500Serics promptlen = 0; 9331500Serics } 9341500Serics } 9351500Serics 9363455Sroot /* 9373455Sroot * Go to home position 9383455Sroot */ 9393455Sroot home() 9403455Sroot { 9413455Sroot tputs(Home,1,putch); 9423455Sroot } 9433455Sroot 9441500Serics static int lastcmd, lastarg, lastp; 9451500Serics static int lastcolon; 9461500Serics char shell_line[132]; 9471500Serics 9481500Serics /* 9491500Serics ** Read a command and do it. A command consists of an optional integer 9501500Serics ** argument followed by the command character. Return the number of lines 9511500Serics ** to display in the next screenful. If there is nothing more to display 9521500Serics ** in the current file, zero is returned. 9531500Serics */ 9541500Serics 9551500Serics command (filename, f) 9561500Serics char *filename; 9571500Serics register FILE *f; 9581500Serics { 9591500Serics register int nlines; 9601500Serics register int retval; 9611500Serics register char c; 9621500Serics char colonch; 9631500Serics FILE *helpf; 9641500Serics int done; 9651500Serics char comchar, cmdbuf[80], *p; 9661500Serics 9671500Serics #define ret(val) retval=val;done++;break 9681500Serics 9691500Serics done = 0; 9701500Serics if (!errors) 9711500Serics prompt (filename); 9721500Serics else 9731500Serics errors = 0; 9741500Serics if (MBIT == RAW && slow_tty) { 9751500Serics otty.sg_flags |= MBIT; 97616582Sleres stty(fileno(stderr), &otty); 9771500Serics } 9781500Serics for (;;) { 9791500Serics nlines = number (&comchar); 9801500Serics lastp = colonch = 0; 9811500Serics if (comchar == '.') { /* Repeat last command */ 9821500Serics lastp++; 9831500Serics comchar = lastcmd; 9841500Serics nlines = lastarg; 9851500Serics if (lastcmd == ':') 9861500Serics colonch = lastcolon; 9871500Serics } 9881500Serics lastcmd = comchar; 9891500Serics lastarg = nlines; 9901500Serics if (comchar == otty.sg_erase) { 9911500Serics kill_line (); 9921500Serics prompt (filename); 9931500Serics continue; 9941500Serics } 9951500Serics switch (comchar) { 9961500Serics case ':': 9971500Serics retval = colon (filename, colonch, nlines); 9981500Serics if (retval >= 0) 9991500Serics done++; 10001500Serics break; 100124490Sbloom case 'b': 100233232Sbostic case ctrl('B'): 100324490Sbloom { 100424490Sbloom register int initline; 100524490Sbloom 100624490Sbloom if (no_intty) { 100724490Sbloom write(2, &bell, 1); 100824490Sbloom return (-1); 100924490Sbloom } 101024490Sbloom 101124490Sbloom if (nlines == 0) nlines++; 101224490Sbloom 101324490Sbloom putchar ('\r'); 101424490Sbloom erase (0); 1015*46779Sbostic prtf ("\n"); 101624490Sbloom if (clreol) 101724490Sbloom cleareol (); 1018*46779Sbostic prtf ("...back %d page", nlines); 101924490Sbloom if (nlines > 1) 102024490Sbloom pr ("s\n"); 102124490Sbloom else 102224490Sbloom pr ("\n"); 102324490Sbloom 102424490Sbloom if (clreol) 102524490Sbloom cleareol (); 102624490Sbloom pr ("\n"); 102724490Sbloom 102824490Sbloom initline = Currline - dlines * (nlines + 1); 102924490Sbloom if (! noscroll) 103024490Sbloom --initline; 103124490Sbloom if (initline < 0) initline = 0; 103224490Sbloom Fseek(f, 0L); 103324490Sbloom Currline = 0; /* skiplns() will make Currline correct */ 103424490Sbloom skiplns(initline, f); 103524490Sbloom if (! noscroll) { 103624490Sbloom ret(dlines + 1); 103724490Sbloom } 103824490Sbloom else { 103924490Sbloom ret(dlines); 104024490Sbloom } 104124490Sbloom } 10421500Serics case ' ': 10431500Serics case 'z': 10441500Serics if (nlines == 0) nlines = dlines; 10451500Serics else if (comchar == 'z') dlines = nlines; 10461500Serics ret (nlines); 10471500Serics case 'd': 104833232Sbostic case ctrl('D'): 10491500Serics if (nlines != 0) nscroll = nlines; 10501500Serics ret (nscroll); 10511500Serics case 'q': 10521500Serics case 'Q': 10531500Serics end_it (); 10541500Serics case 's': 10551500Serics case 'f': 10561500Serics if (nlines == 0) nlines++; 10571500Serics if (comchar == 'f') 10581500Serics nlines *= dlines; 10591500Serics putchar ('\r'); 10601500Serics erase (0); 1061*46779Sbostic prtf ("\n"); 10623594Sroot if (clreol) 10633594Sroot cleareol (); 1064*46779Sbostic prtf ("...skipping %d line", nlines); 10651500Serics if (nlines > 1) 10663594Sroot pr ("s\n"); 10671500Serics else 10683594Sroot pr ("\n"); 10693594Sroot 10703594Sroot if (clreol) 10713594Sroot cleareol (); 10723594Sroot pr ("\n"); 10733594Sroot 10741500Serics while (nlines > 0) { 10751500Serics while ((c = Getc (f)) != '\n') 10761500Serics if (c == EOF) { 10771500Serics retval = 0; 10781500Serics done++; 10791500Serics goto endsw; 10801500Serics } 10811500Serics Currline++; 10821500Serics nlines--; 10831500Serics } 10841500Serics ret (dlines); 10851500Serics case '\n': 10861500Serics if (nlines != 0) 10871500Serics dlines = nlines; 10881500Serics else 10891500Serics nlines = 1; 10901500Serics ret (nlines); 10911500Serics case '\f': 10921500Serics if (!no_intty) { 10931500Serics doclear (); 10941500Serics Fseek (f, screen_start.chrctr); 10951500Serics Currline = screen_start.line; 10961500Serics ret (dlines); 10971500Serics } 10981500Serics else { 10991500Serics write (2, &bell, 1); 11001500Serics break; 11011500Serics } 11021500Serics case '\'': 11031500Serics if (!no_intty) { 11041500Serics kill_line (); 11051500Serics pr ("\n***Back***\n\n"); 11061500Serics Fseek (f, context.chrctr); 11071500Serics Currline = context.line; 11081500Serics ret (dlines); 11091500Serics } 11101500Serics else { 11111500Serics write (2, &bell, 1); 11121500Serics break; 11131500Serics } 11141500Serics case '=': 11151500Serics kill_line (); 11161500Serics promptlen = printd (Currline); 11171500Serics fflush (stdout); 11181500Serics break; 11191500Serics case 'n': 11201500Serics lastp++; 11211500Serics case '/': 11221500Serics if (nlines == 0) nlines++; 11231500Serics kill_line (); 11241500Serics pr ("/"); 11251500Serics promptlen = 1; 11261500Serics fflush (stdout); 11271500Serics if (lastp) { 11281500Serics write (2,"\r", 1); 11291500Serics search (NULL, f, nlines); /* Use previous r.e. */ 11301500Serics } 11311500Serics else { 11321500Serics ttyin (cmdbuf, 78, '/'); 11331500Serics write (2, "\r", 1); 11341500Serics search (cmdbuf, f, nlines); 11351500Serics } 11363455Sroot ret (dlines-1); 11371500Serics case '!': 11381500Serics do_shell (filename); 11391500Serics break; 114024490Sbloom case '?': 11411500Serics case 'h': 11421500Serics if ((helpf = fopen (HELPFILE, "r")) == NULL) 11431500Serics error ("Can't open help file"); 11441500Serics if (noscroll) doclear (); 11451500Serics copy_file (helpf); 114627006Sdonn fclose (helpf); 11471500Serics prompt (filename); 11481500Serics break; 11491500Serics case 'v': /* This case should go right before default */ 11501500Serics if (!no_intty) { 11511500Serics kill_line (); 11521500Serics cmdbuf[0] = '+'; 115324490Sbloom scanstr (Currline - dlines < 0 ? 0 115424490Sbloom : Currline - (dlines + 1) / 2, &cmdbuf[1]); 11551500Serics pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]); 115638522Sbostic execute (filename, _PATH_VI, "vi", cmdbuf, fnames[fnum], 0); 11571500Serics break; 11581500Serics } 11591500Serics default: 116016710Sjak if (dum_opt) { 116116710Sjak kill_line (); 116216710Sjak if (Senter && Sexit) { 116316710Sjak tputs (Senter, 1, putch); 116416710Sjak promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch); 116516710Sjak tputs (Sexit, 1, putch); 116616710Sjak } 116716710Sjak else 116816710Sjak promptlen = pr ("[Press 'h' for instructions.]"); 116916710Sjak fflush (stdout); 117016710Sjak } 117116710Sjak else 117216710Sjak write (2, &bell, 1); 11731500Serics break; 11741500Serics } 11751500Serics if (done) break; 11761500Serics } 11771500Serics putchar ('\r'); 11781500Serics endsw: 11791500Serics inwait = 0; 11801500Serics notell++; 11811500Serics if (MBIT == RAW && slow_tty) { 11821500Serics otty.sg_flags &= ~MBIT; 118316582Sleres stty(fileno(stderr), &otty); 11841500Serics } 11851500Serics return (retval); 11861500Serics } 11871500Serics 11881500Serics char ch; 11891500Serics 11901500Serics /* 11911500Serics * Execute a colon-prefixed command. 11921500Serics * Returns <0 if not a command that should cause 11931500Serics * more of the file to be printed. 11941500Serics */ 11951500Serics 11961500Serics colon (filename, cmd, nlines) 11971500Serics char *filename; 11981500Serics int cmd; 11991500Serics int nlines; 12001500Serics { 12011500Serics if (cmd == 0) 12021500Serics ch = readch (); 12031500Serics else 12041500Serics ch = cmd; 12051500Serics lastcolon = ch; 12061500Serics switch (ch) { 12071500Serics case 'f': 12081500Serics kill_line (); 12091500Serics if (!no_intty) 1210*46779Sbostic promptlen = prtf ("\"%s\" line %d", fnames[fnum], Currline); 12111500Serics else 1212*46779Sbostic promptlen = prtf ("[Not a file] line %d", Currline); 12131500Serics fflush (stdout); 12141500Serics return (-1); 12151500Serics case 'n': 12161500Serics if (nlines == 0) { 12171500Serics if (fnum >= nfiles - 1) 12181500Serics end_it (); 12191500Serics nlines++; 12201500Serics } 12211500Serics putchar ('\r'); 12221500Serics erase (0); 12231500Serics skipf (nlines); 12241500Serics return (0); 12251500Serics case 'p': 12261500Serics if (no_intty) { 12271500Serics write (2, &bell, 1); 12281500Serics return (-1); 12291500Serics } 12301500Serics putchar ('\r'); 12311500Serics erase (0); 12321500Serics if (nlines == 0) 12331500Serics nlines++; 12341500Serics skipf (-nlines); 12351500Serics return (0); 12361500Serics case '!': 12371500Serics do_shell (filename); 12381500Serics return (-1); 12391500Serics case 'q': 12401500Serics case 'Q': 12411500Serics end_it (); 12421500Serics default: 12431500Serics write (2, &bell, 1); 12441500Serics return (-1); 12451500Serics } 12461500Serics } 12471500Serics 12481500Serics /* 12491500Serics ** Read a decimal number from the terminal. Set cmd to the non-digit which 12501500Serics ** terminates the number. 12511500Serics */ 12521500Serics 12531500Serics number(cmd) 12541500Serics char *cmd; 12551500Serics { 12561500Serics register int i; 12571500Serics 12581500Serics i = 0; ch = otty.sg_kill; 12591500Serics for (;;) { 12601500Serics ch = readch (); 12611500Serics if (ch >= '0' && ch <= '9') 12621500Serics i = i*10 + ch - '0'; 12631500Serics else if (ch == otty.sg_kill) 12641500Serics i = 0; 12651500Serics else { 12661500Serics *cmd = ch; 12671500Serics break; 12681500Serics } 12691500Serics } 12701500Serics return (i); 12711500Serics } 12721500Serics 12731500Serics do_shell (filename) 12741500Serics char *filename; 12751500Serics { 12761500Serics char cmdbuf[80]; 12771500Serics 12781500Serics kill_line (); 12791500Serics pr ("!"); 12801500Serics fflush (stdout); 12811500Serics promptlen = 1; 12821500Serics if (lastp) 12831500Serics pr (shell_line); 12841500Serics else { 12851500Serics ttyin (cmdbuf, 78, '!'); 12861500Serics if (expand (shell_line, cmdbuf)) { 12871500Serics kill_line (); 1288*46779Sbostic promptlen = prtf ("!%s", shell_line); 12891500Serics } 12901500Serics } 12911500Serics fflush (stdout); 12921500Serics write (2, "\n", 1); 12931500Serics promptlen = 0; 12941500Serics shellp = 1; 12951500Serics execute (filename, shell, shell, "-c", shell_line, 0); 12961500Serics } 12971500Serics 12981500Serics /* 12991500Serics ** Search for nth ocurrence of regular expression contained in buf in the file 13001500Serics */ 13011500Serics 13021500Serics search (buf, file, n) 13031500Serics char buf[]; 13041500Serics FILE *file; 13051500Serics register int n; 13061500Serics { 13071500Serics long startline = Ftell (file); 13081500Serics register long line1 = startline; 13091500Serics register long line2 = startline; 13101500Serics register long line3 = startline; 13111500Serics register int lncount; 13121500Serics int saveln, rv, re_exec(); 13131500Serics char *s, *re_comp(); 13141500Serics 13151500Serics context.line = saveln = Currline; 13161500Serics context.chrctr = startline; 13171500Serics lncount = 0; 13181500Serics if ((s = re_comp (buf)) != 0) 13191500Serics error (s); 13201500Serics while (!feof (file)) { 13211500Serics line3 = line2; 13221500Serics line2 = line1; 13231500Serics line1 = Ftell (file); 13241500Serics rdline (file); 13251500Serics lncount++; 13261500Serics if ((rv = re_exec (Line)) == 1) 13271500Serics if (--n == 0) { 13281500Serics if (lncount > 3 || (lncount > 1 && no_intty)) 13293455Sroot { 13303455Sroot pr ("\n"); 13313594Sroot if (clreol) 13323594Sroot cleareol (); 13333455Sroot pr("...skipping\n"); 13343455Sroot } 13351500Serics if (!no_intty) { 13361500Serics Currline -= (lncount >= 3 ? 3 : lncount); 13371500Serics Fseek (file, line3); 13383594Sroot if (noscroll) 13393594Sroot if (clreol) { 13403594Sroot home (); 13413594Sroot cleareol (); 134216582Sleres } 13433594Sroot else 13443594Sroot doclear (); 13451500Serics } 13461500Serics else { 13471500Serics kill_line (); 13483594Sroot if (noscroll) 13493594Sroot if (clreol) { 135016582Sleres home (); 13513594Sroot cleareol (); 135216582Sleres } 13533594Sroot else 13543594Sroot doclear (); 13551500Serics pr (Line); 13561500Serics putchar ('\n'); 13571500Serics } 13581500Serics break; 13591500Serics } 13601500Serics else if (rv == -1) 13611500Serics error ("Regular expression botch"); 13621500Serics } 13631500Serics if (feof (file)) { 13641500Serics if (!no_intty) { 136546188Sbostic /* file->_flag &= ~_IOEOF; /* why doesn't fseek do this ??!!??! */ 13661500Serics Currline = saveln; 13671500Serics Fseek (file, startline); 13681500Serics } 13691500Serics else { 13701500Serics pr ("\nPattern not found\n"); 13711500Serics end_it (); 13721500Serics } 13731500Serics error ("Pattern not found"); 13741500Serics } 13751500Serics } 13761500Serics 137732737Sbostic /*VARARGS2*/ 137832737Sbostic execute (filename, cmd, va_alist) 13791500Serics char *filename; 138032737Sbostic char *cmd; 138132737Sbostic va_dcl 13821500Serics { 13831500Serics int id; 138416710Sjak int n; 138532737Sbostic va_list argp; 13861500Serics 13871500Serics fflush (stdout); 13881500Serics reset_tty (); 138916710Sjak for (n = 10; (id = fork ()) < 0 && n > 0; n--) 13901500Serics sleep (5); 13911500Serics if (id == 0) { 139216710Sjak if (!isatty(0)) { 139316710Sjak close(0); 139416710Sjak open("/dev/tty", 0); 139516710Sjak } 139632737Sbostic va_start(argp); 139732737Sbostic execv (cmd, argp); 13981500Serics write (2, "exec failed\n", 12); 13991500Serics exit (1); 140033087Sbostic va_end(argp); /* balance {}'s for some UNIX's */ 14011500Serics } 140216710Sjak if (id > 0) { 140316710Sjak signal (SIGINT, SIG_IGN); 140416710Sjak signal (SIGQUIT, SIG_IGN); 140516710Sjak if (catch_susp) 140616710Sjak signal(SIGTSTP, SIG_DFL); 140716710Sjak while (wait(0) > 0); 140816710Sjak signal (SIGINT, end_it); 140916710Sjak signal (SIGQUIT, onquit); 141016710Sjak if (catch_susp) 141116710Sjak signal(SIGTSTP, onsusp); 141216710Sjak } else 141316710Sjak write(2, "can't fork\n", 11); 14141500Serics set_tty (); 14151500Serics pr ("------------------------\n"); 14161500Serics prompt (filename); 14171500Serics } 14181500Serics /* 14191500Serics ** Skip n lines in the file f 14201500Serics */ 14211500Serics 14221500Serics skiplns (n, f) 14231500Serics register int n; 14241500Serics register FILE *f; 14251500Serics { 14261500Serics register char c; 14271500Serics 14281500Serics while (n > 0) { 14291500Serics while ((c = Getc (f)) != '\n') 14301500Serics if (c == EOF) 14311500Serics return; 14321500Serics n--; 14331500Serics Currline++; 14341500Serics } 14351500Serics } 14361500Serics 14371500Serics /* 14381500Serics ** Skip nskip files in the file list (from the command line). Nskip may be 14391500Serics ** negative. 14401500Serics */ 14411500Serics 14421500Serics skipf (nskip) 14431500Serics register int nskip; 14441500Serics { 14451500Serics if (nskip == 0) return; 14461500Serics if (nskip > 0) { 14471500Serics if (fnum + nskip > nfiles - 1) 14481500Serics nskip = nfiles - fnum - 1; 14491500Serics } 14501500Serics else if (within) 14511500Serics ++fnum; 14521500Serics fnum += nskip; 14531500Serics if (fnum < 0) 14541500Serics fnum = 0; 14553594Sroot pr ("\n...Skipping "); 14563455Sroot pr ("\n"); 14573594Sroot if (clreol) 14583594Sroot cleareol (); 14593455Sroot pr ("...Skipping "); 14601500Serics pr (nskip > 0 ? "to file " : "back to file "); 14611500Serics pr (fnames[fnum]); 14623455Sroot pr ("\n"); 14633594Sroot if (clreol) 14643594Sroot cleareol (); 14653455Sroot pr ("\n"); 14661500Serics --fnum; 14671500Serics } 14681500Serics 14691500Serics /*----------------------------- Terminal I/O -------------------------------*/ 14701500Serics 14711500Serics initterm () 14721500Serics { 14731500Serics char buf[TBUFSIZ]; 147417195Sralph static char clearbuf[TBUFSIZ]; 14751500Serics char *clearptr, *padstr; 14761500Serics int ldisc; 147717592Sleres int lmode; 147810823Ssam char *term; 147916582Sleres int tgrp; 148018030Sbloom struct winsize win; 14811500Serics 148216582Sleres retry: 148316582Sleres if (!(no_tty = gtty(fileno(stdout), &otty))) { 148417592Sleres if (ioctl(fileno(stdout), TIOCLGET, &lmode) < 0) { 148517592Sleres perror("TIOCLGET"); 148617592Sleres exit(1); 148717592Sleres } 148817592Sleres docrterase = ((lmode & LCRTERA) != 0); 148917592Sleres docrtkill = ((lmode & LCRTKIL) != 0); 149016582Sleres /* 149117592Sleres * Wait until we're in the foreground before we save the 149217592Sleres * the terminal modes. 149316582Sleres */ 149416582Sleres if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) { 149517592Sleres perror("TIOCGPGRP"); 149616582Sleres exit(1); 149716582Sleres } 149816582Sleres if (tgrp != getpgrp(0)) { 149916582Sleres kill(0, SIGTTOU); 150016582Sleres goto retry; 150116582Sleres } 150213830Skre if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) { 15033594Sroot dumb++; ul_opt = 0; 15041500Serics } 15051500Serics else { 150618030Sbloom if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) { 150718030Sbloom Lpp = tgetnum("li"); 150818030Sbloom Mcol = tgetnum("co"); 150918030Sbloom } else { 151018030Sbloom if ((Lpp = win.ws_row) == 0) 151118030Sbloom Lpp = tgetnum("li"); 151218030Sbloom if ((Mcol = win.ws_col) == 0) 151318030Sbloom Mcol = tgetnum("co"); 151418030Sbloom } 151518030Sbloom if ((Lpp <= 0) || tgetflag("hc")) { 15161500Serics hard++; /* Hard copy terminal */ 15171500Serics Lpp = 24; 15181500Serics } 151929907Smckusick if (tgetflag("xn")) 152029907Smckusick eatnl++; /* Eat newline at last column + 1; dec, concept */ 152118030Sbloom if (Mcol <= 0) 152218030Sbloom Mcol = 80; 152318030Sbloom 15241500Serics if (tailequ (fnames[0], "page") || !hard && tgetflag("ns")) 15251500Serics noscroll++; 15261500Serics Wrap = tgetflag("am"); 15271500Serics bad_so = tgetflag ("xs"); 15281500Serics clearptr = clearbuf; 15291500Serics eraseln = tgetstr("ce",&clearptr); 15301500Serics Clear = tgetstr("cl", &clearptr); 15311500Serics Senter = tgetstr("so", &clearptr); 15321500Serics Sexit = tgetstr("se", &clearptr); 153316710Sjak if ((soglitch = tgetnum("sg")) < 0) 153416710Sjak soglitch = 0; 15353594Sroot 15363594Sroot /* 15373594Sroot * Set up for underlining: some terminals don't need it; 15383594Sroot * others have start/stop sequences, still others have an 15393594Sroot * underline char sequence which is assumed to move the 15403594Sroot * cursor forward one character. If underline sequence 15413594Sroot * isn't available, settle for standout sequence. 15423594Sroot */ 15433594Sroot 15443594Sroot if (tgetflag("ul") || tgetflag("os")) 15453594Sroot ul_opt = 0; 15463594Sroot if ((chUL = tgetstr("uc", &clearptr)) == NULL ) 15473594Sroot chUL = ""; 154816710Sjak if (((ULenter = tgetstr("us", &clearptr)) == NULL || 154916710Sjak (ULexit = tgetstr("ue", &clearptr)) == NULL) && !*chUL) { 155016710Sjak if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) { 155116710Sjak ULenter = ""; 155216710Sjak ULexit = ""; 155316710Sjak } else 155416710Sjak ulglitch = soglitch; 155516710Sjak } else { 155616710Sjak if ((ulglitch = tgetnum("ug")) < 0) 155716710Sjak ulglitch = 0; 155816710Sjak } 155916582Sleres 15601500Serics if (padstr = tgetstr("pc", &clearptr)) 15611500Serics PC = *padstr; 15623455Sroot Home = tgetstr("ho",&clearptr); 156313536Ssam if (Home == 0 || *Home == '\0') 15643455Sroot { 15653594Sroot if ((cursorm = tgetstr("cm", &clearptr)) != NULL) { 15663594Sroot strcpy(cursorhome, tgoto(cursorm, 0, 0)); 15673455Sroot Home = cursorhome; 15683455Sroot } 15693455Sroot } 15703594Sroot EodClr = tgetstr("cd", &clearptr); 157125540Smckusick if ((chBS = tgetstr("bc", &clearptr)) == NULL) 157225540Smckusick chBS = "\b"; 157325540Smckusick 15741500Serics } 15751500Serics if ((shell = getenv("SHELL")) == NULL) 15761500Serics shell = "/bin/sh"; 15771500Serics } 157816582Sleres no_intty = gtty(fileno(stdin), &otty); 157916582Sleres gtty(fileno(stderr), &otty); 158013830Skre savetty = otty; 15811500Serics ospeed = otty.sg_ospeed; 15821500Serics slow_tty = ospeed < B1200; 158327006Sdonn hardtabs = (otty.sg_flags & TBDELAY) != XTABS; 15841500Serics if (!no_tty) { 15851500Serics otty.sg_flags &= ~ECHO; 15861500Serics if (MBIT == CBREAK || !slow_tty) 15871500Serics otty.sg_flags |= MBIT; 15881500Serics } 15891500Serics } 15901500Serics 15911500Serics readch () 15921500Serics { 15931500Serics char ch; 15941500Serics extern int errno; 15951500Serics 159631089Skarels errno = 0; 15971500Serics if (read (2, &ch, 1) <= 0) 15981500Serics if (errno != EINTR) 159931089Skarels end_it(); 16001500Serics else 16011500Serics ch = otty.sg_kill; 16021500Serics return (ch); 16031500Serics } 16041500Serics 16051500Serics static char BS = '\b'; 160617592Sleres static char *BSB = "\b \b"; 16071500Serics static char CARAT = '^'; 160817592Sleres #define ERASEONECHAR \ 160917592Sleres if (docrterase) \ 161017592Sleres write (2, BSB, sizeof(BSB)); \ 161117592Sleres else \ 161217592Sleres write (2, &BS, sizeof(BS)); 16131500Serics 16141500Serics ttyin (buf, nmax, pchar) 16151500Serics char buf[]; 16161500Serics register int nmax; 16171500Serics char pchar; 16181500Serics { 16191500Serics register char *sptr; 16201500Serics register char ch; 16211500Serics register int slash = 0; 16221500Serics int maxlen; 16231500Serics char cbuf; 16241500Serics 16251500Serics sptr = buf; 16261500Serics maxlen = 0; 16271500Serics while (sptr - buf < nmax) { 16281500Serics if (promptlen > maxlen) maxlen = promptlen; 16291500Serics ch = readch (); 16301500Serics if (ch == '\\') { 16311500Serics slash++; 16321500Serics } 16331500Serics else if ((ch == otty.sg_erase) && !slash) { 16341500Serics if (sptr > buf) { 16351500Serics --promptlen; 163617592Sleres ERASEONECHAR 16371500Serics --sptr; 16381500Serics if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) { 16391500Serics --promptlen; 164017592Sleres ERASEONECHAR 16411500Serics } 16421500Serics continue; 16431500Serics } 16441500Serics else { 16451500Serics if (!eraseln) promptlen = maxlen; 16461500Serics longjmp (restore, 1); 16471500Serics } 16481500Serics } 16491500Serics else if ((ch == otty.sg_kill) && !slash) { 16501500Serics if (hard) { 16511500Serics show (ch); 16521500Serics putchar ('\n'); 16531500Serics putchar (pchar); 16541500Serics } 16551500Serics else { 16561500Serics putchar ('\r'); 16571500Serics putchar (pchar); 16581500Serics if (eraseln) 16591500Serics erase (1); 166017592Sleres else if (docrtkill) 166117592Sleres while (promptlen-- > 1) 166217592Sleres write (2, BSB, sizeof(BSB)); 16631500Serics promptlen = 1; 16641500Serics } 16651500Serics sptr = buf; 16661500Serics fflush (stdout); 16671500Serics continue; 16681500Serics } 16691500Serics if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) { 167017592Sleres ERASEONECHAR 16711500Serics --sptr; 16721500Serics } 16731500Serics if (ch != '\\') 16741500Serics slash = 0; 16751500Serics *sptr++ = ch; 16761500Serics if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { 16771500Serics ch += ch == RUBOUT ? -0100 : 0100; 16781500Serics write (2, &CARAT, 1); 16791500Serics promptlen++; 16801500Serics } 16811500Serics cbuf = ch; 16821500Serics if (ch != '\n' && ch != ESC) { 16831500Serics write (2, &cbuf, 1); 16841500Serics promptlen++; 16851500Serics } 16861500Serics else 16871500Serics break; 16881500Serics } 16891500Serics *--sptr = '\0'; 16901500Serics if (!eraseln) promptlen = maxlen; 16911500Serics if (sptr - buf >= nmax - 1) 16921500Serics error ("Line too long"); 16931500Serics } 16941500Serics 16951500Serics expand (outbuf, inbuf) 16961500Serics char *outbuf; 16971500Serics char *inbuf; 16981500Serics { 16991500Serics register char *instr; 17001500Serics register char *outstr; 17011500Serics register char ch; 17021500Serics char temp[200]; 17031500Serics int changed = 0; 17041500Serics 17051500Serics instr = inbuf; 17061500Serics outstr = temp; 17071500Serics while ((ch = *instr++) != '\0') 17081500Serics switch (ch) { 17091500Serics case '%': 17101500Serics if (!no_intty) { 17111500Serics strcpy (outstr, fnames[fnum]); 17121500Serics outstr += strlen (fnames[fnum]); 17131500Serics changed++; 17141500Serics } 17151500Serics else 17161500Serics *outstr++ = ch; 17171500Serics break; 17181500Serics case '!': 17191500Serics if (!shellp) 17201500Serics error ("No previous command to substitute for"); 17211500Serics strcpy (outstr, shell_line); 17221500Serics outstr += strlen (shell_line); 17231500Serics changed++; 17241500Serics break; 17251500Serics case '\\': 17261500Serics if (*instr == '%' || *instr == '!') { 17271500Serics *outstr++ = *instr++; 17281500Serics break; 17291500Serics } 17301500Serics default: 17311500Serics *outstr++ = ch; 17321500Serics } 17331500Serics *outstr++ = '\0'; 17341500Serics strcpy (outbuf, temp); 17351500Serics return (changed); 17361500Serics } 17371500Serics 17381500Serics show (ch) 17391500Serics register char ch; 17401500Serics { 17411500Serics char cbuf; 17421500Serics 17431500Serics if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { 17441500Serics ch += ch == RUBOUT ? -0100 : 0100; 17451500Serics write (2, &CARAT, 1); 17461500Serics promptlen++; 17471500Serics } 17481500Serics cbuf = ch; 17491500Serics write (2, &cbuf, 1); 17501500Serics promptlen++; 17511500Serics } 17521500Serics 17531500Serics error (mess) 17541500Serics char *mess; 17551500Serics { 17563594Sroot if (clreol) 17573594Sroot cleareol (); 17583594Sroot else 17593594Sroot kill_line (); 17601500Serics promptlen += strlen (mess); 17611500Serics if (Senter && Sexit) { 17621500Serics tputs (Senter, 1, putch); 17631500Serics pr(mess); 17641500Serics tputs (Sexit, 1, putch); 17651500Serics } 17661500Serics else 17671500Serics pr (mess); 17681500Serics fflush(stdout); 17691500Serics errors++; 17701500Serics longjmp (restore, 1); 17711500Serics } 17721500Serics 17731500Serics 17741500Serics set_tty () 17751500Serics { 17761500Serics otty.sg_flags |= MBIT; 17771500Serics otty.sg_flags &= ~ECHO; 177816582Sleres stty(fileno(stderr), &otty); 17791500Serics } 17801500Serics 17811500Serics reset_tty () 17821500Serics { 178331033Sbostic if (no_tty) 178431033Sbostic return; 178516710Sjak if (pstate) { 178616710Sjak tputs(ULexit, 1, putch); 178716710Sjak fflush(stdout); 178816710Sjak pstate = 0; 178916710Sjak } 17901500Serics otty.sg_flags |= ECHO; 17911500Serics otty.sg_flags &= ~MBIT; 179216582Sleres stty(fileno(stderr), &savetty); 17931500Serics } 17941500Serics 17951500Serics rdline (f) 17961500Serics register FILE *f; 17971500Serics { 17981500Serics register char c; 17991500Serics register char *p; 18001500Serics 18011500Serics p = Line; 18021500Serics while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1) 18031500Serics *p++ = c; 18041500Serics if (c == '\n') 18051500Serics Currline++; 18061500Serics *p = '\0'; 18071500Serics } 18081500Serics 18091500Serics /* Come here when we get a suspend signal from the terminal */ 18101500Serics 1811*46779Sbostic void 18121500Serics onsusp () 18131500Serics { 181414861Skarels /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */ 181514861Skarels signal(SIGTTOU, SIG_IGN); 18161500Serics reset_tty (); 18171500Serics fflush (stdout); 181814861Skarels signal(SIGTTOU, SIG_DFL); 18191500Serics /* Send the TSTP signal to suspend our process group */ 182013289Ssam signal(SIGTSTP, SIG_DFL); 182113289Ssam sigsetmask(0); 18221500Serics kill (0, SIGTSTP); 18231500Serics /* Pause for station break */ 18241500Serics 18251500Serics /* We're back */ 18261500Serics signal (SIGTSTP, onsusp); 18271500Serics set_tty (); 18281500Serics if (inwait) 1829*46779Sbostic longjmp (restore, 1); 18301500Serics } 1831