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 6*34880Sbostic * provided that the above copyright notice and this paragraph are 7*34880Sbostic * duplicated in all such forms and that any documentation, 8*34880Sbostic * advertising materials, and other materials related to such 9*34880Sbostic * distribution and use acknowledge that the software was developed 10*34880Sbostic * by the University of California, Berkeley. The name of the 11*34880Sbostic * University may not be used to endorse or promote products derived 12*34880Sbostic * from this software without specific prior written permission. 13*34880Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34880Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34880Sbostic * 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*34880Sbostic static char sccsid[] = "@(#)more.c 5.19 (Berkeley) 06/29/88"; 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 371500Serics #include <stdio.h> 3832736Sbostic #include <sys/param.h> 391500Serics #include <ctype.h> 401500Serics #include <signal.h> 411500Serics #include <errno.h> 421500Serics #include <sgtty.h> 431500Serics #include <setjmp.h> 441500Serics #include <sys/stat.h> 4529906Smckusick #include <sys/file.h> 4632736Sbostic #include <a.out.h> 4732736Sbostic #include <varargs.h> 481500Serics 4913615Ssam #define HELPFILE "/usr/lib/more.help" 5013615Ssam #define VI "/usr/ucb/vi" 511500Serics 521500Serics #define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m)) 531500Serics #define Ftell(f) file_pos 541500Serics #define Fseek(f,off) (file_pos=off,fseek(f,off,0)) 551500Serics #define Getc(f) (++file_pos, getc(f)) 561500Serics #define Ungetc(c,f) (--file_pos, ungetc(c,f)) 571500Serics 581500Serics #define MBIT CBREAK 591500Serics #define stty(fd,argp) ioctl(fd,TIOCSETN,argp) 601500Serics 611500Serics #define TBUFSIZ 1024 621500Serics #define LINSIZ 256 6333232Sbostic #define ctrl(letter) (letter & 077) 641500Serics #define RUBOUT '\177' 651500Serics #define ESC '\033' 661500Serics #define QUIT '\034' 671500Serics 6816582Sleres struct sgttyb otty, savetty; 691500Serics long file_pos, file_size; 701500Serics int fnum, no_intty, no_tty, slow_tty; 7127006Sdonn int dum_opt, dlines, onquit(), end_it(), chgwinsz(); 721500Serics int onsusp(); 731500Serics int nscroll = 11; /* Number of lines scrolled by 'd' */ 741500Serics int fold_opt = 1; /* Fold long lines */ 751500Serics int stop_opt = 1; /* Stop after form feeds */ 763594Sroot int ssp_opt = 0; /* Suppress white space */ 773594Sroot int ul_opt = 1; /* Underline as best we can */ 781500Serics int promptlen; 791500Serics int Currline; /* Line we are currently at */ 801500Serics int startup = 1; 811500Serics int firstf = 1; 821500Serics int notell = 1; 8317592Sleres int docrterase = 0; 8417592Sleres int docrtkill = 0; 851500Serics int bad_so; /* True if overwriting does not turn off standout */ 861500Serics int inwait, Pause, errors; 871500Serics int within; /* true if we are within a file, 881500Serics false if we are between files */ 8929907Smckusick int hard, dumb, noscroll, hardtabs, clreol, eatnl; 901500Serics int catch_susp; /* We should catch the SIGTSTP signal */ 911500Serics char **fnames; /* The list of file names */ 921500Serics int nfiles; /* Number of files left to process */ 931500Serics char *shell; /* The name of the shell to use */ 941500Serics int shellp; /* A previous shell command exists */ 951500Serics char ch; 961500Serics jmp_buf restore; 971500Serics char Line[LINSIZ]; /* Line buffer */ 981500Serics int Lpp = 24; /* lines per page */ 991500Serics char *Clear; /* clear screen */ 1001500Serics char *eraseln; /* erase line */ 1011500Serics char *Senter, *Sexit;/* enter and exit standout mode */ 1023594Sroot char *ULenter, *ULexit; /* enter and exit underline mode */ 1033594Sroot char *chUL; /* underline character */ 1043594Sroot char *chBS; /* backspace character */ 1053455Sroot char *Home; /* go to home */ 1063455Sroot char *cursorm; /* cursor movement */ 1073455Sroot char cursorhome[40]; /* contains cursor movement to home */ 1083594Sroot char *EodClr; /* clear rest of screen */ 1091500Serics char *tgetstr(); 1101500Serics int Mcol = 80; /* number of columns */ 1111500Serics int Wrap = 1; /* set if automargins */ 11216710Sjak int soglitch; /* terminal has standout mode glitch */ 11316710Sjak int ulglitch; /* terminal has underline mode glitch */ 11416710Sjak int pstate = 0; /* current UL state */ 1151500Serics long fseek(); 1163455Sroot char *getenv(); 1171500Serics struct { 1181500Serics long chrctr, line; 1191500Serics } context, screen_start; 1201500Serics extern char PC; /* pad character */ 1211500Serics extern short ospeed; 1221500Serics 1231500Serics 1241500Serics main(argc, argv) 1251500Serics int argc; 1261500Serics char *argv[]; 1271500Serics { 1281500Serics register FILE *f; 1291500Serics register char *s; 1301500Serics register char *p; 1311500Serics register char ch; 1321500Serics register int left; 13316582Sleres int prnames = 0; 1341500Serics int initopt = 0; 1351500Serics int srchopt = 0; 1361500Serics int clearit = 0; 1371500Serics int initline; 1381500Serics char initbuf[80]; 1391500Serics FILE *checkf(); 1401500Serics 1411500Serics nfiles = argc; 1421500Serics fnames = argv; 1431500Serics initterm (); 14415813Sralph nscroll = Lpp/2 - 1; 14515813Sralph if (nscroll <= 0) 14615813Sralph nscroll = 1; 1473455Sroot if(s = getenv("MORE")) argscan(s); 1481500Serics while (--nfiles > 0) { 1491500Serics if ((ch = (*++fnames)[0]) == '-') { 1503455Sroot argscan(*fnames+1); 1511500Serics } 1521500Serics else if (ch == '+') { 1531500Serics s = *fnames; 1541500Serics if (*++s == '/') { 1551500Serics srchopt++; 1561500Serics for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';) 1571500Serics *p++ = *s++; 1581500Serics *p = '\0'; 1591500Serics } 1601500Serics else { 1611500Serics initopt++; 1621500Serics for (initline = 0; *s != '\0'; s++) 1631500Serics if (isdigit (*s)) 1641500Serics initline = initline*10 + *s -'0'; 1651500Serics --initline; 1661500Serics } 1671500Serics } 1681500Serics else break; 1691500Serics } 1703594Sroot /* allow clreol only if Home and eraseln and EodClr strings are 1713455Sroot * defined, and in that case, make sure we are in noscroll mode 1723455Sroot */ 1733455Sroot if(clreol) 1743455Sroot { 17518608Sralph if((Home == NULL) || (*Home == '\0') || 17618608Sralph (eraseln == NULL) || (*eraseln == '\0') || 17718608Sralph (EodClr == NULL) || (*EodClr == '\0') ) 17818608Sralph clreol = 0; 1793455Sroot else noscroll = 1; 1803455Sroot } 1811500Serics if (dlines == 0) 1821500Serics dlines = Lpp - (noscroll ? 1 : 2); 1831500Serics left = dlines; 1841500Serics if (nfiles > 1) 1851500Serics prnames++; 1861500Serics if (!no_intty && nfiles == 0) { 18734104Sbostic char *rindex(); 18834104Sbostic 18934104Sbostic p = rindex(argv[0], '/'); 19034104Sbostic fputs("usage: ",stderr); 19134104Sbostic fputs(p ? p + 1 : argv[0],stderr); 1921500Serics fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr); 1931500Serics exit(1); 1941500Serics } 1951500Serics else 1961500Serics f = stdin; 1971500Serics if (!no_tty) { 1981500Serics signal(SIGQUIT, onquit); 1991500Serics signal(SIGINT, end_it); 20027006Sdonn signal(SIGWINCH, chgwinsz); 2011500Serics if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) { 2021500Serics signal(SIGTSTP, onsusp); 2031500Serics catch_susp++; 2041500Serics } 20516582Sleres stty (fileno(stderr), &otty); 2061500Serics } 2071500Serics if (no_intty) { 2081500Serics if (no_tty) 2091500Serics copy_file (stdin); 2101500Serics else { 2111500Serics if ((ch = Getc (f)) == '\f') 2123594Sroot doclear(); 2131500Serics else { 2141500Serics Ungetc (ch, f); 2153594Sroot if (noscroll && (ch != EOF)) { 2163594Sroot if (clreol) 2173594Sroot home (); 2183594Sroot else 2193594Sroot doclear (); 2203455Sroot } 2211500Serics } 2221500Serics if (srchopt) 2233455Sroot { 2241500Serics search (initbuf, stdin, 1); 2253594Sroot if (noscroll) 2263594Sroot left--; 2273455Sroot } 2281500Serics else if (initopt) 2291500Serics skiplns (initline, stdin); 2301500Serics screen (stdin, left); 2311500Serics } 2321500Serics no_intty = 0; 2331500Serics prnames++; 2341500Serics firstf = 0; 2351500Serics } 2361500Serics 2371500Serics while (fnum < nfiles) { 2381500Serics if ((f = checkf (fnames[fnum], &clearit)) != NULL) { 2391500Serics context.line = context.chrctr = 0; 2401500Serics Currline = 0; 2411500Serics if (firstf) setjmp (restore); 2421500Serics if (firstf) { 2431500Serics firstf = 0; 2441500Serics if (srchopt) 2453455Sroot { 2461500Serics search (initbuf, f, 1); 2473594Sroot if (noscroll) 2483594Sroot left--; 2493455Sroot } 2501500Serics else if (initopt) 2511500Serics skiplns (initline, f); 2521500Serics } 2531500Serics else if (fnum < nfiles && !no_tty) { 2541500Serics setjmp (restore); 2551500Serics left = command (fnames[fnum], f); 2561500Serics } 2571500Serics if (left != 0) { 25832736Sbostic if ((noscroll || clearit) && (file_size != LONG_MAX)) 2593594Sroot if (clreol) 2603594Sroot home (); 2613594Sroot else 2623594Sroot doclear (); 2631500Serics if (prnames) { 2641500Serics if (bad_so) 2651500Serics erase (0); 2663594Sroot if (clreol) 2673594Sroot cleareol (); 2681500Serics pr("::::::::::::::"); 2691500Serics if (promptlen > 14) 2701500Serics erase (14); 2713455Sroot printf ("\n"); 2723455Sroot if(clreol) cleareol(); 2733455Sroot printf("%s\n", fnames[fnum]); 2743455Sroot if(clreol) cleareol(); 27530931Sbostic printf("::::::::::::::\n"); 2761500Serics if (left > Lpp - 4) 2771500Serics left = Lpp - 4; 2781500Serics } 2791500Serics if (no_tty) 2801500Serics copy_file (f); 2811500Serics else { 2821500Serics within++; 2831500Serics screen(f, left); 2841500Serics within = 0; 2851500Serics } 2861500Serics } 2871500Serics setjmp (restore); 2881500Serics fflush(stdout); 2891500Serics fclose(f); 2901500Serics screen_start.line = screen_start.chrctr = 0L; 2911500Serics context.line = context.chrctr = 0L; 2921500Serics } 2931500Serics fnum++; 2941500Serics firstf = 0; 2951500Serics } 2961500Serics reset_tty (); 2971500Serics exit(0); 2981500Serics } 2991500Serics 3003455Sroot argscan(s) 3013455Sroot char *s; 3023455Sroot { 30332273Sbostic int seen_num = 0; 30432273Sbostic 30532273Sbostic while (*s != '\0') { 30632273Sbostic switch (*s) { 30711604Slayer case '0': case '1': case '2': 30811604Slayer case '3': case '4': case '5': 30911604Slayer case '6': case '7': case '8': 31011604Slayer case '9': 31132273Sbostic if (!seen_num) { 31232273Sbostic dlines = 0; 31332273Sbostic seen_num = 1; 31432273Sbostic } 31511604Slayer dlines = dlines*10 + *s - '0'; 31611604Slayer break; 31711604Slayer case 'd': 31811604Slayer dum_opt = 1; 31911604Slayer break; 32011604Slayer case 'l': 32111604Slayer stop_opt = 0; 32211604Slayer break; 32311604Slayer case 'f': 32411604Slayer fold_opt = 0; 32511604Slayer break; 32611604Slayer case 'p': 32711604Slayer noscroll++; 32811604Slayer break; 32911604Slayer case 'c': 33011604Slayer clreol++; 33111604Slayer break; 33211604Slayer case 's': 33311604Slayer ssp_opt = 1; 33411604Slayer break; 33511604Slayer case 'u': 33611604Slayer ul_opt = 0; 33711604Slayer break; 33811604Slayer } 33932273Sbostic s++; 34011604Slayer } 3413455Sroot } 3423455Sroot 3433455Sroot 3441500Serics /* 3451500Serics ** Check whether the file named by fs is an ASCII file which the user may 3461500Serics ** access. If it is, return the opened file. Otherwise return NULL. 3471500Serics */ 3481500Serics 3491500Serics FILE * 3501500Serics checkf (fs, clearfirst) 35132736Sbostic register char *fs; 35232736Sbostic int *clearfirst; 3531500Serics { 35432736Sbostic struct stat stbuf; 35532736Sbostic register FILE *f; 35632736Sbostic char c; 3571500Serics 35832736Sbostic if (stat (fs, &stbuf) == -1) { 35932736Sbostic (void)fflush(stdout); 36032736Sbostic if (clreol) 36132736Sbostic cleareol (); 36232736Sbostic perror(fs); 36332736Sbostic return((FILE *)NULL); 36432736Sbostic } 36532736Sbostic if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { 36632736Sbostic printf("\n*** %s: directory ***\n\n", fs); 36732736Sbostic return((FILE *)NULL); 36832736Sbostic } 36932736Sbostic if ((f = Fopen(fs, "r")) == NULL) { 37032736Sbostic (void)fflush(stdout); 37132736Sbostic perror(fs); 37232736Sbostic return((FILE *)NULL); 37332736Sbostic } 37432736Sbostic if (magic(f, fs)) 37532736Sbostic return((FILE *)NULL); 37632736Sbostic c = Getc(f); 37732736Sbostic *clearfirst = c == '\f'; 3781500Serics Ungetc (c, f); 37932736Sbostic if ((file_size = stbuf.st_size) == 0) 38032736Sbostic file_size = LONG_MAX; 38132736Sbostic return(f); 3821500Serics } 3831500Serics 3841500Serics /* 38532736Sbostic * magic -- 38632736Sbostic * check for file magic numbers. This code would best be shared with 38732736Sbostic * the file(1) program or, perhaps, more should not try and be so smart? 38829906Smckusick */ 38932736Sbostic static 39032736Sbostic magic(f, fs) 39132736Sbostic FILE *f; 39232736Sbostic char *fs; 39329906Smckusick { 39432736Sbostic struct exec ex; 39529906Smckusick 39632736Sbostic if (fread(&ex, sizeof(ex), 1, f) == 1) 39732736Sbostic switch(ex.a_magic) { 39832736Sbostic case OMAGIC: 39932736Sbostic case NMAGIC: 40032736Sbostic case ZMAGIC: 40132736Sbostic case 0405: 40232736Sbostic case 0411: 40332736Sbostic case 0177545: 40432736Sbostic printf("\n******** %s: Not a text file ********\n\n", fs); 40532736Sbostic (void)fclose(f); 40632736Sbostic return(1); 40732736Sbostic } 40832736Sbostic (void)fseek(f, 0L, L_SET); /* rewind() not necessary */ 40933087Sbostic return(0); 41029906Smckusick } 41129906Smckusick 41229906Smckusick /* 4131500Serics ** A real function, for the tputs routine in termlib 4141500Serics */ 4151500Serics 4161500Serics putch (ch) 4171500Serics char ch; 4181500Serics { 4191500Serics putchar (ch); 4201500Serics } 4211500Serics 4221500Serics /* 4231500Serics ** Print out the contents of the file f, one screenful at a time. 4241500Serics */ 4251500Serics 4261500Serics #define STOP -10 4271500Serics 4281500Serics screen (f, num_lines) 4291500Serics register FILE *f; 4301500Serics register int num_lines; 4311500Serics { 4321500Serics register int c; 4331500Serics register int nchars; 4343594Sroot int length; /* length of current line */ 4353594Sroot static int prev_len = 1; /* length of previous line */ 4361500Serics 4371500Serics for (;;) { 4381500Serics while (num_lines > 0 && !Pause) { 4391500Serics if ((nchars = getline (f, &length)) == EOF) 4403455Sroot { 4413594Sroot if (clreol) 4423594Sroot clreos(); 4431500Serics return; 4443455Sroot } 4453594Sroot if (ssp_opt && length == 0 && prev_len == 0) 4463594Sroot continue; 4473594Sroot prev_len = length; 4481500Serics if (bad_so || (Senter && *Senter == ' ') && promptlen > 0) 4491500Serics erase (0); 4503594Sroot /* must clear before drawing line since tabs on some terminals 4513594Sroot * do not erase what they tab over. 4523594Sroot */ 4533594Sroot if (clreol) 4543594Sroot cleareol (); 4551500Serics prbuf (Line, length); 4561500Serics if (nchars < promptlen) 4571500Serics erase (nchars); /* erase () sets promptlen to 0 */ 4581500Serics else promptlen = 0; 4593594Sroot /* is this needed? 4603594Sroot * if (clreol) 4613594Sroot * cleareol(); /* must clear again in case we wrapped * 4623594Sroot */ 4631500Serics if (nchars < Mcol || !fold_opt) 46416710Sjak prbuf("\n", 1); /* will turn off UL if necessary */ 4651500Serics if (nchars == STOP) 4661500Serics break; 4671500Serics num_lines--; 4681500Serics } 46916710Sjak if (pstate) { 47016710Sjak tputs(ULexit, 1, putch); 47116710Sjak pstate = 0; 47216710Sjak } 4731500Serics fflush(stdout); 4741500Serics if ((c = Getc(f)) == EOF) 4753455Sroot { 4763594Sroot if (clreol) 4773594Sroot clreos (); 4781500Serics return; 4793455Sroot } 4803455Sroot 4813594Sroot if (Pause && clreol) 4823594Sroot clreos (); 4831500Serics Ungetc (c, f); 4841500Serics setjmp (restore); 4851500Serics Pause = 0; startup = 0; 4861500Serics if ((num_lines = command (NULL, f)) == 0) 4871500Serics return; 4881500Serics if (hard && promptlen > 0) 4891500Serics erase (0); 49011123Slayer if (noscroll && num_lines >= dlines) 49116582Sleres { 4923594Sroot if (clreol) 4933594Sroot home(); 4943594Sroot else 4953594Sroot doclear (); 4963455Sroot } 4971500Serics screen_start.line = Currline; 4981500Serics screen_start.chrctr = Ftell (f); 4991500Serics } 5001500Serics } 5011500Serics 5021500Serics /* 5031500Serics ** Come here if a quit signal is received 5041500Serics */ 5051500Serics 5061500Serics onquit() 5071500Serics { 5081500Serics signal(SIGQUIT, SIG_IGN); 5091500Serics if (!inwait) { 5101500Serics putchar ('\n'); 5111500Serics if (!startup) { 5121500Serics signal(SIGQUIT, onquit); 5131500Serics longjmp (restore, 1); 5141500Serics } 5151500Serics else 5161500Serics Pause++; 5171500Serics } 5181500Serics else if (!dum_opt && notell) { 5191500Serics write (2, "[Use q or Q to quit]", 20); 5201500Serics promptlen += 20; 5211500Serics notell = 0; 5221500Serics } 5231500Serics signal(SIGQUIT, onquit); 5241500Serics } 5251500Serics 5261500Serics /* 52727006Sdonn ** Come here if a signal for a window size change is received 52827006Sdonn */ 52927006Sdonn 53027006Sdonn chgwinsz() 53127006Sdonn { 53227006Sdonn struct winsize win; 53327006Sdonn 53427006Sdonn (void) signal(SIGWINCH, SIG_IGN); 53527006Sdonn if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) { 53627006Sdonn if (win.ws_row != 0) { 53727006Sdonn Lpp = win.ws_row; 53827006Sdonn nscroll = Lpp/2 - 1; 53927006Sdonn if (nscroll <= 0) 54027006Sdonn nscroll = 1; 54127006Sdonn dlines = Lpp - (noscroll ? 1 : 2); 54227006Sdonn } 54327006Sdonn if (win.ws_col != 0) 54427006Sdonn Mcol = win.ws_col; 54527006Sdonn } 54627006Sdonn (void) signal(SIGWINCH, chgwinsz); 54727006Sdonn } 54827006Sdonn 54927006Sdonn /* 5501500Serics ** Clean up terminal state and exit. Also come here if interrupt signal received 5511500Serics */ 5521500Serics 5531500Serics end_it () 5541500Serics { 5551500Serics 5561500Serics reset_tty (); 5573594Sroot if (clreol) { 5583594Sroot putchar ('\r'); 5593594Sroot clreos (); 5603594Sroot fflush (stdout); 5613594Sroot } 5623455Sroot else if (!clreol && (promptlen > 0)) { 5631500Serics kill_line (); 5641500Serics fflush (stdout); 5651500Serics } 5661500Serics else 5671500Serics write (2, "\n", 1); 5681500Serics _exit(0); 5691500Serics } 5701500Serics 5711500Serics copy_file(f) 5721500Serics register FILE *f; 5731500Serics { 5741500Serics register int c; 5751500Serics 5761500Serics while ((c = getc(f)) != EOF) 5771500Serics putchar(c); 5781500Serics } 5791500Serics 5801500Serics /* Simplified printf function */ 5811500Serics 58232737Sbostic printf (fmt, va_alist) 5831500Serics register char *fmt; 58432737Sbostic va_dcl 5851500Serics { 58632737Sbostic va_list ap; 5871500Serics register char ch; 5881500Serics register int ccount; 5891500Serics 5901500Serics ccount = 0; 59132737Sbostic va_start(ap); 5921500Serics while (*fmt) { 5931500Serics while ((ch = *fmt++) != '%') { 5941500Serics if (ch == '\0') 5951500Serics return (ccount); 5961500Serics ccount++; 5971500Serics putchar (ch); 5981500Serics } 5991500Serics switch (*fmt++) { 6001500Serics case 'd': 60132737Sbostic ccount += printd (va_arg(ap, int)); 6021500Serics break; 6031500Serics case 's': 60432737Sbostic ccount += pr (va_arg(ap, char *)); 6051500Serics break; 6061500Serics case '%': 6071500Serics ccount++; 6081500Serics putchar ('%'); 6091500Serics break; 6101500Serics case '0': 6111500Serics return (ccount); 6121500Serics default: 6131500Serics break; 6141500Serics } 6151500Serics } 61632737Sbostic va_end(ap); 6171500Serics return (ccount); 6181500Serics 6191500Serics } 6201500Serics 6211500Serics /* 6221500Serics ** Print an integer as a string of decimal digits, 6231500Serics ** returning the length of the print representation. 6241500Serics */ 6251500Serics 6261500Serics printd (n) 6271500Serics int n; 6281500Serics { 6291500Serics int a, nchars; 6301500Serics 6311500Serics if (a = n/10) 6321500Serics nchars = 1 + printd(a); 6331500Serics else 6341500Serics nchars = 1; 6351500Serics putchar (n % 10 + '0'); 6361500Serics return (nchars); 6371500Serics } 6381500Serics 6391500Serics /* Put the print representation of an integer into a string */ 6401500Serics static char *sptr; 6411500Serics 6421500Serics scanstr (n, str) 6431500Serics int n; 6441500Serics char *str; 6451500Serics { 6461500Serics sptr = str; 64711604Slayer Sprintf (n); 6481500Serics *sptr = '\0'; 6491500Serics } 6501500Serics 65111604Slayer Sprintf (n) 6521500Serics { 6531500Serics int a; 6541500Serics 6551500Serics if (a = n/10) 65611604Slayer Sprintf (a); 6571500Serics *sptr++ = n % 10 + '0'; 6581500Serics } 6591500Serics 66033232Sbostic static char bell = ctrl('G'); 6611500Serics 6621500Serics strlen (s) 6631500Serics char *s; 6641500Serics { 6651500Serics register char *p; 6661500Serics 6671500Serics p = s; 6681500Serics while (*p++) 6691500Serics ; 6701500Serics return (p - s - 1); 6711500Serics } 6721500Serics 6731500Serics /* See whether the last component of the path name "path" is equal to the 6741500Serics ** string "string" 6751500Serics */ 6761500Serics 6771500Serics tailequ (path, string) 6781500Serics char *path; 6791500Serics register char *string; 6801500Serics { 6811500Serics register char *tail; 6821500Serics 6831500Serics tail = path + strlen(path); 6841500Serics while (tail >= path) 6851500Serics if (*(--tail) == '/') 6861500Serics break; 6871500Serics ++tail; 6881500Serics while (*tail++ == *string++) 6891500Serics if (*tail == '\0') 6901500Serics return(1); 6911500Serics return(0); 6921500Serics } 6931500Serics 6941500Serics prompt (filename) 6951500Serics char *filename; 6961500Serics { 6973594Sroot if (clreol) 6983594Sroot cleareol (); 6993455Sroot else if (promptlen > 0) 7001500Serics kill_line (); 7011500Serics if (!hard) { 7021500Serics promptlen = 8; 70316710Sjak if (Senter && Sexit) { 7041500Serics tputs (Senter, 1, putch); 70516710Sjak promptlen += (2 * soglitch); 70616710Sjak } 7073594Sroot if (clreol) 7083594Sroot cleareol (); 7091500Serics pr("--More--"); 7101500Serics if (filename != NULL) { 7111500Serics promptlen += printf ("(Next file: %s)", filename); 7121500Serics } 7131500Serics else if (!no_intty) { 7141500Serics promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size)); 7151500Serics } 7161500Serics if (dum_opt) { 71716710Sjak promptlen += pr("[Press space to continue, 'q' to quit.]"); 7181500Serics } 7191500Serics if (Senter && Sexit) 7201500Serics tputs (Sexit, 1, putch); 7213594Sroot if (clreol) 7223594Sroot clreos (); 7231500Serics fflush(stdout); 7241500Serics } 7251500Serics else 7261500Serics write (2, &bell, 1); 7271500Serics inwait++; 7281500Serics } 7291500Serics 7301500Serics /* 7311500Serics ** Get a logical line 7321500Serics */ 7331500Serics 7341500Serics getline(f, length) 7351500Serics register FILE *f; 7361500Serics int *length; 7371500Serics { 7381500Serics register int c; 7391500Serics register char *p; 7401500Serics register int column; 7411500Serics static int colflg; 7421500Serics 7431500Serics p = Line; 7441500Serics column = 0; 7451500Serics c = Getc (f); 7461500Serics if (colflg && c == '\n') { 7471500Serics Currline++; 7481500Serics c = Getc (f); 7491500Serics } 7501500Serics while (p < &Line[LINSIZ - 1]) { 7511500Serics if (c == EOF) { 7521500Serics if (p > Line) { 7531500Serics *p = '\0'; 7541500Serics *length = p - Line; 7551500Serics return (column); 7561500Serics } 7571500Serics *length = p - Line; 7581500Serics return (EOF); 7591500Serics } 7601500Serics if (c == '\n') { 7611500Serics Currline++; 7621500Serics break; 7631500Serics } 7641500Serics *p++ = c; 7651500Serics if (c == '\t') 76629907Smckusick if (!hardtabs || column < promptlen && !hard) { 76729907Smckusick if (hardtabs && eraseln && !dumb) { 7681500Serics column = 1 + (column | 7); 7691500Serics tputs (eraseln, 1, putch); 7701500Serics promptlen = 0; 7711500Serics } 7721500Serics else { 77329907Smckusick for (--p; p < &Line[LINSIZ - 1];) { 7741500Serics *p++ = ' '; 77529907Smckusick if ((++column & 7) == 0) 77629907Smckusick break; 7771500Serics } 7781500Serics if (column >= promptlen) promptlen = 0; 7791500Serics } 7801500Serics } 7811500Serics else 7821500Serics column = 1 + (column | 7); 7839627Ssklower else if (c == '\b' && column > 0) 7841500Serics column--; 7851500Serics else if (c == '\r') 7861500Serics column = 0; 7871500Serics else if (c == '\f' && stop_opt) { 7881500Serics p[-1] = '^'; 7891500Serics *p++ = 'L'; 7901500Serics column += 2; 7911500Serics Pause++; 7921500Serics } 7931500Serics else if (c == EOF) { 7941500Serics *length = p - Line; 7951500Serics return (column); 7961500Serics } 7971500Serics else if (c >= ' ' && c != RUBOUT) 7981500Serics column++; 7991500Serics if (column >= Mcol && fold_opt) break; 8001500Serics c = Getc (f); 8011500Serics } 8021500Serics if (column >= Mcol && Mcol > 0) { 8031500Serics if (!Wrap) { 8041500Serics *p++ = '\n'; 8051500Serics } 8061500Serics } 8071500Serics colflg = column == Mcol && fold_opt; 80829907Smckusick if (colflg && eatnl && Wrap) { 80929907Smckusick *p++ = '\n'; /* simulate normal wrap */ 81029907Smckusick } 8111500Serics *length = p - Line; 8121500Serics *p = 0; 8131500Serics return (column); 8141500Serics } 8151500Serics 8161500Serics /* 8171500Serics ** Erase the rest of the prompt, assuming we are starting at column col. 8181500Serics */ 8191500Serics 8201500Serics erase (col) 8211500Serics register int col; 8221500Serics { 8231500Serics 8241500Serics if (promptlen == 0) 8251500Serics return; 8261500Serics if (hard) { 8271500Serics putchar ('\n'); 8281500Serics } 8291500Serics else { 8301500Serics if (col == 0) 8311500Serics putchar ('\r'); 8321500Serics if (!dumb && eraseln) 8331500Serics tputs (eraseln, 1, putch); 8341500Serics else 8351500Serics for (col = promptlen - col; col > 0; col--) 8361500Serics putchar (' '); 8371500Serics } 8381500Serics promptlen = 0; 8391500Serics } 8401500Serics 8411500Serics /* 8421500Serics ** Erase the current line entirely 8431500Serics */ 8441500Serics 8451500Serics kill_line () 8461500Serics { 8471500Serics erase (0); 8481500Serics if (!eraseln || dumb) putchar ('\r'); 8491500Serics } 8501500Serics 8511500Serics /* 8523455Sroot * force clear to end of line 8533455Sroot */ 8543455Sroot cleareol() 8553455Sroot { 8563594Sroot tputs(eraseln, 1, putch); 8573455Sroot } 8583455Sroot 8593594Sroot clreos() 8603455Sroot { 8613594Sroot tputs(EodClr, 1, putch); 8623455Sroot } 8633455Sroot 8643455Sroot /* 8651500Serics ** Print string and return number of characters 8661500Serics */ 8671500Serics 8681500Serics pr(s1) 8691500Serics char *s1; 8701500Serics { 8711500Serics register char *s; 8721500Serics register char c; 8731500Serics 8741500Serics for (s = s1; c = *s++; ) 8751500Serics putchar(c); 8761500Serics return (s - s1 - 1); 8771500Serics } 8781500Serics 8791500Serics 8801500Serics /* Print a buffer of n characters */ 8811500Serics 8821500Serics prbuf (s, n) 8831500Serics register char *s; 8841500Serics register int n; 8851500Serics { 88616710Sjak register char c; /* next output character */ 8873594Sroot register int state; /* next output char's UL state */ 88816710Sjak #define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_'))) 8893594Sroot 8903594Sroot while (--n >= 0) 8913594Sroot if (!ul_opt) 8923594Sroot putchar (*s++); 8933594Sroot else { 89416710Sjak if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) { 89516710Sjak s++; 89616710Sjak continue; 89716710Sjak } 89816710Sjak if (state = wouldul(s, n)) { 89916710Sjak c = (*s == '_')? s[2] : *s ; 9003594Sroot n -= 2; 90116710Sjak s += 3; 90216710Sjak } else 9033594Sroot c = *s++; 90416710Sjak if (state != pstate) { 90516710Sjak if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1)) 90616710Sjak state = 1; 90716710Sjak else 90816710Sjak tputs(state ? ULenter : ULexit, 1, putch); 9093594Sroot } 91016710Sjak if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0) 91116710Sjak putchar(c); 9123594Sroot if (state && *chUL) { 9133594Sroot pr(chBS); 9143594Sroot tputs(chUL, 1, putch); 9153594Sroot } 91616710Sjak pstate = state; 9173594Sroot } 9181500Serics } 9191500Serics 9201500Serics /* 9211500Serics ** Clear the screen 9221500Serics */ 9231500Serics 9241500Serics doclear() 9251500Serics { 9261500Serics if (Clear && !hard) { 9271500Serics tputs(Clear, 1, putch); 9281500Serics 9291500Serics /* Put out carriage return so that system doesn't 9301500Serics ** get confused by escape sequences when expanding tabs 9311500Serics */ 9321500Serics putchar ('\r'); 9331500Serics promptlen = 0; 9341500Serics } 9351500Serics } 9361500Serics 9373455Sroot /* 9383455Sroot * Go to home position 9393455Sroot */ 9403455Sroot home() 9413455Sroot { 9423455Sroot tputs(Home,1,putch); 9433455Sroot } 9443455Sroot 9451500Serics static int lastcmd, lastarg, lastp; 9461500Serics static int lastcolon; 9471500Serics char shell_line[132]; 9481500Serics 9491500Serics /* 9501500Serics ** Read a command and do it. A command consists of an optional integer 9511500Serics ** argument followed by the command character. Return the number of lines 9521500Serics ** to display in the next screenful. If there is nothing more to display 9531500Serics ** in the current file, zero is returned. 9541500Serics */ 9551500Serics 9561500Serics command (filename, f) 9571500Serics char *filename; 9581500Serics register FILE *f; 9591500Serics { 9601500Serics register int nlines; 9611500Serics register int retval; 9621500Serics register char c; 9631500Serics char colonch; 9641500Serics FILE *helpf; 9651500Serics int done; 9661500Serics char comchar, cmdbuf[80], *p; 9671500Serics 9681500Serics #define ret(val) retval=val;done++;break 9691500Serics 9701500Serics done = 0; 9711500Serics if (!errors) 9721500Serics prompt (filename); 9731500Serics else 9741500Serics errors = 0; 9751500Serics if (MBIT == RAW && slow_tty) { 9761500Serics otty.sg_flags |= MBIT; 97716582Sleres stty(fileno(stderr), &otty); 9781500Serics } 9791500Serics for (;;) { 9801500Serics nlines = number (&comchar); 9811500Serics lastp = colonch = 0; 9821500Serics if (comchar == '.') { /* Repeat last command */ 9831500Serics lastp++; 9841500Serics comchar = lastcmd; 9851500Serics nlines = lastarg; 9861500Serics if (lastcmd == ':') 9871500Serics colonch = lastcolon; 9881500Serics } 9891500Serics lastcmd = comchar; 9901500Serics lastarg = nlines; 9911500Serics if (comchar == otty.sg_erase) { 9921500Serics kill_line (); 9931500Serics prompt (filename); 9941500Serics continue; 9951500Serics } 9961500Serics switch (comchar) { 9971500Serics case ':': 9981500Serics retval = colon (filename, colonch, nlines); 9991500Serics if (retval >= 0) 10001500Serics done++; 10011500Serics break; 100224490Sbloom case 'b': 100333232Sbostic case ctrl('B'): 100424490Sbloom { 100524490Sbloom register int initline; 100624490Sbloom 100724490Sbloom if (no_intty) { 100824490Sbloom write(2, &bell, 1); 100924490Sbloom return (-1); 101024490Sbloom } 101124490Sbloom 101224490Sbloom if (nlines == 0) nlines++; 101324490Sbloom 101424490Sbloom putchar ('\r'); 101524490Sbloom erase (0); 101624490Sbloom printf ("\n"); 101724490Sbloom if (clreol) 101824490Sbloom cleareol (); 101924490Sbloom printf ("...back %d page", nlines); 102024490Sbloom if (nlines > 1) 102124490Sbloom pr ("s\n"); 102224490Sbloom else 102324490Sbloom pr ("\n"); 102424490Sbloom 102524490Sbloom if (clreol) 102624490Sbloom cleareol (); 102724490Sbloom pr ("\n"); 102824490Sbloom 102924490Sbloom initline = Currline - dlines * (nlines + 1); 103024490Sbloom if (! noscroll) 103124490Sbloom --initline; 103224490Sbloom if (initline < 0) initline = 0; 103324490Sbloom Fseek(f, 0L); 103424490Sbloom Currline = 0; /* skiplns() will make Currline correct */ 103524490Sbloom skiplns(initline, f); 103624490Sbloom if (! noscroll) { 103724490Sbloom ret(dlines + 1); 103824490Sbloom } 103924490Sbloom else { 104024490Sbloom ret(dlines); 104124490Sbloom } 104224490Sbloom } 10431500Serics case ' ': 10441500Serics case 'z': 10451500Serics if (nlines == 0) nlines = dlines; 10461500Serics else if (comchar == 'z') dlines = nlines; 10471500Serics ret (nlines); 10481500Serics case 'd': 104933232Sbostic case ctrl('D'): 10501500Serics if (nlines != 0) nscroll = nlines; 10511500Serics ret (nscroll); 10521500Serics case 'q': 10531500Serics case 'Q': 10541500Serics end_it (); 10551500Serics case 's': 10561500Serics case 'f': 10571500Serics if (nlines == 0) nlines++; 10581500Serics if (comchar == 'f') 10591500Serics nlines *= dlines; 10601500Serics putchar ('\r'); 10611500Serics erase (0); 10623594Sroot printf ("\n"); 10633594Sroot if (clreol) 10643594Sroot cleareol (); 10653594Sroot printf ("...skipping %d line", nlines); 10661500Serics if (nlines > 1) 10673594Sroot pr ("s\n"); 10681500Serics else 10693594Sroot pr ("\n"); 10703594Sroot 10713594Sroot if (clreol) 10723594Sroot cleareol (); 10733594Sroot pr ("\n"); 10743594Sroot 10751500Serics while (nlines > 0) { 10761500Serics while ((c = Getc (f)) != '\n') 10771500Serics if (c == EOF) { 10781500Serics retval = 0; 10791500Serics done++; 10801500Serics goto endsw; 10811500Serics } 10821500Serics Currline++; 10831500Serics nlines--; 10841500Serics } 10851500Serics ret (dlines); 10861500Serics case '\n': 10871500Serics if (nlines != 0) 10881500Serics dlines = nlines; 10891500Serics else 10901500Serics nlines = 1; 10911500Serics ret (nlines); 10921500Serics case '\f': 10931500Serics if (!no_intty) { 10941500Serics doclear (); 10951500Serics Fseek (f, screen_start.chrctr); 10961500Serics Currline = screen_start.line; 10971500Serics ret (dlines); 10981500Serics } 10991500Serics else { 11001500Serics write (2, &bell, 1); 11011500Serics break; 11021500Serics } 11031500Serics case '\'': 11041500Serics if (!no_intty) { 11051500Serics kill_line (); 11061500Serics pr ("\n***Back***\n\n"); 11071500Serics Fseek (f, context.chrctr); 11081500Serics Currline = context.line; 11091500Serics ret (dlines); 11101500Serics } 11111500Serics else { 11121500Serics write (2, &bell, 1); 11131500Serics break; 11141500Serics } 11151500Serics case '=': 11161500Serics kill_line (); 11171500Serics promptlen = printd (Currline); 11181500Serics fflush (stdout); 11191500Serics break; 11201500Serics case 'n': 11211500Serics lastp++; 11221500Serics case '/': 11231500Serics if (nlines == 0) nlines++; 11241500Serics kill_line (); 11251500Serics pr ("/"); 11261500Serics promptlen = 1; 11271500Serics fflush (stdout); 11281500Serics if (lastp) { 11291500Serics write (2,"\r", 1); 11301500Serics search (NULL, f, nlines); /* Use previous r.e. */ 11311500Serics } 11321500Serics else { 11331500Serics ttyin (cmdbuf, 78, '/'); 11341500Serics write (2, "\r", 1); 11351500Serics search (cmdbuf, f, nlines); 11361500Serics } 11373455Sroot ret (dlines-1); 11381500Serics case '!': 11391500Serics do_shell (filename); 11401500Serics break; 114124490Sbloom case '?': 11421500Serics case 'h': 11431500Serics if ((helpf = fopen (HELPFILE, "r")) == NULL) 11441500Serics error ("Can't open help file"); 11451500Serics if (noscroll) doclear (); 11461500Serics copy_file (helpf); 114727006Sdonn fclose (helpf); 11481500Serics prompt (filename); 11491500Serics break; 11501500Serics case 'v': /* This case should go right before default */ 11511500Serics if (!no_intty) { 11521500Serics kill_line (); 11531500Serics cmdbuf[0] = '+'; 115424490Sbloom scanstr (Currline - dlines < 0 ? 0 115524490Sbloom : Currline - (dlines + 1) / 2, &cmdbuf[1]); 11561500Serics pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]); 11571500Serics execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0); 11581500Serics break; 11591500Serics } 11601500Serics default: 116116710Sjak if (dum_opt) { 116216710Sjak kill_line (); 116316710Sjak if (Senter && Sexit) { 116416710Sjak tputs (Senter, 1, putch); 116516710Sjak promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch); 116616710Sjak tputs (Sexit, 1, putch); 116716710Sjak } 116816710Sjak else 116916710Sjak promptlen = pr ("[Press 'h' for instructions.]"); 117016710Sjak fflush (stdout); 117116710Sjak } 117216710Sjak else 117316710Sjak write (2, &bell, 1); 11741500Serics break; 11751500Serics } 11761500Serics if (done) break; 11771500Serics } 11781500Serics putchar ('\r'); 11791500Serics endsw: 11801500Serics inwait = 0; 11811500Serics notell++; 11821500Serics if (MBIT == RAW && slow_tty) { 11831500Serics otty.sg_flags &= ~MBIT; 118416582Sleres stty(fileno(stderr), &otty); 11851500Serics } 11861500Serics return (retval); 11871500Serics } 11881500Serics 11891500Serics char ch; 11901500Serics 11911500Serics /* 11921500Serics * Execute a colon-prefixed command. 11931500Serics * Returns <0 if not a command that should cause 11941500Serics * more of the file to be printed. 11951500Serics */ 11961500Serics 11971500Serics colon (filename, cmd, nlines) 11981500Serics char *filename; 11991500Serics int cmd; 12001500Serics int nlines; 12011500Serics { 12021500Serics if (cmd == 0) 12031500Serics ch = readch (); 12041500Serics else 12051500Serics ch = cmd; 12061500Serics lastcolon = ch; 12071500Serics switch (ch) { 12081500Serics case 'f': 12091500Serics kill_line (); 12101500Serics if (!no_intty) 12111500Serics promptlen = printf ("\"%s\" line %d", fnames[fnum], Currline); 12121500Serics else 12131500Serics promptlen = printf ("[Not a file] line %d", Currline); 12141500Serics fflush (stdout); 12151500Serics return (-1); 12161500Serics case 'n': 12171500Serics if (nlines == 0) { 12181500Serics if (fnum >= nfiles - 1) 12191500Serics end_it (); 12201500Serics nlines++; 12211500Serics } 12221500Serics putchar ('\r'); 12231500Serics erase (0); 12241500Serics skipf (nlines); 12251500Serics return (0); 12261500Serics case 'p': 12271500Serics if (no_intty) { 12281500Serics write (2, &bell, 1); 12291500Serics return (-1); 12301500Serics } 12311500Serics putchar ('\r'); 12321500Serics erase (0); 12331500Serics if (nlines == 0) 12341500Serics nlines++; 12351500Serics skipf (-nlines); 12361500Serics return (0); 12371500Serics case '!': 12381500Serics do_shell (filename); 12391500Serics return (-1); 12401500Serics case 'q': 12411500Serics case 'Q': 12421500Serics end_it (); 12431500Serics default: 12441500Serics write (2, &bell, 1); 12451500Serics return (-1); 12461500Serics } 12471500Serics } 12481500Serics 12491500Serics /* 12501500Serics ** Read a decimal number from the terminal. Set cmd to the non-digit which 12511500Serics ** terminates the number. 12521500Serics */ 12531500Serics 12541500Serics number(cmd) 12551500Serics char *cmd; 12561500Serics { 12571500Serics register int i; 12581500Serics 12591500Serics i = 0; ch = otty.sg_kill; 12601500Serics for (;;) { 12611500Serics ch = readch (); 12621500Serics if (ch >= '0' && ch <= '9') 12631500Serics i = i*10 + ch - '0'; 12641500Serics else if (ch == otty.sg_kill) 12651500Serics i = 0; 12661500Serics else { 12671500Serics *cmd = ch; 12681500Serics break; 12691500Serics } 12701500Serics } 12711500Serics return (i); 12721500Serics } 12731500Serics 12741500Serics do_shell (filename) 12751500Serics char *filename; 12761500Serics { 12771500Serics char cmdbuf[80]; 12781500Serics 12791500Serics kill_line (); 12801500Serics pr ("!"); 12811500Serics fflush (stdout); 12821500Serics promptlen = 1; 12831500Serics if (lastp) 12841500Serics pr (shell_line); 12851500Serics else { 12861500Serics ttyin (cmdbuf, 78, '!'); 12871500Serics if (expand (shell_line, cmdbuf)) { 12881500Serics kill_line (); 12891500Serics promptlen = printf ("!%s", shell_line); 12901500Serics } 12911500Serics } 12921500Serics fflush (stdout); 12931500Serics write (2, "\n", 1); 12941500Serics promptlen = 0; 12951500Serics shellp = 1; 12961500Serics execute (filename, shell, shell, "-c", shell_line, 0); 12971500Serics } 12981500Serics 12991500Serics /* 13001500Serics ** Search for nth ocurrence of regular expression contained in buf in the file 13011500Serics */ 13021500Serics 13031500Serics search (buf, file, n) 13041500Serics char buf[]; 13051500Serics FILE *file; 13061500Serics register int n; 13071500Serics { 13081500Serics long startline = Ftell (file); 13091500Serics register long line1 = startline; 13101500Serics register long line2 = startline; 13111500Serics register long line3 = startline; 13121500Serics register int lncount; 13131500Serics int saveln, rv, re_exec(); 13141500Serics char *s, *re_comp(); 13151500Serics 13161500Serics context.line = saveln = Currline; 13171500Serics context.chrctr = startline; 13181500Serics lncount = 0; 13191500Serics if ((s = re_comp (buf)) != 0) 13201500Serics error (s); 13211500Serics while (!feof (file)) { 13221500Serics line3 = line2; 13231500Serics line2 = line1; 13241500Serics line1 = Ftell (file); 13251500Serics rdline (file); 13261500Serics lncount++; 13271500Serics if ((rv = re_exec (Line)) == 1) 13281500Serics if (--n == 0) { 13291500Serics if (lncount > 3 || (lncount > 1 && no_intty)) 13303455Sroot { 13313455Sroot pr ("\n"); 13323594Sroot if (clreol) 13333594Sroot cleareol (); 13343455Sroot pr("...skipping\n"); 13353455Sroot } 13361500Serics if (!no_intty) { 13371500Serics Currline -= (lncount >= 3 ? 3 : lncount); 13381500Serics Fseek (file, line3); 13393594Sroot if (noscroll) 13403594Sroot if (clreol) { 13413594Sroot home (); 13423594Sroot cleareol (); 134316582Sleres } 13443594Sroot else 13453594Sroot doclear (); 13461500Serics } 13471500Serics else { 13481500Serics kill_line (); 13493594Sroot if (noscroll) 13503594Sroot if (clreol) { 135116582Sleres home (); 13523594Sroot cleareol (); 135316582Sleres } 13543594Sroot else 13553594Sroot doclear (); 13561500Serics pr (Line); 13571500Serics putchar ('\n'); 13581500Serics } 13591500Serics break; 13601500Serics } 13611500Serics else if (rv == -1) 13621500Serics error ("Regular expression botch"); 13631500Serics } 13641500Serics if (feof (file)) { 13651500Serics if (!no_intty) { 13661500Serics file->_flag &= ~_IOEOF; /* why doesn't fseek do this ??!!??! */ 13671500Serics Currline = saveln; 13681500Serics Fseek (file, startline); 13691500Serics } 13701500Serics else { 13711500Serics pr ("\nPattern not found\n"); 13721500Serics end_it (); 13731500Serics } 13741500Serics error ("Pattern not found"); 13751500Serics } 13761500Serics } 13771500Serics 137832737Sbostic /*VARARGS2*/ 137932737Sbostic execute (filename, cmd, va_alist) 13801500Serics char *filename; 138132737Sbostic char *cmd; 138232737Sbostic va_dcl 13831500Serics { 13841500Serics int id; 138516710Sjak int n; 138632737Sbostic va_list argp; 13871500Serics 13881500Serics fflush (stdout); 13891500Serics reset_tty (); 139016710Sjak for (n = 10; (id = fork ()) < 0 && n > 0; n--) 13911500Serics sleep (5); 13921500Serics if (id == 0) { 139316710Sjak if (!isatty(0)) { 139416710Sjak close(0); 139516710Sjak open("/dev/tty", 0); 139616710Sjak } 139732737Sbostic va_start(argp); 139832737Sbostic execv (cmd, argp); 13991500Serics write (2, "exec failed\n", 12); 14001500Serics exit (1); 140133087Sbostic va_end(argp); /* balance {}'s for some UNIX's */ 14021500Serics } 140316710Sjak if (id > 0) { 140416710Sjak signal (SIGINT, SIG_IGN); 140516710Sjak signal (SIGQUIT, SIG_IGN); 140616710Sjak if (catch_susp) 140716710Sjak signal(SIGTSTP, SIG_DFL); 140816710Sjak while (wait(0) > 0); 140916710Sjak signal (SIGINT, end_it); 141016710Sjak signal (SIGQUIT, onquit); 141116710Sjak if (catch_susp) 141216710Sjak signal(SIGTSTP, onsusp); 141316710Sjak } else 141416710Sjak write(2, "can't fork\n", 11); 14151500Serics set_tty (); 14161500Serics pr ("------------------------\n"); 14171500Serics prompt (filename); 14181500Serics } 14191500Serics /* 14201500Serics ** Skip n lines in the file f 14211500Serics */ 14221500Serics 14231500Serics skiplns (n, f) 14241500Serics register int n; 14251500Serics register FILE *f; 14261500Serics { 14271500Serics register char c; 14281500Serics 14291500Serics while (n > 0) { 14301500Serics while ((c = Getc (f)) != '\n') 14311500Serics if (c == EOF) 14321500Serics return; 14331500Serics n--; 14341500Serics Currline++; 14351500Serics } 14361500Serics } 14371500Serics 14381500Serics /* 14391500Serics ** Skip nskip files in the file list (from the command line). Nskip may be 14401500Serics ** negative. 14411500Serics */ 14421500Serics 14431500Serics skipf (nskip) 14441500Serics register int nskip; 14451500Serics { 14461500Serics if (nskip == 0) return; 14471500Serics if (nskip > 0) { 14481500Serics if (fnum + nskip > nfiles - 1) 14491500Serics nskip = nfiles - fnum - 1; 14501500Serics } 14511500Serics else if (within) 14521500Serics ++fnum; 14531500Serics fnum += nskip; 14541500Serics if (fnum < 0) 14551500Serics fnum = 0; 14563594Sroot pr ("\n...Skipping "); 14573455Sroot pr ("\n"); 14583594Sroot if (clreol) 14593594Sroot cleareol (); 14603455Sroot pr ("...Skipping "); 14611500Serics pr (nskip > 0 ? "to file " : "back to file "); 14621500Serics pr (fnames[fnum]); 14633455Sroot pr ("\n"); 14643594Sroot if (clreol) 14653594Sroot cleareol (); 14663455Sroot pr ("\n"); 14671500Serics --fnum; 14681500Serics } 14691500Serics 14701500Serics /*----------------------------- Terminal I/O -------------------------------*/ 14711500Serics 14721500Serics initterm () 14731500Serics { 14741500Serics char buf[TBUFSIZ]; 147517195Sralph static char clearbuf[TBUFSIZ]; 14761500Serics char *clearptr, *padstr; 14771500Serics int ldisc; 147817592Sleres int lmode; 147910823Ssam char *term; 148016582Sleres int tgrp; 148118030Sbloom struct winsize win; 14821500Serics 148316582Sleres retry: 148416582Sleres if (!(no_tty = gtty(fileno(stdout), &otty))) { 148517592Sleres if (ioctl(fileno(stdout), TIOCLGET, &lmode) < 0) { 148617592Sleres perror("TIOCLGET"); 148717592Sleres exit(1); 148817592Sleres } 148917592Sleres docrterase = ((lmode & LCRTERA) != 0); 149017592Sleres docrtkill = ((lmode & LCRTKIL) != 0); 149116582Sleres /* 149217592Sleres * Wait until we're in the foreground before we save the 149317592Sleres * the terminal modes. 149416582Sleres */ 149516582Sleres if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) { 149617592Sleres perror("TIOCGPGRP"); 149716582Sleres exit(1); 149816582Sleres } 149916582Sleres if (tgrp != getpgrp(0)) { 150016582Sleres kill(0, SIGTTOU); 150116582Sleres goto retry; 150216582Sleres } 150313830Skre if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) { 15043594Sroot dumb++; ul_opt = 0; 15051500Serics } 15061500Serics else { 150718030Sbloom if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) { 150818030Sbloom Lpp = tgetnum("li"); 150918030Sbloom Mcol = tgetnum("co"); 151018030Sbloom } else { 151118030Sbloom if ((Lpp = win.ws_row) == 0) 151218030Sbloom Lpp = tgetnum("li"); 151318030Sbloom if ((Mcol = win.ws_col) == 0) 151418030Sbloom Mcol = tgetnum("co"); 151518030Sbloom } 151618030Sbloom if ((Lpp <= 0) || tgetflag("hc")) { 15171500Serics hard++; /* Hard copy terminal */ 15181500Serics Lpp = 24; 15191500Serics } 152029907Smckusick if (tgetflag("xn")) 152129907Smckusick eatnl++; /* Eat newline at last column + 1; dec, concept */ 152218030Sbloom if (Mcol <= 0) 152318030Sbloom Mcol = 80; 152418030Sbloom 15251500Serics if (tailequ (fnames[0], "page") || !hard && tgetflag("ns")) 15261500Serics noscroll++; 15271500Serics Wrap = tgetflag("am"); 15281500Serics bad_so = tgetflag ("xs"); 15291500Serics clearptr = clearbuf; 15301500Serics eraseln = tgetstr("ce",&clearptr); 15311500Serics Clear = tgetstr("cl", &clearptr); 15321500Serics Senter = tgetstr("so", &clearptr); 15331500Serics Sexit = tgetstr("se", &clearptr); 153416710Sjak if ((soglitch = tgetnum("sg")) < 0) 153516710Sjak soglitch = 0; 15363594Sroot 15373594Sroot /* 15383594Sroot * Set up for underlining: some terminals don't need it; 15393594Sroot * others have start/stop sequences, still others have an 15403594Sroot * underline char sequence which is assumed to move the 15413594Sroot * cursor forward one character. If underline sequence 15423594Sroot * isn't available, settle for standout sequence. 15433594Sroot */ 15443594Sroot 15453594Sroot if (tgetflag("ul") || tgetflag("os")) 15463594Sroot ul_opt = 0; 15473594Sroot if ((chUL = tgetstr("uc", &clearptr)) == NULL ) 15483594Sroot chUL = ""; 154916710Sjak if (((ULenter = tgetstr("us", &clearptr)) == NULL || 155016710Sjak (ULexit = tgetstr("ue", &clearptr)) == NULL) && !*chUL) { 155116710Sjak if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) { 155216710Sjak ULenter = ""; 155316710Sjak ULexit = ""; 155416710Sjak } else 155516710Sjak ulglitch = soglitch; 155616710Sjak } else { 155716710Sjak if ((ulglitch = tgetnum("ug")) < 0) 155816710Sjak ulglitch = 0; 155916710Sjak } 156016582Sleres 15611500Serics if (padstr = tgetstr("pc", &clearptr)) 15621500Serics PC = *padstr; 15633455Sroot Home = tgetstr("ho",&clearptr); 156413536Ssam if (Home == 0 || *Home == '\0') 15653455Sroot { 15663594Sroot if ((cursorm = tgetstr("cm", &clearptr)) != NULL) { 15673594Sroot strcpy(cursorhome, tgoto(cursorm, 0, 0)); 15683455Sroot Home = cursorhome; 15693455Sroot } 15703455Sroot } 15713594Sroot EodClr = tgetstr("cd", &clearptr); 157225540Smckusick if ((chBS = tgetstr("bc", &clearptr)) == NULL) 157325540Smckusick chBS = "\b"; 157425540Smckusick 15751500Serics } 15761500Serics if ((shell = getenv("SHELL")) == NULL) 15771500Serics shell = "/bin/sh"; 15781500Serics } 157916582Sleres no_intty = gtty(fileno(stdin), &otty); 158016582Sleres gtty(fileno(stderr), &otty); 158113830Skre savetty = otty; 15821500Serics ospeed = otty.sg_ospeed; 15831500Serics slow_tty = ospeed < B1200; 158427006Sdonn hardtabs = (otty.sg_flags & TBDELAY) != XTABS; 15851500Serics if (!no_tty) { 15861500Serics otty.sg_flags &= ~ECHO; 15871500Serics if (MBIT == CBREAK || !slow_tty) 15881500Serics otty.sg_flags |= MBIT; 15891500Serics } 15901500Serics } 15911500Serics 15921500Serics readch () 15931500Serics { 15941500Serics char ch; 15951500Serics extern int errno; 15961500Serics 159731089Skarels errno = 0; 15981500Serics if (read (2, &ch, 1) <= 0) 15991500Serics if (errno != EINTR) 160031089Skarels end_it(); 16011500Serics else 16021500Serics ch = otty.sg_kill; 16031500Serics return (ch); 16041500Serics } 16051500Serics 16061500Serics static char BS = '\b'; 160717592Sleres static char *BSB = "\b \b"; 16081500Serics static char CARAT = '^'; 160917592Sleres #define ERASEONECHAR \ 161017592Sleres if (docrterase) \ 161117592Sleres write (2, BSB, sizeof(BSB)); \ 161217592Sleres else \ 161317592Sleres write (2, &BS, sizeof(BS)); 16141500Serics 16151500Serics ttyin (buf, nmax, pchar) 16161500Serics char buf[]; 16171500Serics register int nmax; 16181500Serics char pchar; 16191500Serics { 16201500Serics register char *sptr; 16211500Serics register char ch; 16221500Serics register int slash = 0; 16231500Serics int maxlen; 16241500Serics char cbuf; 16251500Serics 16261500Serics sptr = buf; 16271500Serics maxlen = 0; 16281500Serics while (sptr - buf < nmax) { 16291500Serics if (promptlen > maxlen) maxlen = promptlen; 16301500Serics ch = readch (); 16311500Serics if (ch == '\\') { 16321500Serics slash++; 16331500Serics } 16341500Serics else if ((ch == otty.sg_erase) && !slash) { 16351500Serics if (sptr > buf) { 16361500Serics --promptlen; 163717592Sleres ERASEONECHAR 16381500Serics --sptr; 16391500Serics if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) { 16401500Serics --promptlen; 164117592Sleres ERASEONECHAR 16421500Serics } 16431500Serics continue; 16441500Serics } 16451500Serics else { 16461500Serics if (!eraseln) promptlen = maxlen; 16471500Serics longjmp (restore, 1); 16481500Serics } 16491500Serics } 16501500Serics else if ((ch == otty.sg_kill) && !slash) { 16511500Serics if (hard) { 16521500Serics show (ch); 16531500Serics putchar ('\n'); 16541500Serics putchar (pchar); 16551500Serics } 16561500Serics else { 16571500Serics putchar ('\r'); 16581500Serics putchar (pchar); 16591500Serics if (eraseln) 16601500Serics erase (1); 166117592Sleres else if (docrtkill) 166217592Sleres while (promptlen-- > 1) 166317592Sleres write (2, BSB, sizeof(BSB)); 16641500Serics promptlen = 1; 16651500Serics } 16661500Serics sptr = buf; 16671500Serics fflush (stdout); 16681500Serics continue; 16691500Serics } 16701500Serics if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) { 167117592Sleres ERASEONECHAR 16721500Serics --sptr; 16731500Serics } 16741500Serics if (ch != '\\') 16751500Serics slash = 0; 16761500Serics *sptr++ = ch; 16771500Serics if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { 16781500Serics ch += ch == RUBOUT ? -0100 : 0100; 16791500Serics write (2, &CARAT, 1); 16801500Serics promptlen++; 16811500Serics } 16821500Serics cbuf = ch; 16831500Serics if (ch != '\n' && ch != ESC) { 16841500Serics write (2, &cbuf, 1); 16851500Serics promptlen++; 16861500Serics } 16871500Serics else 16881500Serics break; 16891500Serics } 16901500Serics *--sptr = '\0'; 16911500Serics if (!eraseln) promptlen = maxlen; 16921500Serics if (sptr - buf >= nmax - 1) 16931500Serics error ("Line too long"); 16941500Serics } 16951500Serics 16961500Serics expand (outbuf, inbuf) 16971500Serics char *outbuf; 16981500Serics char *inbuf; 16991500Serics { 17001500Serics register char *instr; 17011500Serics register char *outstr; 17021500Serics register char ch; 17031500Serics char temp[200]; 17041500Serics int changed = 0; 17051500Serics 17061500Serics instr = inbuf; 17071500Serics outstr = temp; 17081500Serics while ((ch = *instr++) != '\0') 17091500Serics switch (ch) { 17101500Serics case '%': 17111500Serics if (!no_intty) { 17121500Serics strcpy (outstr, fnames[fnum]); 17131500Serics outstr += strlen (fnames[fnum]); 17141500Serics changed++; 17151500Serics } 17161500Serics else 17171500Serics *outstr++ = ch; 17181500Serics break; 17191500Serics case '!': 17201500Serics if (!shellp) 17211500Serics error ("No previous command to substitute for"); 17221500Serics strcpy (outstr, shell_line); 17231500Serics outstr += strlen (shell_line); 17241500Serics changed++; 17251500Serics break; 17261500Serics case '\\': 17271500Serics if (*instr == '%' || *instr == '!') { 17281500Serics *outstr++ = *instr++; 17291500Serics break; 17301500Serics } 17311500Serics default: 17321500Serics *outstr++ = ch; 17331500Serics } 17341500Serics *outstr++ = '\0'; 17351500Serics strcpy (outbuf, temp); 17361500Serics return (changed); 17371500Serics } 17381500Serics 17391500Serics show (ch) 17401500Serics register char ch; 17411500Serics { 17421500Serics char cbuf; 17431500Serics 17441500Serics if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { 17451500Serics ch += ch == RUBOUT ? -0100 : 0100; 17461500Serics write (2, &CARAT, 1); 17471500Serics promptlen++; 17481500Serics } 17491500Serics cbuf = ch; 17501500Serics write (2, &cbuf, 1); 17511500Serics promptlen++; 17521500Serics } 17531500Serics 17541500Serics error (mess) 17551500Serics char *mess; 17561500Serics { 17573594Sroot if (clreol) 17583594Sroot cleareol (); 17593594Sroot else 17603594Sroot kill_line (); 17611500Serics promptlen += strlen (mess); 17621500Serics if (Senter && Sexit) { 17631500Serics tputs (Senter, 1, putch); 17641500Serics pr(mess); 17651500Serics tputs (Sexit, 1, putch); 17661500Serics } 17671500Serics else 17681500Serics pr (mess); 17691500Serics fflush(stdout); 17701500Serics errors++; 17711500Serics longjmp (restore, 1); 17721500Serics } 17731500Serics 17741500Serics 17751500Serics set_tty () 17761500Serics { 17771500Serics otty.sg_flags |= MBIT; 17781500Serics otty.sg_flags &= ~ECHO; 177916582Sleres stty(fileno(stderr), &otty); 17801500Serics } 17811500Serics 17821500Serics reset_tty () 17831500Serics { 178431033Sbostic if (no_tty) 178531033Sbostic return; 178616710Sjak if (pstate) { 178716710Sjak tputs(ULexit, 1, putch); 178816710Sjak fflush(stdout); 178916710Sjak pstate = 0; 179016710Sjak } 17911500Serics otty.sg_flags |= ECHO; 17921500Serics otty.sg_flags &= ~MBIT; 179316582Sleres stty(fileno(stderr), &savetty); 17941500Serics } 17951500Serics 17961500Serics rdline (f) 17971500Serics register FILE *f; 17981500Serics { 17991500Serics register char c; 18001500Serics register char *p; 18011500Serics 18021500Serics p = Line; 18031500Serics while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1) 18041500Serics *p++ = c; 18051500Serics if (c == '\n') 18061500Serics Currline++; 18071500Serics *p = '\0'; 18081500Serics } 18091500Serics 18101500Serics /* Come here when we get a suspend signal from the terminal */ 18111500Serics 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) 18291500Serics longjmp (restore); 18301500Serics } 1831