home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume7 / less3 / part02 < prev    next >
Encoding:
Internet Message Format  |  1986-12-04  |  58.8 KB

  1. Subject:  v07i083:  New release of LESS, Part02/03
  2. Newsgroups: mod.sources
  3. Approved: mirror!rs
  4.  
  5. Submitted by: rgb@nscpdc.uucp (Robert Bond)
  6. Mod.sources: Volume 7, Issue 83
  7. Archive-name: less3/Part02
  8.  
  9.  ---- cut here ---- cut here ---- cut here ---- cut here ---- cut here ----
  10. : This is a shell archive.
  11. : Unpack by running /bin/sh.
  12. echo less.nro
  13. cat >less.nro <<'_SHAR_EOF_'
  14. .TH LESS l
  15. .SH NAME
  16. less \- opposite of more
  17. .SH SYNOPSIS
  18. .B "less [-cdepstwmMqQuU] [-h\fIN\fB] [-b[fp]\fIN\fB] [-x\fIN\fB] [-[z]\fIN\fB]"
  19. .br
  20. .B "     [-P[mM]\fIstring\fB] [-l\fIlogfile\fB] [+\fIcmd\fB]  [\fIfilename\fB]..."
  21. .SH DESCRIPTION
  22. .I Less
  23. is a program similar to 
  24. .I more
  25. (1), but which allows backwards movement
  26. in the file as well as forward movement.
  27. Also,
  28. .I less
  29. does not have to read the entire input file before starting,
  30. so with large input files it starts up faster than text editors like
  31. .I vi
  32. (1).
  33. .I Less
  34. uses termcap, so it can run on a variety of terminals.
  35. There is even limited support for hardcopy terminals.
  36. (On a hardcopy terminal, lines which should be printed at the top
  37. of the screen are prefixed with an up-arrow.)
  38. .PP
  39. Commands are based on both
  40. .I more
  41. and
  42. .I vi.
  43. Commands may be preceeded by a decimal number, 
  44. called N in the descriptions below.
  45. The number is used by some commands, as indicated.
  46.  
  47. .SH COMMANDS
  48. In the following descriptions, ^X means control-X.
  49. .IP h
  50. Help: display a summary of these commands.
  51. If you forget all the other commands, remember this one.
  52. .PP
  53. .IP SPACE
  54. Scroll forward N lines, default one window (see option \-z below).
  55. If N is more than the screen size, only the final screenful is displayed.
  56. .PP
  57. .IP "f or ^F"
  58. Same as SPACE.
  59. .PP
  60. .IP "b or ^B"
  61. Scroll backward N lines, default one window (see option \-z below).
  62. If N is more than the screen size, only the final screenful is displayed.
  63. .PP
  64. .IP RETURN
  65. Scroll forward N lines, default 1.
  66. The entire N lines are displayed, even if N is more than the screen size.
  67. .PP
  68. .IP "e or ^E"
  69. Same as RETURN.
  70. .PP
  71. .IP "j or ^J"
  72. Also the same as RETURN.
  73. .PP
  74. .IP "y or ^Y"
  75. Scroll backward N lines, default 1.
  76. The entire N lines are displayed, even if N is more than the screen size.
  77. .IP "k or ^K"
  78. Same as y.
  79. .PP
  80. .IP "d or ^D"
  81. Scroll forward N lines, default 10.
  82. If N is specified, it becomes the new default for 
  83. subsequent d and u commands.
  84. .PP
  85. .IP "u or ^U"
  86. Scroll backward N lines, default 10.
  87. If N is specified, it becomes the new default for 
  88. subsequent d and u commands.
  89. .PP
  90. .IP "r or ^R or ^L"
  91. Repaint the screen.
  92. .PP
  93. .IP R
  94. Repaint the screen, discarding any buffered input.
  95. Useful if the file is changing while it is being viewed.
  96. .PP
  97. .IP g
  98. Go to line N in the file, default 1 (beginning of file).
  99. (Warning: this may be slow if N is large.)
  100. .PP
  101. .IP G
  102. Go to line N in the file, default the end of the file.
  103. (Warning: this may be slow if standard input, 
  104. rather than a file, is being read.)
  105. .PP
  106. .IP p
  107. Go to a position N percent into the file.
  108. N should be between 0 and 100.
  109. (This is possible if standard input is being read,
  110. but only if
  111. .I less
  112. has already read to the end of the file.
  113. It is always fast, but not always useful.)
  114. .PP
  115. .IP %
  116. Same as p.
  117. .PP
  118. .IP m
  119. Followed by any lowercase letter, 
  120. marks the current position with that letter.
  121. .PP
  122. .IP "'"
  123. (Single quote.)
  124. Followed by any lowercase letter, returns to the position which
  125. was previously marked with that letter.
  126. Followed by another single quote, returns to the postion at
  127. which the last "large" movement command was executed.
  128. All marks are lost when a new file is examined.
  129. .PP
  130. .IP /pattern
  131. Search forward in the file for the N-th line containing the pattern.
  132. N defaults to 1.
  133. The pattern is a regular expression, as recognized by
  134. .I ed.
  135. The search starts at the second line displayed
  136. (but see the \-t option, which changes this).
  137. .PP
  138. .IP ?pattern
  139. Search backward in the file for the N-th line containing the pattern.
  140. The search starts at the line immediately before the top line displayed.
  141. .PP
  142. .IP n
  143. Repeat previous search, for N-th line containing the last pattern.
  144. .PP
  145. .IP E [filename]
  146. Examine a new file.
  147. If the filename is missing, the "current" file (see the N and P commands
  148. below) from the list of files in the command line is re-examined.
  149. .PP
  150. .IP N
  151. Examine the next file (from the list of files given in the command line).
  152. If a number N is specified (not to be confused with the command N),
  153. the N-th next file is examined.
  154. .PP
  155. .IP P
  156. Examine the previous file.
  157. If a number N is specified, the N-th previous file is examined.
  158. .PP
  159. .IP "= or ^G"
  160. Prints some information about the file being viewed,
  161. including its name
  162. and the byte offset of the bottom line being displayed.
  163. If possible, it also prints the length of the file
  164. and the percent of the file above the last displayed line.
  165. .PP
  166. .IP \-
  167. Followed by one of the command line option letters (see below),
  168. this will toggle the setting of that option
  169. and print a message describing the new setting.
  170. .PP
  171. .IP +cmd
  172. Causes the specified cmd to be executed each time a new file is examined.
  173. For example, +G causes 
  174. .I less
  175. to initially display each file starting at the end 
  176. rather than the beginning.
  177. .PP
  178. .IP V
  179. Prints the version number of 
  180. .I less 
  181. being run.
  182. .PP
  183. .IP q
  184. Exits
  185. .I less.
  186. .PP
  187. The following 
  188. two 
  189. commands may or may not be valid, depending on your particular installation.
  190. .PP
  191. .IP v
  192. Invokes an editor to edit the current file being viewed.
  193. The editor is taken from the environment variable EDITOR,
  194. or defaults to "vi".
  195. .PP
  196. .IP "! shell-command"
  197. Invokes a shell to run the shell-command given.
  198. .PP
  199. .SH OPTIONS
  200. Command line options are described below.
  201. Most options may be changed while
  202. .I less 
  203. is running, via the "\-" command.
  204. .PP
  205. Options are also taken from the environment variable "LESS".
  206. For example, if you like 
  207. more-style prompting, to avoid typing "less \-m ..." each time 
  208. .I less 
  209. is invoked, you might tell 
  210. .I csh:
  211. .sp
  212. setenv LESS m
  213. .sp
  214. or if you use 
  215. .I sh:
  216. .sp
  217. LESS=m; export LESS
  218. .sp
  219. The environment variable is parsed before the command line,
  220. so command line options override the LESS environment variable.
  221. A dollar sign ($) may be used to signal the end of an option string.
  222. This is important only for options like \-P which take a
  223. following string.
  224. .IP \-s
  225. The \-s option causes
  226. consecutive blank lines to be squeezed into a single blank line.
  227. This is useful when viewing
  228. .I nroff
  229. output.
  230. .IP \-t
  231. Normally, forward searches start just after
  232. the top displayed line (that is, at the second displayed line).
  233. Thus forward searches include the currently displayed screen.
  234. The \-t option causes forward searches to start 
  235. just after the bottom line displayed,
  236. thus skipping the currently displayed screen.
  237. .IP \-m
  238. Normally,
  239. .I less
  240. prompts with a colon.
  241. The \-m option causes 
  242. .I less
  243. to prompt verbosely (like 
  244. .I more),
  245. with the percent into the file.
  246. .IP \-M
  247. The \-M option causes 
  248. .I less
  249. to prompt even more verbosely than 
  250. .I more.
  251. .IP \-P
  252. The \-P option provides a way to tailor the three prompt
  253. styles to your own preference.
  254. You would normally put this option in your LESS environment
  255. variable, rather than type it in with each less command.
  256. Such an option must either be the last option in the LESS variable,
  257. or be terminated by a dollar sign.
  258. \-P followed by a string changes the default (short) prompt to that string.
  259. \-Pm changes the medium (\-m) prompt to the string, and
  260. \-PM changes the long (\-M) prompt.
  261. The string consists of a sequence of letters which are replaced
  262. with certain predefined strings, as follows:
  263. .br
  264.     F    file name
  265. .br
  266.     f    file name, only once
  267. .br
  268.     O    file n of n
  269. .br
  270.     o    file n of n, only once
  271. .br
  272.     b    byte offset
  273. .br
  274.     p    percent into file
  275. .br
  276.     P    percent if known, else byte offset
  277. .br
  278. Angle brackets, < and >, may be used to surround a
  279. literal string to be included in the prompt.
  280. The defaults are "fo" for the short prompt,
  281. "foP" for the medium prompt, and
  282. "Fobp" for the long prompt.
  283. .br
  284. Example: Setting your LESS variable to "PmFOP$PMFObp"
  285. would change the medium and long prompts to always 
  286. include the file name and "file n of n" message.
  287. .br
  288. Another example: Setting your LESS variable to 
  289. .br
  290. "mPm<--Less-->FoPe"
  291. would change the medium prompt to the string "--Less--" followed
  292. by the file name and percent into the file.
  293. It also selects the medium
  294. prompt as the default prompt (because of the first "m").
  295. .IP \-q
  296. Normally, if an attempt is made to scroll past the end of the file
  297. or before the beginning of the file, the terminal bell is rung to
  298. indicate this fact.
  299. The \-q option tells
  300. .I less
  301. not to ring the bell at such times.
  302. If the terminal has a "visual bell", it is used instead.
  303. .IP \-Q
  304. Even if \-q is given, 
  305. .I less 
  306. will ring the bell on certain other errors,
  307. such as typing an invalid character.
  308. The \-Q option tells
  309. .I less
  310. to be quiet all the time; that is, never ring the terminal bell.
  311. If the terminal has a "visual bell", it is used instead.
  312. .IP \-e
  313. Normally the only way to exit less is via the "q" command.
  314. The \-e option tells less to automatically exit
  315. the second time it reaches end-of-file.
  316. .IP \-u
  317. If the \-u option is given, 
  318. backspaces are treated as printable characters;
  319. that is, they are sent to the terminal when they appear in the input.
  320. .IP \-U
  321. If the \-U option is given,
  322. backspaces are printed as the two character sequence "^H".
  323. .sp
  324. If neither \-u nor \-U is given,
  325. backspaces which appear adjacent to an underscore character
  326. are treated specially:
  327. the underlined text is displayed 
  328. using the terminal's hardware underlining capability.
  329. Also, backspaces which appear between two identical characters
  330. are treated specially: 
  331. the overstruck text is printed 
  332. using the terminal's hardware boldface capability.
  333. Other backspaces are deleted, along with the preceeding character.
  334. .IP \-w
  335. Normally,
  336. .I less
  337. uses a tilde character to represent lines past the end of the file.
  338. The \-w option causes blank lines to be used instead.
  339. .IP \-d
  340. Normally,
  341. .I less
  342. will complain if the terminal is dumb; that is, lacks some important capability,
  343. such as the ability to clear the screen or scroll backwards.
  344. The \-d option suppresses this complaint 
  345. (but does not otherwise change the behavior of the program on a dumb terminal).
  346. .IP \-p
  347. Normally, 
  348. .I less 
  349. will repaint the screen by scrolling from the bottom of the screen.
  350. If the \-p option is set, when
  351. .I less 
  352. needs to change the entire display, it will clear the screen
  353. and paint from the top line down.
  354. .IP \-h
  355. Normally,
  356. .I less
  357. will scroll backwards when backwards movement is necessary.
  358. The \-h option specifies a maximum number of lines to scroll backwards.
  359. If it is necessary to move backwards more than this many lines,
  360. the screen is repainted in a forward direction.
  361. (If the terminal does not have the ability to scroll
  362. backwards, \-h0 is implied.)
  363. .IP \-[z]
  364. When given a backwards or forwards window command,
  365. .I less
  366. will by
  367. default scroll backwards or forwards one screenful of lines. 
  368. The \-z\fIn\fR option changes the default scrolling window size 
  369. to \fIn\fR lines.
  370. If \fIn\fR is greater than the screen size, 
  371. the scrolling window size will be set to one screenful.  
  372. Note that the "z" is optional for compatibility with
  373. .I more.
  374. .IP -x
  375. The -x\fIn\fR option sets tab stops every \fIn\fR positions.
  376. The default for \fIn\fR is 8.
  377. .IP -l
  378. The -l option, followed immediately by a filename,
  379. will cause 
  380. .I less
  381. to copy its input to the named file as it is being viewed.
  382. This applies only when the input file is a pipe,
  383. not an ordinary file.
  384. .IP -b
  385. The -b\fIn\fR option tells
  386. .I less
  387. to use a non-standard buffer size.
  388. There are two standard (default) buffer sizes,
  389. one is used when a file is being read and the other
  390. when a pipe (standard input) is being read.
  391. The current defaults are 5 buffers for files and 12 for pipes.
  392. (Buffers are 1024 bytes.)
  393. The number \fIn\fR specifies a different number of buffers to use.
  394. The -b may be followed by "f", in which case only 
  395. the file default is changed, or by "p" in which case only the 
  396. pipe default is changed.  Otherwise, both are changed.
  397. .IP -c
  398. Normally, when data is read by
  399. .I less,
  400. it is scanned to ensure that bit 7 (the high order bit) is turned off in
  401. each byte read, and to ensure that there are no null (zero) bytes in
  402. the data (null bytes are turned into "@" characters).
  403. If the data is known to be "clean",
  404. the -c option will tell 
  405. .I less
  406. to skip this checking, causing an imperceptible speed improvement.
  407. (However, if the data is not "clean", unpredicatable results may occur.)
  408. .IP +
  409. If a command line option begins with \fB+\fR,
  410. the remainder of that option is taken to be an initial command to
  411. .I less.
  412. For example, +G tells
  413. .I less
  414. to start at the end of the file rather than the beginning,
  415. and +/xyz tells it to start at the first occurence of "xyz" in the file.
  416. As a special case, +<number> acts like +<number>g; 
  417. that is, it starts the display at the specified line number
  418. (however, see the caveat under the "g" command above).
  419. If the option starts with \fB++\fR, the initial command applies to
  420. every file being viewed, not just the first one.
  421. The + command described previously
  422. may also be used to set (or change) an initial command for every file.
  423.  
  424. .SH BUGS
  425. When used on standard input (rather than a file), you can move
  426. backwards only a finite amount, corresponding to that portion
  427. of the file which is still buffered.
  428. The -b option may be used to expand the buffer space.
  429. _SHAR_EOF_
  430.  
  431. echo main.c
  432. cat >main.c <<'_SHAR_EOF_'
  433. /*
  434.  * Entry point, initialization, miscellaneous routines.
  435.  */
  436.  
  437. #include "less.h"
  438. #include "position.h"
  439. #include <setjmp.h>
  440.  
  441. public int    ispipe;
  442. public jmp_buf    main_loop;
  443. public char *    first_cmd;
  444. public char *    every_first_cmd;
  445. public int    new_file;
  446. public int    is_tty;
  447. public char     current_file[128];
  448. public int    any_display;
  449. public int    ac;
  450. public char **    av;
  451. public int     curr_ac;
  452. #if LOGFILE
  453. public int    logfile = -1;
  454. public char *    namelogfile = NULL;
  455. #endif
  456. #if EDITOR
  457. public char *    editor;
  458. #endif
  459.  
  460. extern int file;
  461. extern int nbufs;
  462. extern int sigs;
  463. extern int quit_at_eof;
  464. extern int p_nbufs, f_nbufs;
  465. extern int back_scroll;
  466. extern int top_scroll;
  467. extern int sc_height;
  468. extern int errmsgs;
  469.  
  470.  
  471. /*
  472.  * Edit a new file.
  473.  * Filename "-" means standard input.
  474.  * No filename means the "current" file, from the command line.
  475.  */
  476.     public void
  477. edit(filename)
  478.     char *filename;
  479. {
  480.     register int f;
  481.     char message[100];
  482.     static didpipe;
  483.  
  484.     if (filename == NULL || *filename == '\0')
  485.     {
  486.         if (curr_ac >= ac)
  487.         {
  488.             error("No current file");
  489.             return;
  490.         }
  491.         filename = av[curr_ac];
  492.     }
  493.     if (strcmp(filename, "-") == 0)
  494.     {
  495.         /* 
  496.          * Use standard input.
  497.          */
  498.         if (didpipe)
  499.         {
  500.             error("Can view standard input only once");
  501.             return;
  502.         }
  503.         f = 0;
  504.     } else if ((f = open(filename, 0)) < 0)
  505.     {
  506.         static char co[] = "Cannot open ";
  507.         strcpy(message, co);
  508.         strtcpy(message+sizeof(co)-1, filename, 
  509.             sizeof(message)-sizeof(co));
  510.         error(message);
  511.         return;
  512.     }
  513.  
  514.     if (isatty(f))
  515.     {
  516.         /*
  517.          * Not really necessary to call this an error,
  518.          * but if the control terminal (for commands)
  519.          * and the input file (for data) are the same,
  520.          * we get weird results at best.
  521.          */
  522.         error("Can't take input from a terminal");
  523.         if (f > 0)
  524.             close(f);
  525.         return;
  526.     }
  527.  
  528. #if LOGFILE
  529.     /*
  530.      * If he asked for a log file and we have opened standard input,
  531.      * create the log file.  
  532.      * We take care not to blindly overwrite an existing file.
  533.      */
  534.     end_logfile();
  535.     if (f == 0 && namelogfile != NULL && is_tty)
  536.     {
  537.         int exists;
  538.         int answer;
  539.  
  540.         /*
  541.          * {{ We could use access() here. }}
  542.          */
  543.         exists = open(namelogfile, 0);
  544.         close(exists);
  545.         exists = (exists >= 0);
  546.  
  547.         if (exists)
  548.         {
  549.             static char w[] = "WARNING: log file exists: ";
  550.             strcpy(message, w);
  551.             strtcpy(message+sizeof(w)-1, namelogfile,
  552.                 sizeof(message)-sizeof(w));
  553.             error(message);
  554.             answer = 'X';    /* Ask the user what to do */
  555.         } else
  556.             answer = 'O';    /* Create the log file */
  557.  
  558.     loop:
  559.         switch (answer)
  560.         {
  561.         case 'O': case 'o':
  562.             logfile = creat(namelogfile, 0644);
  563.             break;
  564.         case 'A': case 'a':
  565.             logfile = open(namelogfile, 1);
  566.             if (lseek(logfile, (off_t)0, 2) < 0)
  567.             {
  568.                 close(logfile);
  569.                 logfile = -1;
  570.             }
  571.             break;
  572.         case 'D': case 'd':
  573.             answer = 0;    /* Don't print an error message */
  574.             break;
  575.         case 'q':
  576.             quit();
  577.         default:
  578.             puts("\n  Overwrite, Append, or Don't log? ");
  579.             answer = getc();
  580.             puts("\n");
  581.             flush();
  582.             goto loop;
  583.         }
  584.  
  585.         if (logfile < 0 && answer != 0)
  586.         {
  587.             sprintf(message, "Cannot write to \"%s\"", 
  588.                 namelogfile);
  589.             error(message);
  590.         }
  591.     }
  592. #endif
  593.  
  594.     /*
  595.      * We are now committed to using the new file.
  596.      * Close the current input file and set up to use the new one.
  597.      */
  598.     if (file > 0)
  599.         close(file);
  600.     new_file = 1;
  601.     strtcpy(current_file, filename, sizeof(current_file));
  602.     ispipe = (f == 0);
  603.     if (ispipe)
  604.         didpipe = 1;
  605.     file = f;
  606.     ch_init( (ispipe) ? p_nbufs : f_nbufs );
  607.     init_mark();
  608.  
  609.     if (every_first_cmd != NULL)
  610.         first_cmd = every_first_cmd;
  611.  
  612.     if (is_tty)
  613.     {
  614.         int no_display = !any_display;
  615.         any_display = 1;
  616.         if (no_display && errmsgs > 0)
  617.         {
  618.             /*
  619.              * We displayed some messages on error output
  620.              * (file descriptor 2; see error() function).
  621.              * Before erasing the screen contents,
  622.              * display the file name and wait for a keystroke.
  623.              */
  624.             error(filename);
  625.         }
  626.         /*
  627.          * Indicate there is nothing displayed yet.
  628.          */
  629.         pos_clear();
  630.     }
  631. }
  632.  
  633. /*
  634.  * Edit the next file in the command line list.
  635.  */
  636.     public void
  637. next_file(n)
  638.     int n;
  639. {
  640.     if (curr_ac + n >= ac)
  641.     {
  642.         if (quit_at_eof)
  643.             quit();
  644.         error("No (N-th) next file");
  645.     } else
  646.         edit(av[curr_ac += n]);
  647. }
  648.  
  649. /*
  650.  * Edit the previous file in the command line list.
  651.  */
  652.     public void
  653. prev_file(n)
  654.     int n;
  655. {
  656.     if (curr_ac - n < 0)
  657.         error("No (N-th) previous file");
  658.     else
  659.         edit(av[curr_ac -= n]);
  660. }
  661.  
  662. /*
  663.  * Copy a file directly to standard output.
  664.  * Used if standard output is not a tty.
  665.  */
  666.     static void
  667. cat_file()
  668. {
  669.     register int c;
  670.  
  671.     while ((c = ch_forw_get()) != EOF)
  672.         putc(c);
  673.     flush();
  674. }
  675.  
  676. /*
  677.  * Entry point.
  678.  */
  679. main(argc, argv)
  680.     int argc;
  681.     char *argv[];
  682. {
  683.     char *getenv();
  684.  
  685.  
  686.     /*
  687.      * Process command line arguments and LESS environment arguments.
  688.      * Command line arguments override environment arguments.
  689.      */
  690.     init_option();
  691.     scan_option(getenv("LESS"));
  692.     argv++;
  693.     while ( (--argc > 0) && 
  694.         (argv[0][0] == '-' || argv[0][0] == '+') && 
  695.         argv[0][1] != '\0')
  696.         scan_option(*argv++);
  697.  
  698. #if EDITOR
  699.     editor = getenv("EDITOR");
  700.     if (editor == NULL || *editor == '\0')
  701.         editor = EDIT_PGM;
  702. #endif
  703.  
  704.     /*
  705.      * Set up list of files to be examined.
  706.      */
  707.     ac = argc;
  708.     av = argv;
  709.     curr_ac = 0;
  710.  
  711.     /*
  712.      * Set up terminal, etc.
  713.      */
  714.     is_tty = isatty(1);
  715.     if (!is_tty)
  716.     {
  717.         /*
  718.          * Output is not a tty.
  719.          * Just copy the input file(s) to output.
  720.          */
  721.         if (ac < 1)
  722.         {
  723.             edit("-");
  724.             cat_file();
  725.         } else
  726.         {
  727.             do
  728.             {
  729.                 edit((char *)NULL);
  730.                 if (file >= 0)
  731.                     cat_file();
  732.             } while (++curr_ac < ac);
  733.         }
  734.         exit(0);
  735.     }
  736.  
  737.     raw_mode(1);
  738.     get_term();
  739.     open_getc();
  740.     init();
  741.  
  742.     if (setjmp(main_loop))
  743.         quit();
  744.     init_signals();
  745.  
  746.     /*
  747.      * Select the first file to examine.
  748.      */
  749.     if (ac < 1)
  750.         edit("-");    /* Standard input */
  751.     else 
  752.     {
  753.         /*
  754.          * Try all the files named as command arguments.
  755.          * We are simply looking for one which can be
  756.          * opened without error.
  757.          */
  758.         do
  759.         {
  760.             edit((char *)NULL);
  761.         } while (file < 0 && ++curr_ac < ac);
  762.     }
  763.  
  764.     if (file >= 0)
  765.         commands();
  766.     quit();
  767. }
  768.  
  769. /*
  770.  * Copy a string, truncating to the specified length if necessary.
  771.  * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
  772.  */
  773. strtcpy(to, from, len)
  774.     char *to;
  775.     char *from;
  776.     int len;
  777. {
  778.     strncpy(to, from, len);
  779.     to[len-1] = '\0';
  780. }
  781.  
  782. /*
  783.  * Exit the program.
  784.  */
  785.     public void
  786. quit()
  787. {
  788.     /*
  789.      * Put cursor at bottom left corner, clear the line,
  790.      * reset the terminal modes, and exit.
  791.      */
  792. #if LOGFILE
  793.     end_logfile();
  794. #endif
  795.     lower_left();
  796.     clear_eol();
  797.     deinit();
  798.     flush();
  799.     raw_mode(0);
  800.     exit(0);
  801. }
  802. _SHAR_EOF_
  803.  
  804. echo option.c
  805. cat >option.c <<'_SHAR_EOF_'
  806. /*
  807.  * Process command line options.
  808.  * Each option is a single letter which controls a program variable.
  809.  * The options have defaults which may be changed via
  810.  * the command line option, or toggled via the "-" command.
  811.  */
  812.  
  813. #include "less.h"
  814.  
  815. #define    toupper(c)    ((c)-'a'+'A')
  816.  
  817. #define    END_OPTION_STRING    ('$')
  818.  
  819. /*
  820.  * Types of options.
  821.  */
  822. #define    BOOL        01    /* Boolean option: 0 or 1 */
  823. #define    TRIPLE        02    /* Triple-valued option: 0, 1 or 2 */
  824. #define    NUMBER        04    /* Numeric option */
  825. #define    REPAINT        040    /* Repaint screen after toggling option */
  826. #define    NO_TOGGLE    0100    /* Option cannot be toggled with "-" cmd */
  827.  
  828. /*
  829.  * Variables controlled by command line options.
  830.  */
  831. public int p_nbufs, f_nbufs;    /* Number of buffers.  There are two values,
  832.                    one used for input from a pipe and 
  833.                    the other for input from a file. */
  834. public int clean_data;        /* Can we assume the data is "clean"? 
  835.                    (That is, free of nulls, etc) */
  836. public int quiet;        /* Should we suppress the audible bell? */
  837. public int top_search;        /* Should forward searches start at the top 
  838.                    of the screen? (alternative is bottom) */
  839. public int top_scroll;        /* Repaint screen from top?
  840.                    (alternative is scroll from bottom) */
  841. public int pr_type;        /* Type of prompt (short, medium, long) */
  842. public int bs_mode;        /* How to process backspaces */
  843. public int know_dumb;        /* Don't complain about dumb terminals */
  844. public int quit_at_eof;        /* Quit after hitting end of file twice */
  845. public int squeeze;        /* Squeeze multiple blank lines into one */
  846. public int tabstop;        /* Tab settings */
  847. public int back_scroll;        /* Repaint screen on backwards movement */
  848. public int twiddle;        /* Display "~" for lines after EOF */
  849.  
  850. extern char *prproto[];
  851. extern int nbufs;
  852. extern int sc_window;
  853. extern char *first_cmd;
  854. extern char *every_first_cmd;
  855. #if LOGFILE
  856. extern char *namelogfile;
  857. #endif
  858.  
  859. #define    DEF_F_NBUFS    5    /* Default for f_nbufs */
  860. #define    DEF_P_NBUFS    12    /* Default for p_nbufs */
  861.  
  862. static struct option
  863. {
  864.     char oletter;        /* The controlling letter (a-z) */
  865.     char otype;        /* Type of the option */
  866.     int odefault;        /* Default value */
  867.     int *ovar;        /* Pointer to the associated variable */
  868.     char *odesc[3];        /* Description of each value */
  869. } option[] =
  870. {
  871.     { 'c', BOOL, 0, &clean_data,
  872.         { "Don't assume data is clean",
  873.           "Assume data is clean",
  874.           NULL
  875.         }
  876.     },
  877.     { 'd', BOOL|NO_TOGGLE, 0, &know_dumb,
  878.         { NULL, NULL, NULL}
  879.     },
  880.     { 'e', BOOL, 0, &quit_at_eof,
  881.         { "Don't quit at end-of-file",
  882.           "Quit at end-of-file",
  883.           NULL
  884.         }
  885.     },
  886.     { 'h', NUMBER, -1, &back_scroll,
  887.         { "Backwards scroll limit is %d lines",
  888.           NULL, NULL
  889.         }
  890.     },
  891.     { 'p', BOOL, 0, &top_scroll,
  892.         { "Repaint by scrolling from bottom of screen",
  893.           "Repaint by painting from top of screen",
  894.           NULL
  895.         }
  896.     },
  897.     { 'x', NUMBER, 8, &tabstop,
  898.         { "Tab stops every %d spaces", 
  899.           NULL, NULL 
  900.         }
  901.     },
  902.     { 's', BOOL|REPAINT, 0, &squeeze,
  903.         { "Don't squeeze multiple blank lines",
  904.           "Squeeze multiple blank lines",
  905.           NULL
  906.         }
  907.     },
  908.     { 't', BOOL, 1, &top_search,
  909.         { "Forward search starts from bottom of screen",
  910.           "Forward search starts from top of screen",
  911.           NULL
  912.         }
  913.     },
  914.     { 'w', BOOL|REPAINT, 1, &twiddle,
  915.         { "Display nothing for lines after end-of-file",
  916.           "Display ~ for lines after end-of-file",
  917.           NULL
  918.         }
  919.     },
  920.     { 'm', TRIPLE, 0, &pr_type,
  921.         { "Short prompt",
  922.           "Medium prompt",
  923.           "Long prompt"
  924.         }
  925.     },
  926.     { 'q', TRIPLE, 0, &quiet,
  927.         { "Ring the bell for errors AND at eof/bof",
  928.           "Ring the bell for errors but not at eof/bof",
  929.           "Never ring the bell"
  930.         }
  931.     },
  932.     { 'u', TRIPLE|REPAINT, 0, &bs_mode,
  933.         { "Underlined text displayed in underline mode",
  934.           "Backspaces cause overstrike",
  935.           "Backspaces print as ^H"
  936.         }
  937.     },
  938.     { 'z', NUMBER, 24, &sc_window,
  939.         { "Scroll window size is %d lines",
  940.           NULL, NULL
  941.         }
  942.     },
  943.     { '\0' }
  944. };
  945.  
  946. public char all_options[64];    /* List of all valid options */
  947.  
  948. /*
  949.  * Initialize each option to its default value.
  950.  */
  951.     public void
  952. init_option()
  953. {
  954.     register struct option *o;
  955.     register char *p;
  956.  
  957.     /*
  958.      * First do special cases, not in option table.
  959.      */
  960.     first_cmd = every_first_cmd = NULL;
  961.     f_nbufs = DEF_F_NBUFS;        /* -bf */
  962.     p_nbufs = DEF_P_NBUFS;        /* -bp */
  963.  
  964.     p = all_options;
  965.     *p++ = 'b';
  966.  
  967.     for (o = option;  o->oletter != '\0';  o++)
  968.     {
  969.         /*
  970.          * Set each variable to its default.
  971.          * Also make a list of all options, in "all_options".
  972.          */
  973.         *(o->ovar) = o->odefault;
  974.         *p++ = o->oletter;
  975.         if (o->otype & TRIPLE)
  976.             *p++ = toupper(o->oletter);
  977.     }
  978.     *p = '\0';
  979. }
  980.  
  981. /*
  982.  * Toggle command line flags from within the program.
  983.  * Used by the "-" command.
  984.  */
  985.     public void
  986. toggle_option(c)
  987.     int c;
  988. {
  989.     register struct option *o;
  990.     char message[100];
  991.     char buf[5];
  992.  
  993.     /*
  994.      * First check for special cases not handled by the option table.
  995.      */
  996.     switch (c)
  997.     {
  998.     case 'b':
  999.         sprintf(message, "%d buffers", nbufs);
  1000.         error(message);
  1001.         return;
  1002.     }
  1003.  
  1004.  
  1005.     for (o = option;  o->oletter != '\0';  o++)
  1006.     {
  1007.         if (o->otype & NO_TOGGLE)
  1008.             continue;
  1009.         if ((o->otype & BOOL) && (o->oletter == c))
  1010.         {
  1011.             /*
  1012.              * Boolean option: 
  1013.              * just toggle it.
  1014.              */
  1015.             *(o->ovar) = ! *(o->ovar);
  1016.         } else if ((o->otype & TRIPLE) && (o->oletter == c))
  1017.         {
  1018.             /*
  1019.              * Triple-valued option with lower case letter:
  1020.              * make it 1 unless already 1, then make it 0.
  1021.              */
  1022.             *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1;
  1023.         } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
  1024.         {
  1025.             /*
  1026.              * Triple-valued option with upper case letter:
  1027.              * make it 2 unless already 2, then make it 0.
  1028.              */
  1029.             *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2;
  1030.         } else if ((o->otype & NUMBER) && (o->oletter == c))
  1031.         {
  1032.             sprintf(message, o->odesc[0], 
  1033.                 (o->ovar == &back_scroll) ? 
  1034.                 get_back_scroll() : *(o->ovar));
  1035.             error(message);
  1036.             return;
  1037.         } else
  1038.             continue;
  1039.  
  1040.         if (o->otype & REPAINT)
  1041.             repaint();
  1042.         error(o->odesc[*(o->ovar)]);
  1043.         return;
  1044.     }
  1045.  
  1046.     if (control_char(c))
  1047.         sprintf(buf, "^%c", carat_char(c));
  1048.     else
  1049.         sprintf(buf, "%c", c);
  1050.     sprintf(message, "\"-%s\": no such flag.  Use one of \"%s\"", 
  1051.         buf, all_options);
  1052.     error(message);
  1053. }
  1054.  
  1055. /*
  1056.  * Scan to end of string or to an END_OPTION_STRING character.
  1057.  * In the latter case, replace the char with a null char.
  1058.  * Return a pointer to the remainder of the string, if any.
  1059.  */
  1060.     static char *
  1061. optstring(s)
  1062.     char *s;
  1063. {
  1064.     register char *p;
  1065.  
  1066.     for (p = s;  *p != '\0';  p++)
  1067.         if (*p == END_OPTION_STRING)
  1068.         {
  1069.             *p = '\0';
  1070.             return (p+1);
  1071.         }
  1072.     return (p);
  1073. }
  1074.  
  1075. /* 
  1076.  * Scan an argument (either from command line or from LESS environment 
  1077.  * variable) and process it.
  1078.  */
  1079.     public void
  1080. scan_option(s)
  1081.     char *s;
  1082. {
  1083.     register struct option *o;
  1084.     register int c;
  1085.     char message[80];
  1086.  
  1087.     if (s == NULL)
  1088.         return;
  1089.  
  1090.     next:
  1091.     if (*s == '\0')
  1092.         return;
  1093.     switch (c = *s++)
  1094.     {
  1095.     case '-':
  1096.     case ' ':
  1097.     case '\t':
  1098.     case END_OPTION_STRING:
  1099.         goto next;
  1100.     case '+':
  1101.         if (*s == '+')
  1102.             every_first_cmd = ++s;
  1103.         first_cmd = s;
  1104.         s = optstring(s);
  1105.         goto next;
  1106.     case 'P':
  1107.         switch (*s)
  1108.         {
  1109.         case 'm':  prproto[PR_MEDIUM] = ++s;  break;
  1110.         case 'M':  prproto[PR_LONG] = ++s;    break;
  1111.         default:   prproto[PR_SHORT] = s;     break;
  1112.         }
  1113.         s = optstring(s);
  1114.         goto next;
  1115. #if LOGFILE
  1116.     case 'l':
  1117.         namelogfile = s;
  1118.         s = optstring(s);
  1119.         goto next;
  1120. #endif
  1121.     case 'b':
  1122.         switch (*s)
  1123.         {
  1124.         case 'f':
  1125.             s++;
  1126.             f_nbufs = getnum(&s, 'b');
  1127.             break;
  1128.         case 'p':
  1129.             s++;
  1130.             p_nbufs = getnum(&s, 'b');
  1131.             break;
  1132.         default:
  1133.             f_nbufs = p_nbufs = getnum(&s, 'b');
  1134.             break;
  1135.         }
  1136.         goto next;
  1137.     case '0':  case '1':  case '2':  case '3':  case '4':
  1138.     case '5':  case '6':  case '7':  case '8':  case '9':
  1139.         {
  1140.             /*
  1141.              * Handle special "more" compatibility form "-number"
  1142.              * to set the scrolling window size.
  1143.              */
  1144.             s--;
  1145.             sc_window = getnum(&s, '-');
  1146.             goto next;
  1147.         }
  1148.     }
  1149.  
  1150.     for (o = option;  o->oletter != '\0';  o++)
  1151.     {
  1152.         if ((o->otype & BOOL) && (o->oletter == c))
  1153.         {
  1154.             *(o->ovar) = ! o->odefault;
  1155.             goto next;
  1156.         } else if ((o->otype & TRIPLE) && (o->oletter == c))
  1157.         {
  1158.             *(o->ovar) = (o->odefault == 1) ? 0 : 1;
  1159.             goto next;
  1160.         } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
  1161.         {
  1162.             *(o->ovar) = (o->odefault == 2) ? 0 : 2;
  1163.             goto next;
  1164.         } else if ((o->otype & NUMBER) && (o->oletter == c))
  1165.         {
  1166.             *(o->ovar) = getnum(&s, c);
  1167.             goto next;
  1168.         }
  1169.     }
  1170.  
  1171.     sprintf(message, "\"-%c\": invalid flag", c);
  1172.     error(message);
  1173.     exit(1);
  1174. }
  1175.  
  1176. /*
  1177.  * Translate a string into a number.
  1178.  * Like atoi(), but takes a pointer to a char *, and updates
  1179.  * the char * to point after the translated number.
  1180.  */
  1181.     static int
  1182. getnum(sp, c)
  1183.     char **sp;
  1184.     int c;
  1185. {
  1186.     register char *s;
  1187.     register int n;
  1188.     char message[80];
  1189.  
  1190.     s = *sp;
  1191.     if (*s < '0' || *s > '9')
  1192.     {
  1193.         sprintf(message, "number is required after -%c", c);
  1194.         error(message);
  1195.         exit(1);
  1196.     }
  1197.  
  1198.     n = 0;
  1199.     while (*s >= '0' && *s <= '9')
  1200.         n = 10 * n + *s++ - '0';
  1201.     *sp = s;
  1202.     return (n);
  1203. }
  1204. _SHAR_EOF_
  1205.  
  1206. echo prim.c
  1207. cat >prim.c <<'_SHAR_EOF_'
  1208. /*
  1209.  * Primitives for displaying the file on the screen.
  1210.  */
  1211.  
  1212. #include "less.h"
  1213. #include "position.h"
  1214.  
  1215. public int hit_eof;    /* Keeps track of how many times we hit end of file */
  1216.  
  1217. extern int quiet;
  1218. extern int top_search;
  1219. extern int top_scroll;
  1220. extern int back_scroll;
  1221. extern int sc_width, sc_height;
  1222. extern int sigs;
  1223. extern char *line;
  1224. extern char *first_cmd;
  1225.  
  1226. /*
  1227.  * Sound the bell to indicate he is trying to move past end of file.
  1228.  */
  1229.     static void
  1230. eof_bell()
  1231. {
  1232.     if (quiet == NOT_QUIET)
  1233.         bell();
  1234.     else
  1235.         vbell();
  1236. }
  1237.  
  1238. /*
  1239.  * Check to see if the end of file is currently "displayed".
  1240.  */
  1241.     static void
  1242. eof_check()
  1243. {
  1244.     POSITION pos;
  1245.  
  1246.     /*
  1247.      * If the bottom line is empty, we are at EOF.
  1248.      * If the bottom line ends at the file length,
  1249.      * we must be just at EOF.
  1250.      */
  1251.     pos = position(BOTTOM_PLUS_ONE);
  1252.     if (pos == NULL_POSITION || pos == ch_length())
  1253.         hit_eof++;
  1254. }
  1255.  
  1256. /*
  1257.  * Display n lines, scrolling forward, 
  1258.  * starting at position pos in the input file.
  1259.  * "force" means display the n lines even if we hit end of file.
  1260.  * "only_last" means display only the last screenful if n > screen size.
  1261.  */
  1262.     static void
  1263. forw(n, pos, force, only_last)
  1264.     register int n;
  1265.     POSITION pos;
  1266.     int force;
  1267.     int only_last;
  1268. {
  1269.     int eof = 0;
  1270.     int nlines = 0;
  1271.     int repaint_flag;
  1272.     static int first_time = 1;
  1273.  
  1274.     /*
  1275.      * repaint_flag tells us not to display anything till the end, 
  1276.      * then just repaint the entire screen.
  1277.      */
  1278.     repaint_flag = (only_last && n > sc_height-1);
  1279.  
  1280.     if (!repaint_flag)
  1281.     {
  1282.         if (top_scroll && n >= sc_height - 1)
  1283.         {
  1284.             /*
  1285.              * Start a new screen.
  1286.              * {{ This is not really desirable if we happen
  1287.              *    to hit eof in the middle of this screen,
  1288.              *    but we don't yet know if that will happen. }}
  1289.              */
  1290.             clear();
  1291.             home();
  1292.             force = 1;
  1293.         } else
  1294.         {
  1295.             lower_left();
  1296.             clear_eol();
  1297.         }
  1298.  
  1299.         if (pos != position(BOTTOM_PLUS_ONE))
  1300.         {
  1301.             /*
  1302.              * This is not contiguous with what is
  1303.              * currently displayed.  Clear the screen image 
  1304.              * (position table) and start a new screen.
  1305.              */
  1306.             pos_clear();
  1307.             add_forw_pos(pos);
  1308.             force = 1;
  1309.             if (top_scroll)
  1310.             {
  1311.                 clear();
  1312.                 home();
  1313.             } else if (!first_time)
  1314.             {
  1315.                 puts("...skipping...\n");
  1316.             }
  1317.         }
  1318.     }
  1319.  
  1320.     while (--n >= 0)
  1321.     {
  1322.         /*
  1323.          * Read the next line of input.
  1324.          */
  1325.         pos = forw_line(pos);
  1326.         if (pos == NULL_POSITION)
  1327.         {
  1328.             /*
  1329.              * End of file: stop here unless the top line 
  1330.              * is still empty, or "force" is true.
  1331.              */
  1332.             eof = 1;
  1333.             if (!force && position(TOP) != NULL_POSITION)
  1334.                 break;
  1335.             line = NULL;
  1336.         }
  1337.         /*
  1338.          * Add the position of the next line to the position table.
  1339.          * Display the current line on the screen.
  1340.          */
  1341.         add_forw_pos(pos);
  1342.         nlines++;
  1343.         if (repaint_flag || 
  1344.             (first_time && line == NULL && !top_scroll))
  1345.             continue;
  1346.         put_line();
  1347.     }
  1348.  
  1349.     if (eof)
  1350.         hit_eof++;
  1351.     else
  1352.         eof_check();
  1353.     if (nlines == 0)
  1354.         eof_bell();
  1355.     else if (repaint_flag)
  1356.         repaint();
  1357.     first_time = 0;
  1358. }
  1359.  
  1360. /*
  1361.  * Display n lines, scrolling backward.
  1362.  */
  1363.     static void
  1364. back(n, pos, force, only_last)
  1365.     register int n;
  1366.     POSITION pos;
  1367.     int force;
  1368.     int only_last;
  1369. {
  1370.     int nlines = 0;
  1371.     int repaint_flag;
  1372.  
  1373.     repaint_flag = (n > get_back_scroll() || (only_last && n > sc_height-1));
  1374.     hit_eof = 0;
  1375.     while (--n >= 0)
  1376.     {
  1377.         /*
  1378.          * Get the previous line of input.
  1379.          */
  1380.         pos = back_line(pos);
  1381.         if (pos == NULL_POSITION)
  1382.         {
  1383.             /*
  1384.              * Beginning of file: stop here unless "force" is true.
  1385.              */
  1386.             if (!force)
  1387.                 break;
  1388.             line = NULL;
  1389.         }
  1390.         /*
  1391.          * Add the position of the previous line to the position table.
  1392.          * Display the line on the screen.
  1393.          */
  1394.         add_back_pos(pos);
  1395.         nlines++;
  1396.         if (!repaint_flag)
  1397.         {
  1398.             home();
  1399.             add_line();
  1400.             put_line();
  1401.         }
  1402.     }
  1403.  
  1404.     eof_check();
  1405.     if (nlines == 0)
  1406.         eof_bell();
  1407.     else if (repaint_flag)
  1408.         repaint();
  1409. }
  1410.  
  1411. /*
  1412.  * Display n more lines, forward.
  1413.  * Start just after the line currently displayed at the bottom of the screen.
  1414.  */
  1415.     public void
  1416. forward(n, only_last)
  1417.     int n;
  1418.     int only_last;
  1419. {
  1420.     POSITION pos;
  1421.  
  1422.     pos = position(BOTTOM_PLUS_ONE);
  1423.     if (pos == NULL_POSITION)
  1424.     {
  1425.         eof_bell();
  1426.         hit_eof++;
  1427.         return;
  1428.     }
  1429.     forw(n, pos, 0, only_last);
  1430. }
  1431.  
  1432. /*
  1433.  * Display n more lines, backward.
  1434.  * Start just before the line currently displayed at the top of the screen.
  1435.  */
  1436.     public void
  1437. backward(n, only_last)
  1438.     int n;
  1439.     int only_last;
  1440. {
  1441.     POSITION pos;
  1442.  
  1443.     pos = position(TOP);
  1444.     if (pos == NULL_POSITION)
  1445.     {
  1446.         /* 
  1447.          * This will almost never happen,
  1448.          * because the top line is almost never empty. 
  1449.          */
  1450.         eof_bell();
  1451.         return;   
  1452.     }
  1453.     back(n, pos, 0, only_last);
  1454. }
  1455.  
  1456. /*
  1457.  * Repaint the screen, starting from a specified position.
  1458.  */
  1459.     static void
  1460. prepaint(pos)    
  1461.     POSITION pos;
  1462. {
  1463.     hit_eof = 0;
  1464.     forw(sc_height-1, pos, 0, 0);
  1465. }
  1466.  
  1467. /*
  1468.  * Repaint the screen.
  1469.  */
  1470.     public void
  1471. repaint()
  1472. {
  1473.     /*
  1474.      * Start at the line currently at the top of the screen
  1475.      * and redisplay the screen.
  1476.      */
  1477.     prepaint(position(TOP));
  1478. }
  1479.  
  1480. /*
  1481.  * Jump to the end of the file.
  1482.  * It is more convenient to paint the screen backward,
  1483.  * from the end of the file toward the beginning.
  1484.  */
  1485.     public void
  1486. jump_forw()
  1487. {
  1488.     POSITION pos;
  1489.  
  1490.     if (ch_end_seek())
  1491.     {
  1492.         error("Cannot seek to end of file");
  1493.         return;
  1494.     }
  1495.     lastmark();
  1496.     pos = ch_tell();
  1497.     clear();
  1498.     pos_clear();
  1499.     add_back_pos(pos);
  1500.     back(sc_height - 1, pos, 0, 0);
  1501. }
  1502.  
  1503. /*
  1504.  * Jump to line n in the file.
  1505.  */
  1506.     public void
  1507. jump_back(n)
  1508.     register int n;
  1509. {
  1510.     register int c;
  1511.     int nlines;
  1512.  
  1513.     /*
  1514.      * This is done the slow way, by starting at the beginning
  1515.      * of the file and counting newlines.
  1516.      */
  1517.     if (ch_seek((POSITION)0))
  1518.     {
  1519.         /* 
  1520.          * Probably a pipe with beginning of file no longer buffered. 
  1521.          * If he wants to go to line 1, we do the best we can, 
  1522.          * by going to the first line which is still buffered.
  1523.          */
  1524.         if (n <= 1 && ch_beg_seek() == 0)
  1525.             jump_loc(ch_tell());
  1526.         error("Cannot get to beginning of file");
  1527.         return;
  1528.     }
  1529.  
  1530.     /*
  1531.      * Start counting lines.
  1532.      */
  1533.     for (nlines = 1;  nlines < n;  nlines++)
  1534.     {
  1535.         while ((c = ch_forw_get()) != '\n')
  1536.             if (c == EOF)
  1537.             {
  1538.                 char message[40];
  1539.                 sprintf(message, "File has only %d lines", 
  1540.                     nlines-1);
  1541.                 error(message);
  1542.                 return;
  1543.             }
  1544.     }
  1545.  
  1546.     jump_loc(ch_tell());
  1547. }
  1548.  
  1549. /*
  1550.  * Jump to a specified percentage into the file.
  1551.  * This is a poor compensation for not being able to
  1552.  * quickly jump to a specific line number.
  1553.  */
  1554.     public void
  1555. jump_percent(percent)
  1556.     int percent;
  1557. {
  1558.     POSITION pos, len;
  1559.     register int c;
  1560.  
  1561.     /*
  1562.      * Determine the position in the file
  1563.      * (the specified percentage of the file's length).
  1564.      */
  1565.     if ((len = ch_length()) == NULL_POSITION)
  1566.     {
  1567.         error("Don't know length of file");
  1568.         return;
  1569.     }
  1570.     pos = (percent * len) / 100;
  1571.  
  1572.     /*
  1573.      * Back up to the beginning of the line.
  1574.      */
  1575.     if (ch_seek(pos) == 0)
  1576.     {
  1577.         while ((c = ch_back_get()) != '\n' && c != EOF)
  1578.             ;
  1579.         if (c == '\n')
  1580.             (void) ch_forw_get();
  1581.         pos = ch_tell();
  1582.     }
  1583.     jump_loc(pos);
  1584. }
  1585.  
  1586. /*
  1587.  * Jump to a specified position in the file.
  1588.  */
  1589.     public void
  1590. jump_loc(pos)
  1591.     POSITION pos;
  1592. {
  1593.     register int nline;
  1594.     POSITION tpos;
  1595.  
  1596.     /*
  1597.      * See if the desired line is BEFORE the currently
  1598.      * displayed screen.  If so, see if it is close enough 
  1599.      * to scroll backwards to it.
  1600.      * {{ This can be expensive if he has specified a very
  1601.      *    large back_scroll count.  Perhaps we should put
  1602.      *    some sanity limit on the loop count here. }}
  1603.      */
  1604.     tpos = position(TOP);
  1605.     if (tpos != NULL_POSITION && pos < tpos)
  1606.     {
  1607.         int bs = get_back_scroll();
  1608.         for (nline = 1;  nline <= bs;  nline++)
  1609.         {
  1610.             tpos = back_line(tpos);
  1611.             if (tpos == NULL_POSITION)
  1612.                 break;
  1613.             if (tpos <= pos)
  1614.             {
  1615.                 back(nline, position(TOP), 1, 0);
  1616.                 return;
  1617.             }
  1618.         }
  1619.     } else if ((nline = onscreen(pos)) >= 0)
  1620.     {
  1621.         /*
  1622.          * The line is currently displayed.  
  1623.          * Just scroll there.
  1624.          */
  1625.         forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
  1626.         return;
  1627.     }
  1628.  
  1629.     /*
  1630.      * Line is not on screen.
  1631.      * Remember where we were; clear and paint the screen.
  1632.      */
  1633.     if (ch_seek(pos))
  1634.     {
  1635.         error("Cannot seek to that position");
  1636.         return;
  1637.     }
  1638.     lastmark();
  1639.     prepaint(pos);
  1640. }
  1641.  
  1642. /*
  1643.  * The table of marks.
  1644.  * A mark is simply a position in the file.
  1645.  */
  1646. #define    NMARKS        (27)        /* 26 for a-z plus one for quote */
  1647. #define    LASTMARK    (NMARKS-1)    /* For quote */
  1648. static POSITION marks[NMARKS];
  1649.  
  1650. /*
  1651.  * Initialize the mark table to show no marks are set.
  1652.  */
  1653.     public void
  1654. init_mark()
  1655. {
  1656.     int i;
  1657.  
  1658.     for (i = 0;  i < NMARKS;  i++)
  1659.         marks[i] = NULL_POSITION;
  1660. }
  1661.  
  1662. /*
  1663.  * See if a mark letter is valid (between a and z).
  1664.  */
  1665.     static int
  1666. badmark(c)
  1667.     int c;
  1668. {
  1669.     if (c < 'a' || c > 'z')
  1670.     {
  1671.         error("Choose a letter between 'a' and 'z'");
  1672.         return (1);
  1673.     }
  1674.     return (0);
  1675. }
  1676.  
  1677. /*
  1678.  * Set a mark.
  1679.  */
  1680.     public void
  1681. setmark(c)
  1682.     int c;
  1683. {
  1684.     if (badmark(c))
  1685.         return;
  1686.     marks[c-'a'] = position(TOP);
  1687. }
  1688.  
  1689.     public void
  1690. lastmark()
  1691. {
  1692.     marks[LASTMARK] = position(TOP);
  1693. }
  1694.  
  1695. /*
  1696.  * Go to a previously set mark.
  1697.  */
  1698.     public void
  1699. gomark(c)
  1700.     int c;
  1701. {
  1702.     POSITION pos;
  1703.  
  1704.     if (c == '\'')
  1705.         pos = marks[LASTMARK];
  1706.     else if (badmark(c))
  1707.         return;
  1708.     else 
  1709.         pos = marks[c-'a'];
  1710.  
  1711.     if (pos == NULL_POSITION)
  1712.         error("mark not set");
  1713.     else
  1714.         jump_loc(pos);
  1715. }
  1716.  
  1717. /*
  1718.  * Get the backwards scroll limit.
  1719.  * Must call this function instead of just using the value of
  1720.  * back_scroll, because the default case depends on sc_height and
  1721.  * top_scroll, as well as back_scroll.
  1722.  */
  1723.     public int
  1724. get_back_scroll()
  1725. {
  1726.     if (back_scroll < 0)
  1727.         return (sc_height - 1 - top_scroll);
  1728.     return (back_scroll);
  1729. }
  1730.  
  1731. /*
  1732.  * Search for the n-th occurence of a specified pattern, 
  1733.  * either forward (direction == '/'), or backwards (direction == '?').
  1734.  */
  1735.     public void
  1736. search(direction, pattern, n)
  1737.     int direction;
  1738.     char *pattern;
  1739.     register int n;
  1740. {
  1741.     register int search_forward = (direction == '/');
  1742.     POSITION pos, linepos;
  1743.  
  1744. #if RECOMP
  1745.     char *re_comp();
  1746.     char *errmsg;
  1747.  
  1748.     /*
  1749.      * (re_comp handles a null pattern internally, 
  1750.      *  so there is no need to check for a null pattern here.)
  1751.      */
  1752.     if ((errmsg = re_comp(pattern)) != NULL)
  1753.     {
  1754.         error(errmsg);
  1755.         return;
  1756.     }
  1757. #else
  1758. #if REGCMP
  1759.     char *regcmp();
  1760.     static char *cpattern = NULL;
  1761.  
  1762.     if (pattern == NULL || *pattern == '\0')
  1763.     {
  1764.         /*
  1765.          * A null pattern means use the previous pattern.
  1766.          * The compiled previous pattern is in cpattern, so just use it.
  1767.          */
  1768.         if (cpattern == NULL)
  1769.         {
  1770.             error("No previous regular expression");
  1771.             return;
  1772.         }
  1773.     } else
  1774.     {
  1775.         /*
  1776.          * Otherwise compile the given pattern.
  1777.          */
  1778.         char *s;
  1779.         if ((s = regcmp(pattern, 0)) == NULL)
  1780.         {
  1781.             error("Invalid pattern");
  1782.             return;
  1783.         }
  1784.         if (cpattern != NULL)
  1785.             free(cpattern);
  1786.         cpattern = s;
  1787.     }
  1788. #else
  1789.     static char lpbuf[100];
  1790.     static char *last_pattern = NULL;
  1791.  
  1792.     if (pattern == NULL || *pattern == '\0')
  1793.     {
  1794.         /*
  1795.          * Null pattern means use the previous pattern.
  1796.          */
  1797.         if (last_pattern == NULL)
  1798.         {
  1799.             error("No previous regular expression");
  1800.             return;
  1801.         }
  1802.         pattern = last_pattern;
  1803.     } else
  1804.     {
  1805.         strcpy(lpbuf, pattern);
  1806.         last_pattern = lpbuf;
  1807.     }
  1808. #endif
  1809. #endif
  1810.  
  1811.     /*
  1812.      * Figure out where to start the search.
  1813.      */
  1814.  
  1815.     if (position(TOP) == NULL_POSITION)
  1816.     {
  1817.         /*
  1818.          * Nothing is currently displayed.
  1819.          * Start at the beginning of the file.
  1820.          * (This case is mainly for first_cmd searches,
  1821.          * for example, "+/xyz" on the command line.)
  1822.          */
  1823.         pos = (POSITION)0;
  1824.     } else if (!search_forward)
  1825.     {
  1826.         /*
  1827.          * Backward search: start just before the top line
  1828.          * displayed on the screen.
  1829.          */
  1830.         pos = position(TOP);
  1831.     } else if (top_search)
  1832.     {
  1833.         /*
  1834.          * Forward search and "start from top".
  1835.          * Start at the second line displayed on the screen.
  1836.          */
  1837.         pos = position(TOP_PLUS_ONE);
  1838.     } else
  1839.     {
  1840.         /*
  1841.          * Forward search but don't "start from top".
  1842.          * Start just after the bottom line displayed on the screen.
  1843.          */
  1844.         pos = position(BOTTOM_PLUS_ONE);
  1845.     }
  1846.  
  1847.     if (pos == NULL_POSITION)
  1848.     {
  1849.         /*
  1850.          * Can't find anyplace to start searching from.
  1851.          */
  1852.         error("Nothing to search");
  1853.         return;
  1854.     }
  1855.  
  1856.     for (;;)
  1857.     {
  1858.         /*
  1859.          * Get lines until we find a matching one or 
  1860.          * until we hit end-of-file (or beginning-of-file 
  1861.          * if we're going backwards).
  1862.          */
  1863.         if (sigs)
  1864.             /*
  1865.              * A signal aborts the search.
  1866.              */
  1867.             return;
  1868.  
  1869.         if (search_forward)
  1870.         {
  1871.             /*
  1872.              * Read the next line, and save the 
  1873.              * starting position of that line in linepos.
  1874.              */
  1875.             linepos = pos;
  1876.             pos = forw_raw_line(pos);
  1877.         } else
  1878.         {
  1879.             /*
  1880.              * Read the previous line and save the
  1881.              * starting position of that line in linepos.
  1882.              */
  1883.             pos = back_raw_line(pos);
  1884.             linepos = pos;
  1885.         }
  1886.  
  1887.         if (pos == NULL_POSITION)
  1888.         {
  1889.             /*
  1890.              * We hit EOF/BOF without a match.
  1891.              */
  1892.             error("Pattern not found");
  1893.             return;
  1894.         }
  1895.  
  1896.         /*
  1897.          * Test the next line to see if we have a match.
  1898.          * This is done in a variety of ways, depending
  1899.          * on what pattern matching functions are available.
  1900.          */
  1901. #if REGCMP
  1902.         if ( (regex(cpattern, line) != NULL)
  1903. #else
  1904. #if RECOMP
  1905.         if ( (re_exec(line) == 1)
  1906. #else
  1907.         if ( (match(pattern, line))
  1908. #endif
  1909. #endif
  1910.                 && (--n <= 0) )
  1911.             /*
  1912.              * Found the matching line.
  1913.              */
  1914.             break;
  1915.     }
  1916.  
  1917.     jump_loc(linepos);
  1918. }
  1919.  
  1920. #if (!REGCMP) && (!RECOMP)
  1921. /*
  1922.  * We have neither regcmp() nor re_comp().
  1923.  * We use this function to do simple pattern matching.
  1924.  * It supports no metacharacters like *, etc.
  1925.  */
  1926.     static int
  1927. match(pattern, buf)
  1928.     char *pattern, *buf;
  1929. {
  1930.     register char *pp, *lp;
  1931.  
  1932.     for ( ;  *buf != '\0';  buf++)
  1933.     {
  1934.         for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
  1935.             if (*pp == '\0' || *lp == '\0')
  1936.                 break;
  1937.         if (*pp == '\0')
  1938.             return (1);
  1939.     }
  1940.     return (0);
  1941. }
  1942. #endif
  1943. _SHAR_EOF_
  1944.  
  1945. echo ch.c
  1946. cat >ch.c <<'_SHAR_EOF_'
  1947. /*
  1948.  * Low level character input from the input file.
  1949.  * We use these special purpose routines which optimize moving
  1950.  * both forward and backward from the current read pointer.
  1951.  */
  1952.  
  1953. #include "less.h"
  1954.  
  1955. public int file = -1;    /* File descriptor of the input file */
  1956.  
  1957. /*
  1958.  * Pool of buffers holding the most recently used blocks of the input file.
  1959.  */
  1960. #define BUFSIZ    1024
  1961. struct buf {
  1962.     struct buf *next, *prev;
  1963.     long block;
  1964.     char data[BUFSIZ];
  1965. };
  1966. static struct buf *bufs = NULL;
  1967. public int nbufs;
  1968.  
  1969. /*
  1970.  * The buffer pool is kept as a doubly-linked circular list,
  1971.  * in order from most- to least-recently used.
  1972.  * The circular list is anchored by buf_anchor.
  1973.  */
  1974. static struct {
  1975.     struct buf *next, *prev;
  1976. } buf_anchor;
  1977. #define    END_OF_CHAIN    ((struct buf *)&buf_anchor)
  1978. #define    buf_head    buf_anchor.next
  1979. #define    buf_tail    buf_anchor.prev
  1980.  
  1981. /*
  1982.  * If we fail to allocate enough memory for buffers, we try to limp
  1983.  * along with a minimum number of buffers.  
  1984.  */
  1985. #define    DEF_NBUFS    2    /* Minimum number of buffers */
  1986.  
  1987. extern int clean_data;
  1988. extern int ispipe;
  1989. extern int sigs;
  1990.  
  1991. #if LOGFILE
  1992. extern int logfile;
  1993. #endif
  1994.  
  1995. /*
  1996.  * Current position in file.
  1997.  * Stored as a block number and an offset into the block.
  1998.  */
  1999. static long ch_block;
  2000. static int ch_offset;
  2001.  
  2002. /* 
  2003.  * Length of file, needed if input is a pipe.
  2004.  */
  2005. static POSITION ch_fsize;
  2006.  
  2007. /*
  2008.  * Largest block number read if input is standard input (a pipe).
  2009.  */
  2010. static long last_piped_block;
  2011.  
  2012. /*
  2013.  * Get the character pointed to by the read pointer.
  2014.  * ch_get() is a macro which is more efficient to call
  2015.  * than fch_get (the function), in the usual case 
  2016.  * that the block desired is at the head of the chain.
  2017.  */
  2018. #define    ch_get()   ((buf_head->block == ch_block) ? \
  2019.             buf_head->data[ch_offset] : fch_get())
  2020.     static int
  2021. fch_get()
  2022. {
  2023.     register struct buf *bp;
  2024.     register int n;
  2025.     register int end;
  2026.     POSITION pos;
  2027.  
  2028.     /*
  2029.      * Look for a buffer holding the desired block.
  2030.      */
  2031.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  2032.         if (bp->block == ch_block)
  2033.             goto found;
  2034.     /*
  2035.      * Block is not in a buffer.  
  2036.      * Take the least recently used buffer 
  2037.      * and read the desired block into it.
  2038.      */
  2039.     bp = buf_tail;
  2040.     bp->block = ch_block;
  2041.     pos = ch_block * BUFSIZ;
  2042.     if (ispipe)
  2043.     {
  2044.         /*
  2045.          * The block requested should be one more than
  2046.          * the last block read.
  2047.          */
  2048.         if (ch_block != ++last_piped_block)
  2049.         {
  2050.             /* This "should not happen". */
  2051.             char message[80];
  2052.             sprintf(message, "Pipe error: last %ld, want %ld\n",
  2053.                 (long)last_piped_block-1, (long)ch_block);
  2054.             error(message);
  2055.             quit();
  2056.         }
  2057.     } else
  2058.         lseek(file, pos, 0);
  2059.  
  2060.     /*
  2061.      * Read the block.  This may take several reads if the input
  2062.      * is coming from standard input, due to the nature of pipes.
  2063.      */
  2064.     end = 0;
  2065.     while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0)
  2066.         if ((end += n) >= BUFSIZ)
  2067.             break;
  2068.  
  2069.     if (n < 0)
  2070.     {
  2071.         error("read error");
  2072.         quit();
  2073.     }
  2074.  
  2075. #if LOGFILE
  2076.     /*
  2077.      * If we have a log file, write this block to it.
  2078.      */
  2079.     if (logfile >= 0 && end > 0)
  2080.         write(logfile, bp->data, end);
  2081. #endif
  2082.  
  2083.     /*
  2084.      * Set an EOF marker in the buffered data itself.
  2085.      * Then ensure the data is "clean": there are no 
  2086.      * extra EOF chars in the data and that the "meta"
  2087.      * bit (the 0200 bit) is reset in each char.
  2088.      */
  2089.     if (end < BUFSIZ)
  2090.     {
  2091.         ch_fsize = pos + end;
  2092.         bp->data[end] = EOF;
  2093.     }
  2094.  
  2095.     if (!clean_data)
  2096.         while (--end >= 0)
  2097.         {
  2098.             bp->data[end] &= 0177;
  2099.             if (bp->data[end] == EOF)
  2100.                 bp->data[end] = '@';
  2101.         }
  2102.  
  2103.     found:
  2104.     /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */
  2105.     {
  2106.         /*
  2107.          * Move the buffer to the head of the buffer chain.
  2108.          * This orders the buffer chain, most- to least-recently used.
  2109.          */
  2110.         bp->next->prev = bp->prev;
  2111.         bp->prev->next = bp->next;
  2112.  
  2113.         bp->next = buf_head;
  2114.         bp->prev = END_OF_CHAIN;
  2115.         buf_head->prev = bp;
  2116.         buf_head = bp;
  2117.     }
  2118.     return (bp->data[ch_offset]);
  2119. }
  2120.  
  2121. #if LOGFILE
  2122. /*
  2123.  * Close the logfile.
  2124.  * If we haven't read all of standard input into it, do that now.
  2125.  */
  2126.     public void
  2127. end_logfile()
  2128. {
  2129.     static int tried;
  2130.  
  2131.     if (logfile < 0)
  2132.         return;
  2133.     if (!tried && ch_fsize == NULL_POSITION)
  2134.     {
  2135.         tried = 1;
  2136.         lower_left();
  2137.         clear_eol();
  2138.         so_enter();
  2139.         puts("finishing logfile... (interrupt to abort)");
  2140.         so_exit();
  2141.         flush();
  2142.         while (sigs == 0 && ch_forw_get() != EOF)
  2143.             ;
  2144.     }
  2145.     close(logfile);
  2146.     logfile = -1;
  2147. }
  2148. #endif
  2149.  
  2150. /*
  2151.  * Determine if a specific block is currently in one of the buffers.
  2152.  */
  2153.     static int
  2154. buffered(block)
  2155.     long block;
  2156. {
  2157.     register struct buf *bp;
  2158.  
  2159.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  2160.         if (bp->block == block)
  2161.             return (1);
  2162.     return (0);
  2163. }
  2164.  
  2165. /*
  2166.  * Seek to a specified position in the file.
  2167.  * Return 0 if successful, non-zero if can't seek there.
  2168.  */
  2169.     public int
  2170. ch_seek(pos)
  2171.     register POSITION pos;
  2172. {
  2173.     long new_block;
  2174.  
  2175.     new_block = pos / BUFSIZ;
  2176.     if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block))
  2177.     {
  2178.         /*
  2179.          * Set read pointer.
  2180.          */
  2181.         ch_block = new_block;
  2182.         ch_offset = pos % BUFSIZ;
  2183.         return (0);
  2184.     }
  2185.     return (1);
  2186. }
  2187.  
  2188. /*
  2189.  * Seek to the end of the file.
  2190.  */
  2191.     public int
  2192. ch_end_seek()
  2193. {
  2194.     if (ispipe)
  2195.     {
  2196.         /*
  2197.          * Do it the slow way: read till end of data.
  2198.          */
  2199.         while (ch_forw_get() != EOF)
  2200.             ;
  2201.     } else
  2202.     {
  2203.         (void) ch_seek((POSITION)(lseek(file, (off_t)0, 2)));
  2204.     }
  2205.     return (0);
  2206. }
  2207.  
  2208. /*
  2209.  * Seek to the beginning of the file, or as close to it as we can get.
  2210.  * We may not be able to seek there if input is a pipe and the
  2211.  * beginning of the pipe is no longer buffered.
  2212.  */
  2213.     public int
  2214. ch_beg_seek()
  2215. {
  2216.     register struct buf *bp, *firstbp;
  2217.  
  2218.     /*
  2219.      * Try a plain ch_seek first.
  2220.      */
  2221.     if (ch_seek((POSITION)0) == 0)
  2222.         return (0);
  2223.  
  2224.     /*
  2225.      * Can't get to position 0.
  2226.      * Look thru the buffers for the one closest to position 0.
  2227.      */
  2228.     firstbp = bp = buf_head;
  2229.     if (bp == END_OF_CHAIN)
  2230.         return (1);
  2231.     while ((bp = bp->next) != END_OF_CHAIN)
  2232.         if (bp->block < firstbp->block)
  2233.             firstbp = bp;
  2234.     ch_block = firstbp->block;
  2235.     ch_offset = 0;
  2236.     return (0);
  2237. }
  2238.  
  2239. /*
  2240.  * Return the length of the file, if known.
  2241.  */
  2242.     public POSITION
  2243. ch_length()
  2244. {
  2245.     if (ispipe)
  2246.         return (ch_fsize);
  2247.     return ((POSITION)(lseek(file, (off_t)0, 2)));
  2248. }
  2249.  
  2250. /*
  2251.  * Return the current position in the file.
  2252.  */
  2253.     public POSITION
  2254. ch_tell()
  2255. {
  2256.     return (ch_block * BUFSIZ + ch_offset);
  2257. }
  2258.  
  2259. /*
  2260.  * Get the current char and post-increment the read pointer.
  2261.  */
  2262.     public int
  2263. ch_forw_get()
  2264. {
  2265.     register int c;
  2266.  
  2267.     c = ch_get();
  2268.     if (c != EOF && ++ch_offset >= BUFSIZ)
  2269.     {
  2270.         ch_offset = 0;
  2271.         ch_block ++;
  2272.     }
  2273.     return (c);
  2274. }
  2275.  
  2276. /*
  2277.  * Pre-decrement the read pointer and get the new current char.
  2278.  */
  2279.     public int
  2280. ch_back_get()
  2281. {
  2282.     register int c;
  2283.  
  2284.     if (--ch_offset < 0)
  2285.     {
  2286.         if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
  2287.         {
  2288.             ch_offset = 0;
  2289.             return (EOF);
  2290.         }
  2291.         ch_offset = BUFSIZ - 1;
  2292.         ch_block--;
  2293.     }
  2294.     c = ch_get();
  2295.     return (c);
  2296. }
  2297.  
  2298. /*
  2299.  * Initialize the buffer pool to all empty.
  2300.  * Caller suggests that we use want_nbufs buffers.
  2301.  */
  2302.     public void
  2303. ch_init(want_nbufs)
  2304.     int want_nbufs;
  2305. {
  2306.     register struct buf *bp;
  2307.     char *calloc();
  2308.  
  2309.     if (nbufs < want_nbufs)
  2310.     {
  2311.         /*
  2312.          * We don't have enough buffers.  
  2313.          * Free what we have (if any) and allocate some new ones.
  2314.          */
  2315.         if (bufs != NULL)
  2316.             free((char *)bufs);
  2317.         bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf));
  2318.         nbufs = want_nbufs;
  2319.         if (bufs == NULL)
  2320.         {
  2321.             /*
  2322.              * Couldn't get that many.
  2323.              * Try for a small default number of buffers.
  2324.              */
  2325.             char message[80];
  2326.             sprintf(message,
  2327.               "Cannot allocate %d buffers.  Using %d buffers.", 
  2328.               nbufs, DEF_NBUFS);
  2329.             error(message);
  2330.             bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf));
  2331.             nbufs = DEF_NBUFS;
  2332.             if (bufs == NULL)
  2333.             {
  2334.                 /*
  2335.                  * Couldn't even get the smaller number of bufs.
  2336.                  * Something is wrong here, don't continue.
  2337.                  */
  2338.                 sprintf(message, 
  2339.                 "Cannot even allocate %d buffers!  Quitting.",
  2340.                   DEF_NBUFS);
  2341.                 error(message);
  2342.                 quit();
  2343.                 /*NOTREACHED*/
  2344.             }
  2345.         }
  2346.     }
  2347.  
  2348.     /*
  2349.      * Initialize the buffers to empty.
  2350.      * Set up the circular list.
  2351.      */
  2352.     for (bp = &bufs[0];  bp < &bufs[nbufs];  bp++)
  2353.     {
  2354.         bp->next = bp + 1;
  2355.         bp->prev = bp - 1;
  2356.         bp->block = (long)(-1);
  2357.     }
  2358.     bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN;
  2359.     buf_head = &bufs[0];
  2360.     buf_tail = &bufs[nbufs-1];
  2361.     last_piped_block = -1;
  2362.     ch_fsize = NULL_POSITION;
  2363.     (void) ch_seek((POSITION)0);
  2364. }
  2365. _SHAR_EOF_
  2366.  
  2367. echo position.c
  2368. cat >position.c <<'_SHAR_EOF_'
  2369. /*
  2370.  * Routines dealing with the "position" table.
  2371.  * This is a table which tells the position (in the input file) of the
  2372.  * first char on each currently displayed line.
  2373.  *
  2374.  * {{ The position table is scrolled by moving all the entries.
  2375.  *    Would be better to have a circular table 
  2376.  *    and just change a couple of pointers. }}
  2377.  */
  2378.  
  2379. #include "less.h"
  2380. #include "position.h"
  2381.  
  2382. #define    NPOS    100        /* {{ sc_height must be less than NPOS }} */
  2383. static POSITION table[NPOS];    /* The position table */
  2384.  
  2385. extern int sc_width, sc_height;
  2386.  
  2387. /*
  2388.  * Return the starting file position of a line displayed on the screen.
  2389.  * The line may be specified as a line number relative to the top
  2390.  * of the screen, but is usually one of these special cases:
  2391.  *    the top (first) line on the screen
  2392.  *    the second line on the screen
  2393.  *    the bottom line on the screen
  2394.  *    the line after the bottom line on the screen
  2395.  */
  2396.     public POSITION
  2397. position(where)
  2398.     int where;
  2399. {
  2400.     switch (where)
  2401.     {
  2402.     case BOTTOM:
  2403.         where = sc_height - 2;
  2404.         break;
  2405.     case BOTTOM_PLUS_ONE:
  2406.         where = sc_height - 1;
  2407.         break;
  2408.     }
  2409.     return (table[where]);
  2410. }
  2411.  
  2412. /*
  2413.  * Add a new file position to the bottom of the position table.
  2414.  */
  2415.     public void
  2416. add_forw_pos(pos)
  2417.     POSITION pos;
  2418. {
  2419.     register int i;
  2420.  
  2421.     /*
  2422.      * Scroll the position table up.
  2423.      */
  2424.     for (i = 1;  i < sc_height;  i++)
  2425.         table[i-1] = table[i];
  2426.     table[sc_height - 1] = pos;
  2427. }
  2428.  
  2429. /*
  2430.  * Add a new file position to the top of the position table.
  2431.  */
  2432.     public void
  2433. add_back_pos(pos)
  2434.     POSITION pos;
  2435. {
  2436.     register int i;
  2437.  
  2438.     /*
  2439.      * Scroll the position table down.
  2440.      */
  2441.     for (i = sc_height - 1;  i > 0;  i--)
  2442.         table[i] = table[i-1];
  2443.     table[0] = pos;
  2444. }
  2445.  
  2446. /*
  2447.  * Initialize the position table, done whenever we clear the screen.
  2448.  */
  2449.     public void
  2450. pos_clear()
  2451. {
  2452.     register int i;
  2453.  
  2454.     for (i = 0;  i < sc_height;  i++)
  2455.         table[i] = NULL_POSITION;
  2456. }
  2457.  
  2458. /*
  2459.  * See if the byte at a specified position is currently on the screen.
  2460.  * Check the position table to see if the position falls within its range.
  2461.  * Return the position table entry if found, -1 if not.
  2462.  */
  2463.     public int
  2464. onscreen(pos)
  2465.     POSITION pos;
  2466. {
  2467.     register int i;
  2468.  
  2469.     if (pos < table[0])
  2470.         return (-1);
  2471.     for (i = 1;  i < sc_height;  i++)
  2472.         if (pos < table[i])
  2473.             return (i-1);
  2474.     return (-1);
  2475. }
  2476. _SHAR_EOF_
  2477.  
  2478. echo input.c
  2479. cat >input.c <<'_SHAR_EOF_'
  2480. /*
  2481.  * High level routines dealing with getting lines of input 
  2482.  * from the file being viewed.
  2483.  *
  2484.  * When we speak of "lines" here, we mean PRINTABLE lines;
  2485.  * lines processed with respect to the screen width.
  2486.  * We use the term "raw line" to refer to lines simply
  2487.  * delimited by newlines; not processed with respect to screen width.
  2488.  */
  2489.  
  2490. #include "less.h"
  2491.  
  2492. extern int squeeze;
  2493. extern char *line;
  2494.  
  2495. /*
  2496.  * Get the next line.
  2497.  * A "current" position is passed and a "new" position is returned.
  2498.  * The current position is the position of the first character of
  2499.  * a line.  The new position is the position of the first character
  2500.  * of the NEXT line.  The line obtained is the line starting at curr_pos.
  2501.  */
  2502.     public POSITION
  2503. forw_line(curr_pos)
  2504.     POSITION curr_pos;
  2505. {
  2506.     POSITION new_pos;
  2507.     register int c;
  2508.  
  2509.     if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
  2510.         return (NULL_POSITION);
  2511.  
  2512.     c = ch_forw_get();
  2513.     if (c == EOF)
  2514.         return (NULL_POSITION);
  2515.  
  2516.     prewind();
  2517.     for (;;)
  2518.     {
  2519.         if (c == '\n' || c == EOF)
  2520.         {
  2521.             /*
  2522.              * End of the line.
  2523.              */
  2524.             new_pos = ch_tell();
  2525.             break;
  2526.         }
  2527.  
  2528.         /*
  2529.          * Append the char to the line and get the next char.
  2530.          */
  2531.         if (pappend(c))
  2532.         {
  2533.             /*
  2534.              * The char won't fit in the line; the line
  2535.              * is too long to print in the screen width.
  2536.              * End the line here.
  2537.              */
  2538.             new_pos = ch_tell() - 1;
  2539.             break;
  2540.         }
  2541.         c = ch_forw_get();
  2542.     }
  2543.     (void) pappend('\0');
  2544.  
  2545.     if (squeeze && *line == '\0')
  2546.     {
  2547.         /*
  2548.          * This line is blank.
  2549.          * Skip down to the last contiguous blank line
  2550.          * and pretend it is the one which we are returning.
  2551.          */
  2552.         while ((c = ch_forw_get()) == '\n')
  2553.             ;
  2554.         if (c != EOF)
  2555.             (void) ch_back_get();
  2556.         new_pos = ch_tell();
  2557.     }
  2558.  
  2559.     return (new_pos);
  2560. }
  2561.  
  2562. /*
  2563.  * Get the previous line.
  2564.  * A "current" position is passed and a "new" position is returned.
  2565.  * The current position is the position of the first character of
  2566.  * a line.  The new position is the position of the first character
  2567.  * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
  2568.  */
  2569.     public POSITION
  2570. back_line(curr_pos)
  2571.     POSITION curr_pos;
  2572. {
  2573.     POSITION new_pos, begin_new_pos;
  2574.     int c;
  2575.  
  2576.     if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
  2577.         ch_seek(curr_pos-1))
  2578.         return (NULL_POSITION);
  2579.  
  2580.     if (squeeze)
  2581.     {
  2582.         /*
  2583.          * Find out if the "current" line was blank.
  2584.          */
  2585.         (void) ch_forw_get();    /* Skip the newline */
  2586.         c = ch_forw_get();    /* First char of "current" line */
  2587.         (void) ch_back_get();    /* Restore our position */
  2588.         (void) ch_back_get();
  2589.  
  2590.         if (c == '\n')
  2591.         {
  2592.             /*
  2593.              * The "current" line was blank.
  2594.              * Skip over any preceeding blank lines,
  2595.              * since we skipped them in forw_line().
  2596.              */
  2597.             while ((c = ch_back_get()) == '\n')
  2598.                 ;
  2599.             if (c == EOF)
  2600.                 return (NULL_POSITION);
  2601.             (void) ch_forw_get();
  2602.         }
  2603.     }
  2604.  
  2605.     /*
  2606.      * Scan backwards until we hit the beginning of the line.
  2607.      */
  2608.     for (;;)
  2609.     {
  2610.         c = ch_back_get();
  2611.         if (c == '\n')
  2612.         {
  2613.             /*
  2614.              * This is the newline ending the previous line.
  2615.              * We have hit the beginning of the line.
  2616.              */
  2617.             new_pos = ch_tell() + 1;
  2618.             break;
  2619.         }
  2620.         if (c == EOF)
  2621.         {
  2622.             /*
  2623.              * We have hit the beginning of the file.
  2624.              * This must be the first line in the file.
  2625.              * This must, of course, be the beginning of the line.
  2626.              */
  2627.             new_pos = ch_tell();
  2628.             break;
  2629.         }
  2630.     }
  2631.  
  2632.     /*
  2633.      * Now scan forwards from the beginning of this line.
  2634.      * We keep discarding "printable lines" (based on screen width)
  2635.      * until we reach the curr_pos.
  2636.      *
  2637.      * {{ This algorithm is pretty inefficient if the lines
  2638.      *    are much longer than the screen width, 
  2639.      *    but I don't know of any better way. }}
  2640.      */
  2641.     if (ch_seek(new_pos))
  2642.         return (NULL_POSITION);
  2643.     loop:
  2644.     begin_new_pos = new_pos;
  2645.     prewind();
  2646.  
  2647.     do
  2648.     {
  2649.         c = ch_forw_get();
  2650.         new_pos++;
  2651.         if (c == '\n')
  2652.             break;
  2653.         if (pappend(c))
  2654.         {
  2655.             /*
  2656.              * Got a full printable line, but we haven't
  2657.              * reached our curr_pos yet.  Discard the line
  2658.              * and start a new one.
  2659.              */
  2660.             (void) pappend('\0');
  2661.             (void) ch_back_get();
  2662.             new_pos--;
  2663.             goto loop;
  2664.         }
  2665.     } while (new_pos < curr_pos);
  2666.  
  2667.     (void) pappend('\0');
  2668.  
  2669.     return (begin_new_pos);
  2670. }
  2671. _SHAR_EOF_
  2672.  
  2673. echo output.c
  2674. cat >output.c <<'_SHAR_EOF_'
  2675. /*
  2676.  * High level routines dealing with the output to the screen.
  2677.  */
  2678.  
  2679. #include "less.h"
  2680.  
  2681. public int errmsgs;    /* Count of messages displayed by error() */
  2682.  
  2683. extern int sigs;
  2684. extern int sc_width, sc_height;
  2685. extern int ul_width, ue_width;
  2686. extern int so_width, se_width;
  2687. extern int bo_width, be_width;
  2688. extern int tabstop;
  2689. extern int twiddle;
  2690. extern int any_display;
  2691. extern char *line;
  2692. extern char *first_cmd;
  2693.  
  2694. /*
  2695.  * Display the line which is in the line buffer.
  2696.  */
  2697.     public void
  2698. put_line()
  2699. {
  2700.     register char *p;
  2701.     register int c;
  2702.     register int column;
  2703.     extern int auto_wrap, ignaw;
  2704.  
  2705.     if (sigs)
  2706.         /*
  2707.          * Don't output if a signal is pending.
  2708.          */
  2709.         return;
  2710.  
  2711.     if (line == NULL)
  2712.         line = (twiddle) ? "~" : "";
  2713.  
  2714.     column = 0;
  2715.     for (p = line;  *p != '\0';  p++)
  2716.     {
  2717.         switch (c = *p)
  2718.         {
  2719.         case UL_CHAR:
  2720.             ul_enter();
  2721.             column += ul_width;
  2722.             break;
  2723.         case UE_CHAR:
  2724.             ul_exit();
  2725.             column += ue_width;
  2726.             break;
  2727.         case BO_CHAR:
  2728.             bo_enter();
  2729.             column += bo_width;
  2730.             break;
  2731.         case BE_CHAR:
  2732.             bo_exit();
  2733.             column += be_width;
  2734.             break;
  2735.         case '\t':
  2736.             do
  2737.             {
  2738.                 putc(' ');
  2739.                 column++;
  2740.             } while ((column % tabstop) != 0);
  2741.             break;
  2742.         case '\b':
  2743.             putbs();
  2744.             column--;
  2745.             break;
  2746.         default:
  2747.             if (c & 0200)
  2748.             {
  2749.                 putc('^');
  2750.                 putc(c & 0177);
  2751.                 column += 2;
  2752.             } else
  2753.             {
  2754.                 putc(c);
  2755.                 column++;
  2756.             }
  2757.         }
  2758.     }
  2759.     if (column < sc_width || !auto_wrap || ignaw)
  2760.         putc('\n');
  2761. }
  2762.  
  2763. /*
  2764.  * Is a given character a "control" character?
  2765.  * {{ ASCII DEPENDENT }}
  2766.  */
  2767.     public int
  2768. control_char(c)
  2769.     int c;
  2770. {
  2771.     return (c < ' ' || c == '\177');
  2772. }
  2773.  
  2774. /*
  2775.  * Return the printable character used to identify a control character
  2776.  * (printed after a carat; e.g. '\3' => "^C").
  2777.  * {{ ASCII DEPENDENT }}
  2778.  */
  2779.     public int
  2780. carat_char(c)
  2781.     int c;
  2782. {
  2783.     return ((c == '\177') ? '?' : (c | 0100));
  2784. }
  2785.  
  2786.  
  2787. static char obuf[1024];
  2788. static char *ob = obuf;
  2789.  
  2790. /*
  2791.  * Flush buffered output.
  2792.  */
  2793.     public void
  2794. flush()
  2795. {
  2796.     write(1, obuf, ob-obuf);
  2797.     ob = obuf;
  2798. }
  2799.  
  2800. /*
  2801.  * Discard buffered output.
  2802.  */
  2803.     public void
  2804. dropout()
  2805. {
  2806.     ob = obuf;
  2807. }
  2808.  
  2809. /*
  2810.  * Output a character.
  2811.  */
  2812.     public void
  2813. putc(c)
  2814.     int c;
  2815. {
  2816.     if (ob >= &obuf[sizeof(obuf)])
  2817.         flush();
  2818.     *ob++ = c;
  2819. }
  2820.  
  2821. /*
  2822.  * Output a string.
  2823.  */
  2824.     public void
  2825. puts(s)
  2826.     register char *s;
  2827. {
  2828.     while (*s != '\0')
  2829.         putc(*s++);
  2830. }
  2831.  
  2832. /*
  2833.  * Output a message in the lower left corner of the screen
  2834.  * and wait for carriage return.
  2835.  */
  2836.  
  2837. static char return_to_continue[] = "  (press RETURN)";
  2838.  
  2839.     public void
  2840. error(s)
  2841.     char *s;
  2842. {
  2843.     register int c;
  2844.     static char buf[2];
  2845.  
  2846.     errmsgs++;
  2847.     if (!any_display)
  2848.     {
  2849.         /*
  2850.          * Nothing has been displayed yet.
  2851.          * Output this message on error output (file
  2852.          * descriptor 2) and don't wait for a keystroke
  2853.          * to continue.
  2854.          *
  2855.          * This has the desirable effect of producing all
  2856.          * error messages on error output if standard output
  2857.          * is directed to a file.  It also does the same if
  2858.          * we never produce any real output; for example, if
  2859.          * the input file(s) cannot be opened.  If we do
  2860.          * eventually produce output, code in edit() makes
  2861.          * sure these messages can be seen before they are
  2862.          * overwritten or scrolled away.
  2863.          */
  2864.         write(2, s, strlen(s));
  2865.         write(2, "\n", 1);
  2866.         return;
  2867.     }
  2868.  
  2869.     lower_left();
  2870.     clear_eol();
  2871.     so_enter();
  2872.     puts(s);
  2873.     puts(return_to_continue);
  2874.     so_exit();
  2875.  
  2876. #if ONLY_RETURN
  2877.     while ((c = getc()) != '\n' && c != '\r')
  2878.         bell();
  2879. #else
  2880.     c = getc();
  2881.     if (c != '\n' && c != '\r' && c != ' ')
  2882.     {
  2883.         buf[0] = c;
  2884.         first_cmd = buf;
  2885.     }
  2886. #endif
  2887.  
  2888.     if (strlen(s) + sizeof(return_to_continue) + 
  2889.         so_width + se_width + 1 > sc_width)
  2890.         /*
  2891.          * Printing the message has probably scrolled the screen.
  2892.          * {{ Unless the terminal doesn't have auto margins,
  2893.          *    in which case we just hammered on the right margin. }}
  2894.          */
  2895.         repaint();
  2896. }
  2897.  
  2898. #ifdef notdef
  2899.     public int
  2900. error_width()
  2901. {
  2902.     /*
  2903.      * Don't use the last position, because some terminals
  2904.      * will scroll if you write in the last char of the last line.
  2905.      */
  2906.     return (sc_width - 
  2907.         (sizeof(return_to_continue) + so_width + se_width + 1));
  2908. }
  2909. #endif
  2910. _SHAR_EOF_
  2911.  
  2912.  
  2913.  
  2914.