home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / flistfrontend / src / browse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-27  |  49.4 KB  |  2,081 lines

  1. #ifndef NO_IDENT
  2. static char *Id = "$Id: browse.c,v 1.22 1995/10/27 13:31:04 tom Exp $";
  3. #endif
  4.  
  5. /*
  6.  * Title:    BROWSE.c (BROWSE main program)
  7.  * Author:    T. Dickey
  8.  *        (This is a completely re-written version of a program
  9.  *        titled BROWSE, which was written by L.Seeton, 06-Nov-1983).
  10.  * Created:    16 Apr 1984
  11.  * Last update:
  12.  *        27 Oct 1995, release-version
  13.  *        22 Oct 1995, DEC-C clean-compile
  14.  *        28 May 1995, use stdarg instead of VARARGS hack.
  15.  *        27 May 1995, prototyped
  16.  *        18 Feb 1995    port to AXP (renamed 'alarm').
  17.  *        24 Feb 1989    disable /COMMAND when invoked from FLIST.
  18.  *                added LOGFILE/LOGARGS hooks to integrate with
  19.  *                FLIST's /COMMAND and /LOG options.
  20.  *        22 Aug 1988    Forgot to check for no file at all!
  21.  *        07 Oct 1985    Added 80/132-column switch 'G'.
  22.  *        17 Aug 1985    Corrected case of overstrike of text on space.
  23.  *        15 Aug 1985    Permit value on /SQUEEZE
  24.  *        29 Jul 1985    Interpret VT100 keypad.
  25.  *        18 Jul 1985    Gave up on using 'rmscc' (file attributes) to
  26.  *                set implicit value for /JOIN, since we have
  27.  *                often files from tape w/o carriage-control.
  28.  *        03 Jul 1985    Use 'scanint' instead of 'sscanf' to bypass bug
  29.  *                in CC2.0
  30.  *        15 Jun 1985    Added '&' in 'opts[]' declaration to make
  31.  *                CC2.0 happy.
  32.  *        27 Apr 1985    Broke out read-command with 'edtcmd' stuff.
  33.  *        12 Apr 1985    Added 'gotraw' call to make buffer-input faster.
  34.  *                Use VARARGS in 'warn'.
  35.  *        10 Apr 1985    Added 'clrwarn()' call so that if we get an
  36.  *                error (e.g., in /COMMAND) which does not normally
  37.  *                halt operation, then FLIST won't have its
  38.  *                refresh-disable set.
  39.  *        06 Apr 1985    redid search-abort as CTRL/X-ast.  Added
  40.  *                /COMMAND-option (given support in 'getraw').
  41.  *        28 Mar 1985    added 'status' argument to 'error'.
  42.  *        26 Mar 1985    RELEASE-2: v923
  43.  *        20 Mar 1985    Changed to a conventional (VMS-style) help-display.
  44.  *        14 Mar 1985    renamed the release-version of this to BROWSE
  45.  *        02 Mar 1985    changed /OVER to 2-state toggle, with an
  46.  *                argument which permits any state to be set.
  47.  *                Permit highlighting of space overstruck by a
  48.  *                nonnull/nonblank.
  49.  *        18 Feb 1985    fixed 'offset[0]' latching; indexed files do
  50.  *                not necessarily start with rfa=0.
  51.  *        15 Feb 1985    alter /JOIN to look for trailing LF (not CR).
  52.  *        13 Feb 1985    use 'calloc' for static buffers.  Moved crt-init
  53.  *                so that if we can't look at file, then we won't
  54.  *                clear the screen.
  55.  *        12 Feb 1985    added 'erstat' call to check on record-errors.
  56.  *                Added 'more_name', to restrict length of name
  57.  *                in summary-line.
  58.  *        02 Feb 1985    use scrolling-margins
  59.  *        26 Jan 1985    cleanup use of sound_alarm in out-of-bounds scroll.
  60.  *        20 Jan 1985    corrected for zero-length records in /SQUEEZE.
  61.  *        19 Jan 1985    make repeat-count work with U,D,F,B commands.
  62.  *                Added I- and J-commands, also /TRIM option.
  63.  *        17 Jan 1985    make "_" command something we can use with the
  64.  *                other commands.  Also, recognize CTRL/K, CTRL/W
  65.  *                in the read-buffer routine.
  66.  *        02 Jan 1985    Use 'rmscc' to set default on '/JOIN'.  Added
  67.  *                'Z' as synonym for QUIT.
  68.  *        26 Dec 1984    Changed call on 'dclwild'.
  69.  *        04 Dec 1984    Added 'X', to abort search/skip.  Use 'putraw',
  70.  *                'whoami'.  Added '/JOIN', '/SQUEEZE' options.
  71.  *        06 Nov 1984    Re-wrote search-compare, using 'strvcmp'.
  72.  *                This permits BROWSE to find overlapping search-
  73.  *                targets (as well as fix a reset-bug).
  74.  *        23 Oct 1984    (had stepped on trailing-spaces)
  75.  *        21 Oct 1984    pad the record-size allocation
  76.  *        20 Oct 1984    cleanup of '^' transition, record-length.
  77.  *        15 Oct 1984    added 'snapshot', version
  78.  *        13 Oct 1984    completed multi-line record coding.
  79.  *        29 Sep 1984    began re-code to permit record to span line
  80.  *        27 Sep 1984    added 'col_base' logic to fix ^L display bug.
  81.  *                Corrected combination-display of FIND_?.
  82.  *        21 Sep 1984    permit 3-state O-flag, added M-flag toggle
  83.  *        20 Sep 1984    add logic to do space-independent search
  84.  *        17 Sep 1984    added O-flag toggle
  85.  *        15 Sep 1984    eliminated dependency on 'stderr'
  86.  *        11 Sep 1984    corrected position of FIND_R marker, broke out
  87.  *                'fgetr' code while setting up RMSIO package,
  88.  *                use macro-form of 'toupper'.
  89.  *        23 Aug 1984    Added numeric argument (for fractional L/R)
  90.  *        25 Jul 1984    Added 'cmd_arg' to 'argvdcl()' call.  Show
  91.  *                filename w/o pathname if current default.
  92.  *        13 Jul 1984    Use DCLOPT for options-parsing
  93.  *        10 Jul 1984    No ^C command.  Show filename in blurb.
  94.  *        04 Jul 1984    Use 'argvdcl()' to better parse argv.
  95.  *                Fixed bug in backward search.
  96.  *        27 Jun 1984
  97.  *        20 Jun 1984    Cannot mix Bitgraph with FLIST (main_incl)
  98.  *        18 Jun 1984    completed crt modification
  99.  *        31 May 1984    (TED) Began re-code to use 'crt.c' module
  100.  *        29 May 1984    (TED) Optimized user-buf read.
  101.  *        27 May 1984    (TED) Adjustments to "/" search.
  102.  *        22 May 1984    (TED) Use 'getpad' name to combine with FLIST.
  103.  *        12 May 1984    (TED) Further altered getraw, error-return.
  104.  *        07 May 1984    (TED) Altered (using MAIN-define) this source
  105.  *                to permit it to be used as a subroutine, or
  106.  *                as standalone source.
  107.  *        26 Apr 1984    (TED) trim lines before printing them.  Allow
  108.  *                more than 1 file to be inspected.
  109.  *        18 Apr 1984    (TED) altered search command to show progress
  110.  *        17 Apr 1984    (TED) added help message, "o" option, search cmd.
  111.  *        16 Apr 1984    (TED), added L/R scrolling, Bitgraph option,
  112.  *                rewrote to use DEC library routines to obtain
  113.  *                the actual terminal characteristics, and to do
  114.  *                correct VT52/VT100 cursor movement.
  115.  *
  116.  * Notes:    BROWSE does both left/right and up/down scrolling.  The total
  117.  *        number of lines on the CRT screen is assumed even, so that
  118.  *        when the status line is deducted, the remaining part is odd.
  119.  *        This logic is implicit in the up/down scrolling, which overlaps
  120.  *        each marked section by one line (e.g., scrolling forward one
  121.  *        screen keeps one line the same).
  122.  */
  123.  
  124. #include    <stdio.h>    /* for 'sprintf()' */
  125. #include    <signal.h>    /* for 'sleep()' */
  126.  
  127. #include    <stdlib.h>
  128. #include    <stdarg.h>
  129. #include    <string.h>
  130.  
  131. #include    "rmsio.h"
  132. #include    <ctype.h>
  133. #include    <stsdef.h>
  134.  
  135. #include    "bool.h"
  136. #include    "cmdstk.h"
  137. #include    "edtcmd.h"
  138. #include    "getpad.h"
  139. #include    "getraw.h"
  140.  
  141. #include    "dclarg.h"
  142. #include    "dclopt.h"
  143. #include    "freelist.h"
  144. #include    "names.h"
  145. #include    "dspc.h"
  146. #include    "whoami.h"
  147.  
  148. #include    "strutils.h"
  149.  
  150. #ifdef main_incl
  151. #include    "flist.h"
  152. #else
  153. #define    CTL(c)    (037 & c)
  154. #include    "warning.h"
  155. #endif
  156.  
  157. static    int    more_0_bg (short *lpp_, short *width_);
  158. static    int    more_0_vt (short *lpp_, short *width_);
  159. static    void    more_0_line (void);
  160. static    void    more_0_mark (void);
  161. static    void    more_0_page (void);
  162. static    void    more_char (char c);
  163. static    void    more_conv (void);
  164. static    void    more_ctlc (char *ctl_);
  165. static    void    more_down (int frac, int rpt);
  166. static    void    more_file (void);
  167. static    void    more_getr (void);
  168. static    int    more_lastp (void);
  169. static    int    more_limit (int val, int lo, int hi);
  170. static    void    more_line (void);
  171. static    int    more_move (int dy, int dx);
  172. static    int    more_msg (char *c_, int last);
  173. static    char*    more_name (int maxlen);
  174. static    void    more_next (int new);
  175. static    void    more_null (int inx);
  176. static    int    more_r_buf (char *co_, char *m1_, char *delim_);
  177. static    int    more_r_cmd (void);
  178. static    int    more_read (int rec);
  179. static    void    more_right (void);
  180. static    int    more_round (int num, int d);
  181. static    void    more_screen (int view_size, int dirflg);
  182. static    void    more_seek (int next);
  183. static    int    more_size (int over);
  184. static    int    more_skip (char *find_);
  185. static    void    more_this (void);
  186.  
  187. #ifdef DEBUG
  188. static    void    more_show (char *format, ...);
  189. #endif
  190.  
  191. /*
  192.  * Local (static) data:
  193.  */
  194. #define    MAX_PAGE    99999    /* maximum value for half-screen index    */
  195. #define    MAXREC        999999    /* maximum record # this program reads    */
  196. #define    MAXBFR        513    /* size of input/output buffers        */
  197. #define    MAXVEC        1024    /* granularity of seek-mark array    */
  198.  
  199. #define    MAXCOL        (colmax-7)    /* threshold used in ruler-mode    */
  200.  
  201.     /* Index to 'offset[]' for next, current previous pages */
  202. #define    TOP_NEXT    (now)
  203. #define    TOP_THIS    (now-2)
  204. #define    TOP_PREV    (now-4)
  205.  
  206.     /* Record # (0,1,2,...) at 'offset[]' position:    */
  207. #define    LINEMARK(n)    (HalfPage*(n))
  208.  
  209.     /* Derived line-number, from 'inpage':            */
  210. #define    INPAGE_LINE    (inpage + BaseLine)
  211. #define    INFILE_LINE    (crt_top() + INPAGE_LINE)
  212.  
  213.     /* Mark-index of last half-screen:            */
  214. #define    END_MARK    ((last_page >= 0) ? end_mark : MAX_PAGE)
  215.  
  216.     /* Mark-index of last full (even) page:            */
  217. #define    LAST_PAGE    ((last_page >= 0) ? last_page : end_mark)
  218.  
  219. #define    FIND_L    1    /* if off-screen on left        */
  220. #define    FIND_R    2    /* if off-screen on right        */
  221. #define    FIND_IN    4    /* if any in-screen            */
  222. #define    FIND_XX    8    /* debug: show half-screen marks    */
  223.  
  224. #define    STRVCMP    strvcmp(find_bfr, &i_bfr[inx], lstate_end-inx)
  225.  
  226. #define    BELL    CTL('G')
  227. #define    RUBOUT    '\177'
  228. #define    toshift(c)    (iscntrl(c) ? ((c) | 0100) : (c))
  229.  
  230. #define    MARK_WIDTH    13    /* Columns used for 'M_OPT'    */
  231.  
  232. #define    SHOW_OFF(n)    (n),offset[n].rfa,offset[n].cra
  233.  
  234. static    RFILE    *fp;
  235.  
  236. typedef    struct    {
  237.     unsigned rfa;    /* Record's file-address (direct 'ftell/fseek')    */
  238.     short    cra;    /* Character's record-address (maintained here)    */
  239.     short    rec;    /* Actual record number                */
  240.     } OFFSET;
  241. static
  242. OFFSET    *offset,    /* 'ftell' positions, for retyping        */
  243.     rstate;        /* State of 'more_read', to extract line    */
  244.  
  245.             /* State of current page-characters        */
  246. static
  247. int            /* Terminal characteristics:            */
  248.     lpp,        /* maximum lines/page                */
  249.     width,        /* screen width                    */
  250.     width2,        /* 1/2, for scrolling left/right        */
  251.     did_crt_init = FALSE,
  252.  
  253.     M_option,    /* TRUE iff "/MARK" set originally        */
  254.     O_option,    /* TRUE iff "/OVER" set originally        */
  255.  
  256.     /*
  257.      * Miscellaneous flags:
  258.      */
  259.     user_cmd,    /* (main-case): most recent command read    */
  260.     user_arg,    /* " " " numeric argument, if given        */
  261.     end_flag,    /* TRUE if current page is last in file        */
  262.  
  263.     find_flg,    /* Set by 'STRVCMP', to number of chars to show    */
  264.     findmark,    /* Set iff find_bfr in current line        */
  265.     mark_width,    /* Actual number of columns used if /MARK    */
  266.     multiline,    /* TRUE iff a multi-line record was found    */
  267.  
  268.     FullPage,    /* number of text lines in screen        */
  269.     HalfPage,    /* number of text lines in half-screen        */
  270.     BaseLine,    /* Adjustment after half-screen scroll        */
  271.  
  272.     now,        /* Index to 'offset[]' for current page     */
  273.     end_mark,    /* Highest known index to 'offset[]'        */
  274.     last_page,    /* ...index of last full page            */
  275.     last_line,    /* ...last display-line number in file        */
  276.     maxvec,        /* Number of locations allocated in 'offset[]'    */
  277.  
  278.     i_recl,        /* maximum record length of input        */
  279.     i_size,        /* ... corresponding buffer size        */
  280.     i_line,        /* Line number in file (for display/read)    */
  281.  
  282.     rstate_len,    /* length of current input record        */
  283.     lstate_len,    /* length of portion to be displayed        */
  284.     lstate_1st,    /* ...and index to portion to display        */
  285.     lstate_end,    /* ...and index above last char            */
  286.  
  287.     /*
  288.      * State of ruler-mode:
  289.      */
  290.     ruler_mode,    /* TRUE if we display a scale in status line    */
  291.     ruler_min,    /* First column of present ruler-text        */
  292.     ruler_max,    /* Last column of present ruler-text        */
  293.     ruler_x,    /* cursor-position: x = column            */
  294.     ruler_y,    /* cursor-position: y = row            */
  295.  
  296.     /*
  297.      * State of output display:
  298.      */
  299.     colmin, colmax,    /* limits on columns to display            */
  300.     column,        /* column # within line buffer            */
  301.     colend,        /* highest column # in page            */
  302.     *endcol,    /* end-column for each display line        */
  303.  
  304.     inpage,        /* line number, within page            */
  305.     onpage;        /* printing-char's read in current page        */
  306.  
  307. static
  308. DCLARG    *WhatIsIt;    /* => name of file to display            */
  309.  
  310. static
  311. char    *i_bfr,        /* = input record                */
  312.     *o_bfr,        /* = current text                */
  313.     *obfr_,        /* => o_bfr, current character            */
  314.     *sbfr_,        /* => find_bfr, current search-try        */
  315.     *find_bfr,
  316.     *ruler_text,    /* Ruler text                */
  317.     *fatal_msg;    /* Set iff I/O error            */
  318.  
  319. /*
  320.  * Define option keywords and their flags:
  321.  */
  322. static
  323. int    B_opt,        /* Init for Bitgraph terminal        */
  324.     D_opt,        /* debug (values 1,2 or logical)    */
  325.     J_opt,        /* Join records ending in CR        */
  326.     O_opt,        /* show \r, \b codes            */
  327.     M_opt,        /* show ftell marks at each line    */
  328.     S_opt,        /* limit blank lines to double-space    */
  329.     T_opt,        /* ignore trailing blanks in J-command    */
  330.     W_opt,        /* wide (132-column) mode        */
  331.     _FALSE    = FALSE,
  332.     _MAYBE    = (-1);
  333. static
  334. char    CmdFile[MAX_PATH],
  335.     CmdDft[] = "SYS$LOGIN:BROWSE.CMD";
  336.  
  337. #define    SZ(n)    &n,sizeof(n)
  338. static
  339. DCLOPT    opts[] = {
  340.     {"bg",        &_FALSE,    0,    SZ(B_opt),     1, 00001},
  341.     {"command",    0,        CmdDft,    SZ(CmdFile),    1, 00200},
  342.     {"debug",    &_FALSE,    0,    SZ(D_opt),     1, 00002},
  343.     {"join",    &_FALSE,    0,    SZ(J_opt),     1, 00020},
  344.     {"marks",    &_FALSE,    0,    SZ(M_opt),     1, 00004},
  345.     {"over",    &_MAYBE,    0,    SZ(O_opt),     1, 00010},
  346.     {"squeeze",    &_MAYBE,    0,    SZ(S_opt),     1, 00040},
  347.     {"trim",    &_FALSE,    0,    SZ(T_opt),     1, 00100},
  348.     {"wide",    &_FALSE,    0,    SZ(W_opt),     1, 00001}
  349.     };
  350.  
  351. #ifdef    main_incl
  352. #define    LOGFILE(s)    flist_log s;
  353. #define    LOGARGS(c,n)    logfile(c,n)
  354. static void logfile(int cmd, int count);
  355.  
  356. main_incl
  357. #else
  358. #define    LOGFILE(s)
  359. #define    LOGARGS(c,n)    n
  360. main
  361. #endif
  362.  
  363. (
  364. int    argc,
  365. char    **argv)
  366. {
  367. #ifdef    main_incl
  368.     DCLARG    *dcl_    = argvdcl (argc, argv, "", 2);
  369. #else
  370.     DCLARG    *dcl_    = argvdcl (argc, argv, "", 0);
  371. #endif
  372.  
  373.     DCLARG    *opt_;
  374.     int    (*if_bg)(short *lpp, short *width) = more_0_vt;
  375.     char    *c_,
  376.         msg    [MAXBFR];
  377.  
  378.     WhatIsIt = 0;
  379.  
  380.     if (dclchk (dcl_, msg))
  381.     {
  382.         warn (msg);
  383.         return;
  384.     }
  385. #ifdef    main_incl
  386.     dcl_ = dcl_->dcl_next;    /* Skip over program-name token    */
  387. #endif
  388.  
  389.     if (dclopt (msg, dcl_, (DCLOPT *)&opts, sizeof(opts)))
  390.     {
  391.         warn (msg);
  392.         return;
  393.     }
  394. #ifndef    main_incl
  395.     getraw_init (CmdFile, CmdDft);
  396. #endif
  397.  
  398.     if (B_opt)    if_bg    = more_0_bg;
  399.     if (D_opt)    M_opt    = TRUE;
  400.     M_option = M_opt;    /* Save initial "/MARK" */
  401.  
  402.     /*
  403.      * Restrict /OVER to legal values, saving initial value in 'O_option':
  404.      *    0 - no control-character interpretation
  405.      *    1 - show \r as ^M, \b as ^H
  406.      *    2 - show \r as <CR>, \b as <BS>
  407.      *    3 - show all control characters as '.'
  408.      */
  409.     if (O_opt > 3)    O_opt = 3;
  410.     O_option = O_opt;
  411.     if (O_option < 0)
  412.     {
  413.         O_option = 1;
  414.         O_opt = 0;
  415.     }
  416.  
  417.     for (opt_ = dcl_; opt_; opt_ = opt_->dcl_next)
  418.     {
  419.         if (! isopt(opt_->dcl_text[0]))
  420.         {
  421.             if (! (WhatIsIt || dclwild (opt_)))
  422.                 WhatIsIt = opt_;
  423.             else
  424.             {
  425.                 warn ("You must specify a single file");
  426.                 freelist (dcl_);
  427.                 return;
  428.             }
  429.         }
  430.     }
  431.  
  432.     fatal_msg  = calloc(1, CRT_COLS);
  433.  
  434.     if (!WhatIsIt) {
  435.         warn ("No file specified");
  436.         freelist (dcl_);
  437.         return;
  438.     }
  439.  
  440.     if ((fp = ropen (WhatIsIt->dcl_text, "r")))
  441.     {
  442.     CMDSTK    *oldcmd_ = cmdstk_init ();
  443. #ifdef main_incl
  444.     int    oldtop = crt_top(),
  445.         oldend = crt_end();
  446.  
  447.         crt_set (TRUE, 0);
  448.         crt_set (FALSE, 0);
  449. #else
  450.         crt_init (if_bg);
  451.         edtcmd_init ();
  452.         did_crt_init = TRUE;
  453.         crt_margin (1, crt_lpp()-1);
  454. #endif
  455.         lpp    = crt_lpp ();
  456.         multiline = FALSE;
  457.         more_0_mark ();
  458.  
  459.         i_recl    = rsize(fp);
  460.         i_size    = i_recl + 4;
  461.  
  462.         if ((J_opt > 0) || (S_opt >= 0)) i_size <<= 2;
  463.                 /* Allow extra if merge */
  464.  
  465.         i_bfr    = calloc(1, i_size);
  466.         obfr_    =
  467.         o_bfr    = calloc(1, CRT_COLS);
  468.         find_bfr    = calloc(1, CRT_COLS);
  469.         ruler_text    = calloc(1, CRT_COLS);
  470.         offset    = calloc(maxvec = MAXVEC, sizeof(OFFSET));
  471.         endcol    = calloc(crt_lpp(), sizeof(endcol[0]));
  472.  
  473.         more_file ();
  474.         rclose (fp);
  475.  
  476.         cfree (i_bfr);
  477.         cfree (o_bfr);
  478.         cfree (find_bfr);
  479.         cfree (ruler_text);
  480.         cfree (offset);
  481.         cfree (endcol);
  482.  
  483. #ifdef    main_incl
  484.         crt_set (TRUE,  oldtop);
  485.         crt_set (FALSE, oldend);
  486.         clrwarn ();        /* Reset FLIST's screen-latch */
  487. #else
  488.         crt_quit (TRUE);
  489. #endif
  490.         cmdstk_free (oldcmd_);
  491.     }
  492.     else
  493.     {
  494.         erstat (fp, fatal_msg, CRT_COLS);
  495.         warn (fatal_msg);
  496.     }
  497.     cfree (fatal_msg);
  498.     freelist (dcl_);
  499. }
  500.  
  501. /* <name>:
  502.  * Given a display-buffer limit, attempt to show as much as possible of the
  503.  * VMS filename.
  504.  */
  505. static
  506. char    *more_name (int maxlen)
  507. {
  508.     register char    *c_ = WhatIsIt->dcl_text;
  509.     register int    len = strlen (c_);
  510.     static    char    bfr[CRT_COLS];
  511.  
  512.     if (len <= maxlen)    return (c_);
  513. #define    ADJ(x)    c_ += WhatIsIt->x; len -= WhatIsIt->x;\
  514.     if (len <= maxlen) return (c_);
  515.  
  516.     ADJ(dcl$b_node);
  517.     ADJ(dcl$b_dev);
  518.     ADJ(dcl$b_dir);
  519.     strncpy (bfr, c_, len-4);
  520.     strcpy (&bfr[len-4], " ...");
  521.     return (c_);
  522. }
  523.  
  524. /* <file>:
  525.  * Do all processing for a single file:
  526.  */
  527. static
  528. void    more_file (void)
  529. {
  530.     int    j,
  531.         forward    = TRUE;
  532.  
  533.     find_bfr[0] = EOS;
  534.  
  535.     FullPage  = lpp - 1;        /* Expect 23 lines, normal display */
  536.     HalfPage  = FullPage / 2;    /* ...line #11 is middle of 0..22 */
  537.     width2      = width / 2;
  538.  
  539.     colmin      = 0;
  540.     TOP_NEXT  = -1;            /* First read will be '0'    */
  541.     multiline = FALSE;        /* Assume one line per record    */
  542.  
  543.     i_line      = 0;            /* ...line #0            */
  544.     last_page = -1;            /* If minus, haven't found EOF    */
  545.     last_line = MAXREC;        /* Allow a large number of rec's*/
  546.     end_mark  = 0;
  547.     end_flag  = FALSE;
  548.  
  549.     rstate.rec= 0;            /* ...record #0            */
  550.     rstate.cra= -1;            /* Force first record-read    */
  551.     rstate.rfa= 0;
  552.     rstate_len = lstate_len = lstate_end = 0;
  553.     lstate_1st = -1;
  554.  
  555.     ruler_mode = ruler_x = ruler_y = 0;
  556.     ruler_min = ruler_max = -1;
  557.  
  558.     crt_set (FALSE, crt_top() + FullPage-1); /* Init the scroller    */
  559.     more_screen (FullPage, 0);
  560.  
  561.     while (TRUE)
  562.     {
  563.     int    gold    = FALSE;
  564.         do
  565.         {
  566.         user_cmd = more_r_cmd();
  567.         gold    = gold || (user_cmd == GOLDKEY);
  568.         }
  569.         while (user_cmd == GOLDKEY);
  570.  
  571.         if (! isascii(user_cmd))
  572.         {
  573.         int    j;
  574.         struct {
  575.         int    input,    normal,    shifted;
  576.         } KEYPAD[] = {
  577.             {padENTER,    'M',    'M'},
  578.             {HELPKEY,    '?',    '?'},
  579.             {padPF3,    'N',    '/'},
  580.             {padPF4,    'P',    '\\'},
  581.             {pad0,        'H',    'H'},
  582.             {pad1,        'L',    'L'},
  583.             {pad2,        'R',    'R'},
  584.             {pad3,        'J',    'J'},
  585.             {pad4,        pad4,    'E'},
  586.             {pad5,        pad5,    'T'},
  587.             {pad6,        'I',    'I'},
  588.             {pad9,        '_',    '_'}
  589.             };
  590.  
  591.         if (user_cmd == pad7)
  592.             user_cmd = forward ? 'F' : 'B';
  593.         else if (user_cmd == pad8)
  594.             user_cmd = forward ? 'D' : 'U';
  595.         else for (j = 0; j < SIZEOF(KEYPAD); j++)
  596.         {
  597.             if (KEYPAD[j].input == user_cmd)
  598.             {
  599.             user_cmd = gold ? KEYPAD[j].shifted : KEYPAD[j].normal;
  600.             break;
  601.             }
  602.         }
  603.         }
  604.  
  605.         switch (user_cmd)
  606.         {
  607.         case pad4:    forward = TRUE;        break;
  608.         case pad5:    forward = FALSE;    break;
  609.  
  610.         case 'Z':
  611.         case 'Q':            /* Quit                */
  612.         LOGARGS('Z',0);
  613.         getraw_flush();
  614.         return;
  615.  
  616.         case padUP:
  617.         if (more_move (-1, 0))    break;
  618.         case 'U':            /* Display previous 1/2 screen    */
  619.         LOGARGS('U', user_arg = max(1, user_arg));
  620.         if (TOP_THIS > 0)
  621.             more_next (TOP_THIS - user_arg);
  622.         else
  623.             sound_alarm ();
  624.         break;
  625.  
  626.         case ' ':            /* (to look like Unix-more)    */
  627.         case 'F':            /* Scroll forward 1 screen    */
  628.             LOGARGS('F', user_arg = max(1, user_arg));
  629.             more_down (2, user_arg);
  630.         break;
  631.  
  632.         case padDOWN:
  633.         if (more_move(1,0))    break;
  634.         case 'D':            /* Display another 1/2 screen    */
  635.         LOGARGS('D', user_arg = max(1, user_arg));
  636.         more_down (1, user_arg);
  637.         break;
  638.  
  639.         case 'T':            /* Return to top-of-file    */
  640.         LOGARGS('T',0);
  641.         if (TOP_THIS > 0)
  642.             more_next (0);
  643.         else if (!ruler_y)
  644.             sound_alarm ();
  645.         ruler_y = 0;
  646.         break;
  647.  
  648.         case 'B':            /* Back up one page        */
  649.         LOGARGS('B', user_arg = max(1, user_arg));
  650.         if (TOP_THIS > 0)
  651.             more_next (TOP_THIS - (2 * user_arg));
  652.         else
  653.             sound_alarm();
  654.         break;
  655.  
  656.         case 'E':            /* Go to end of file        */
  657.         LOGARGS('E',0);
  658.         if (last_page < 0)
  659.         {
  660.             int    old = TOP_NEXT;
  661.             while (last_page < 0 && ! ctlx_tst()) more_skip(nullC);
  662.             more_seek (old);    /* fix scroll */
  663.         }
  664.         if ((j = more_lastp()) != TOP_THIS)
  665.             more_next (j);
  666.         else
  667.             sound_alarm();
  668.         ruler_y = min(last_line-1, crt_end());
  669.         break;
  670.  
  671.         /* patch: user_arg not used! */
  672.         case '\\':            /* Search backward for a string    */
  673.         case 'P':            /* ...search for prev occurrence*/
  674.  
  675.         case '/':            /* Search for a string        */
  676.         case 'N':            /* ...search for next occurrence */
  677.         if (ispunct(user_cmd))
  678.         {
  679.             find_bfr[0] = EOS;
  680.             more_r_buf (find_bfr, "Search for:", nullC);
  681.             LOGFILE(("  $ %c%s", user_cmd, find_bfr))
  682.             strvcpy (find_bfr, nullC);
  683.             if (*find_bfr)    cmdstk_put (find_bfr);
  684.         }
  685. #ifdef    main_incl
  686.         else {
  687.             LOGARGS(user_cmd,user_arg);
  688.         }
  689. #endif/*main_incl*/
  690.         if (find_bfr[0] == EOS)
  691.         {
  692.             more_this ();    /* (clear old markers) */
  693.             sound_alarm ();
  694.         }
  695.         else if (user_cmd == 'P' && TOP_THIS <= 0)
  696.             sound_alarm();
  697.         else
  698.         {
  699.         int    fwd = (user_cmd == '/' || user_cmd == 'N'),
  700.             new, next,
  701.             old = TOP_NEXT,
  702.             got = FALSE;
  703.         char    *c_ = find_bfr;
  704.  
  705.             /*
  706.              * If first, start on current page.
  707.              */
  708.             switch (user_cmd)
  709.             {
  710.             case '/':
  711.             more_seek (max(0,TOP_THIS));
  712.             break;
  713.             case '\\':
  714.             more_seek (new = TOP_NEXT-1);
  715.             break;
  716.             case 'P':
  717.             more_seek (new = TOP_THIS-1);
  718.             }
  719.  
  720.             while (! (end_flag || ctlx_tst()))
  721.             {
  722.             if (got = more_skip (find_bfr))
  723.                 break;
  724.             if (! fwd)
  725.             {
  726.                 if (--new >= 0)
  727.                 more_seek (new);
  728.                 else
  729.                 break;
  730.             }
  731.             }
  732.  
  733.             /*
  734.              * If found, get pointer to top of half-page containing
  735.              * string, scroll to that screen.  The index 'now' is set
  736.              * to the mark at the bottom of the half-screen containing
  737.              * the string, if 'N', else top.
  738.              */
  739.             if (got)
  740.             {
  741.             new = TOP_NEXT;
  742.             if (user_cmd == 'N')    new--;
  743.             more_seek (old); /* fix scroll */
  744.             next = new;
  745.                 /* Prefer full-screen scrolling: */
  746.             if ((old-1) == next)    next = old - 2;
  747.             if ((old-3) == next)    next = old - 4;
  748.                 /* Restrict to legal limits: */
  749.             next = max(0, min(next, more_lastp()));
  750. #ifdef    DEBUG
  751.             more_show ("(...)got:%d old:%d new:%d =>%d",
  752.                     got, old, new, next);
  753. #endif
  754.             more_next (next);
  755.             }
  756.             else
  757.             {
  758.             TOP_NEXT = old;
  759.             more_this ();    /* refresh old    */
  760.             sound_alarm ();
  761.             }
  762.         }
  763.         break;
  764.  
  765.         case '^':            /* Toggle debug-mode        */
  766.         LOGARGS('^',0);
  767.         if (!D_opt && !M_opt)
  768.         {
  769.             M_opt = 1;
  770.             more_0_mark ();
  771.             more_this ();
  772.         }
  773. #ifdef    DEBUG
  774.         D_opt = ! D_opt;
  775. #endif
  776.         break;
  777.  
  778.         case 'O':            /* Toggle overlay mode        */
  779.         LOGARGS('O', user_arg);
  780.         if (user_arg <= 3)    /* (maximum code permitted)    */
  781.         {
  782.             if (user_arg)
  783.             O_opt = user_arg;
  784.             else
  785.             {
  786.             if (O_opt)    O_opt = 0;
  787.             else        O_opt = O_option;
  788.             }
  789.             more_this ();
  790.         }
  791.         else
  792.             sound_alarm();
  793.         break;
  794.  
  795.         case 'M':            /* Toggle mark-display mode    */
  796.         LOGARGS('M', user_arg);
  797.         if (M_option || M_opt)
  798.         {
  799.             if (M_opt == 0)        M_opt = 1; /* mark   */
  800.             else if (M_opt == 1)    M_opt = 2; /* length */
  801.             else            M_opt = 0; /* off    */
  802.             more_0_mark ();
  803.             more_this ();
  804.         }
  805.         else
  806.             sound_alarm ();
  807.         break;
  808.  
  809.         case 'K':            /* Make a snapshot of screen    */
  810.         LOGARGS('K',0);
  811.         snapshot ();
  812.         break;
  813.  
  814.         case 'V':            /* Display current version    */
  815.         LOGARGS('V',0);
  816.         more_msg ("BROWSE (v951027)", TRUE);
  817.         sleep (3);
  818.         break;
  819.  
  820.         case 'G':            /* Toggle 80/132 columns    */
  821.         LOGARGS('G',0);
  822.         {
  823.             int width, length;
  824.             termsize (FALSE, &width, &length);
  825.             if (width <= 80)    width = 132;
  826.             else        width = 80;
  827.             if (! termsize (TRUE, &width, &length))    break;
  828.         }
  829.         case 'W':            /* Refresh current window    */
  830.         crt_clear ();
  831.         more_this ();
  832.         break;
  833.  
  834.         case 'H':            /* scroll left to home-column    */
  835.         LOGARGS('H', user_arg = max(0, user_arg-1));
  836.         if (colmin != user_arg && more_size(O_opt) > width)
  837.         {
  838.             colmin = user_arg;
  839.             ruler_x = 0;
  840.             more_this ();
  841.         }
  842.         else if (ruler_x)    /* Force ruler-position if we    */
  843.             ruler_x = 0;    /* ...are alreay in that screen    */
  844.         else if (ruler_mode)    /* Try to move up one row    */
  845.         {
  846.             if (!more_move (-1, 0))
  847.             {
  848.             if (TOP_THIS > 0)    more_next (TOP_THIS-1);
  849.             else            sound_alarm();
  850.             }
  851.         }
  852.         else
  853.             sound_alarm();
  854.         break;
  855.  
  856.         case 'I':            /* tab right (ruler only)    */
  857.         LOGARGS('I', j = max(0, user_arg-1));
  858.         user_arg = 0;        /* kill the repeat-factor    */
  859.         if (ruler_mode)
  860.         {
  861.             if (!more_move (0, (ruler_x | 7) + 1 + (8 * j) - ruler_x))
  862.             more_right();
  863.         }
  864.         else
  865.             sound_alarm();
  866.         break;
  867.  
  868.         case 'J':            /* tab to end-column        */
  869.         LOGARGS('J', user_arg);
  870.         if (ruler_mode)
  871.         {
  872.             j = endcol[ruler_y - crt_top()];
  873.             j = max(0, j-1);
  874.             if (ruler_x == j)    /* repeated-action scrolls down    */
  875.             {
  876.             if (!more_move (1, 0))
  877.                 more_down(1, user_arg = max(1, user_arg));
  878.             user_arg = 0;
  879.             }
  880.             j = endcol[ruler_y - crt_top()];
  881.             j = max(0, j-1);
  882.             if (!more_move (0, j - ruler_x))
  883.             more_right();
  884.         }
  885.         else if (colend < colmin || colend > colmax-1
  886.         ||    (colmin && colend < colmin + width2))
  887.         {
  888.             colmin = width2 * ((colend - width2 - 1) / width2);
  889.             if (colmin < 0)    colmin = 0;
  890.             more_this();    /* Scroll to screen containing end
  891.                        of longest record in display    */
  892.         }
  893.         else
  894.             sound_alarm ();        /* No action performed        */
  895.         break;
  896.  
  897.         case padLEFT:
  898.         if (more_move(0, -1))    break;
  899.         user_arg = -user_arg;
  900.         case 'L':            /* scroll left (1/2) screen    */
  901.         if (user_arg == 0) user_arg = width2;
  902.         LOGARGS('L', user_arg);
  903.         if (colmin > 0)
  904.         {
  905.             colmin -= user_arg;
  906.             if (colmin < 0)
  907.             {
  908.             colmin = 0;    /* Limit shift    */
  909.             sound_alarm ();    /* ...but yell    */
  910.             }
  911.             more_this ();
  912.         }
  913.         else
  914.             sound_alarm();
  915.         break;
  916.  
  917.         case padRIGHT:
  918.         if (more_move (0, 1))    break;
  919.         case 'R':            /* Scroll right (1/2) screen    */
  920.         LOGARGS('R', user_arg);
  921.         more_right();
  922.         break;
  923.  
  924.         case '_':        /* Display a scale in the prompt-line */
  925.         LOGARGS('_',0);
  926.         ruler_mode = ! ruler_mode;
  927.         break;
  928.  
  929.         case '?':
  930.         LOGARGS('?',0);
  931.         crt_help (0, "BROWSE");
  932.         break;
  933.  
  934.         default:
  935.         sound_alarm();
  936.         }
  937.     }
  938. }
  939.  
  940. /* <0_mark>:
  941.  * Initialize display-width, based on terminal size and on use of '/MARK'.
  942.  */
  943. static
  944. void    more_0_mark (void)
  945. {
  946.     width    = crt_width ();
  947.     if (M_opt)
  948.     {
  949.         width -= (mark_width = MARK_WIDTH);
  950.         if (multiline)
  951.         {
  952. #define    MULT_WIDTH    8
  953.             width -= MULT_WIDTH;
  954.             mark_width += MULT_WIDTH;
  955.         }
  956.     }
  957.     width2 = width / 2;
  958. }
  959.  
  960. /* <0_line>:
  961.  * Re-init flags for current line (or record).
  962.  */
  963. static
  964. void    more_0_line(void)
  965. {
  966.     obfr_     = o_bfr; *obfr_ = EOS;
  967.     findmark = 0;
  968.     column   = 0;
  969. }
  970.  
  971. /* <0_page>:
  972.  * Re-init flags for current page.
  973.  */
  974. static
  975. void    more_0_page(void)
  976. {
  977.     onpage    = inpage = 0;
  978.     colmax    = colmin + width;
  979.     more_0_line();
  980. }
  981.  
  982. /* <move>:
  983.  * Move the cursor in the specified direction.
  984.  */
  985. static
  986. int    more_move (int dy, int dx)
  987. {
  988.     register
  989.     int    rpt    = max(1, user_arg),
  990.         top    = crt_top(),
  991.         end    = crt_end();
  992.  
  993.     if (ruler_mode)
  994.     {
  995.         user_arg = 0;
  996.         if (dx *= rpt)
  997.         {
  998.         ruler_x += dx;
  999.         ruler_x = more_limit(ruler_x, 0, more_size(O_opt));
  1000.         if (ruler_x < colmin)
  1001.             user_arg = ruler_x - (colmin + 1);
  1002.         else if (ruler_x > MAXCOL)
  1003.             user_arg = ruler_x - MAXCOL;
  1004.         user_arg = width2 * more_round (user_arg, width2);
  1005.         }
  1006.         else if (dy *= rpt)
  1007.         {
  1008.         ruler_y += dy;
  1009.         ruler_y = more_limit (ruler_y, 0, last_line-1);
  1010.         if (ruler_y < top)
  1011.             user_arg = top - ruler_y;
  1012.         else if (ruler_y > end)
  1013.             user_arg = ruler_y - end;
  1014.         user_arg = more_round (user_arg, HalfPage);
  1015.         }
  1016.         return (user_arg == 0);
  1017.     }
  1018.     return (FALSE);
  1019. }
  1020.  
  1021. /* <limit>:
  1022.  * Limit a value to a specified range, sounding alarm if it falls out.
  1023.  */
  1024. static
  1025. int    more_limit (int val, int lo, int hi)
  1026. {
  1027.     if (val < lo)
  1028.         sound_alarm(),    val = lo;
  1029.     else if (val > hi)
  1030.         sound_alarm(),    val = hi;
  1031.     return (val);
  1032. }
  1033.  
  1034. /* <round>:
  1035.  * Given a number 'num', and the granularity 'd', return the closest multiple
  1036.  * of 'd'.
  1037.  */
  1038. static
  1039. int    more_round (int num, int d)
  1040. {
  1041.     if (num >= 0)
  1042.         return ((num + d - 1) / d);
  1043.     else
  1044.         return (-more_round(-num, d));
  1045. }
  1046.  
  1047. /* <lastp>:
  1048.  * Return the last seek-mark index which we may use to initiate a screen.
  1049.  */
  1050. static
  1051. int    more_lastp (void)
  1052. {
  1053.     return (max(0, LAST_PAGE));
  1054. }
  1055.  
  1056. /* <size>:
  1057.  * Return the maximum number of characters wide, given a value of /OVER:
  1058.  */
  1059. static
  1060. int    more_size (int over)
  1061. {
  1062.     int    j = i_recl;
  1063.  
  1064.     if (over == 0)        j <<= 3;
  1065.     else if (over <= 2)    j <<= 1;
  1066.     return ((j | 7) + 1);    /* Allow at least one tab stop    */
  1067. }
  1068.  
  1069. /* <right>:
  1070.  * Shift screen right by either 1/2 screen, or by the number of columns
  1071.  * specified in 'user_arg'.
  1072.  */
  1073. static
  1074. void    more_right(void)
  1075. {
  1076.     int    j,
  1077.         endcol    = more_size(O_opt);
  1078.  
  1079.     if (user_arg == 0)    user_arg = width2;
  1080.     if (endcol > width)
  1081.     {
  1082.         if ((j = more_limit (colmin + user_arg, 0, endcol)) != colmin)
  1083.         {
  1084.         colmin = j;
  1085.         more_this ();
  1086.         }
  1087.     }
  1088.     else
  1089.         sound_alarm();
  1090. }
  1091.  
  1092. /* <down>:
  1093.  * Combined forward-2, down-1 commands taking into account repeat-factor.
  1094.  * We permit an "X" to abort the forward-search.
  1095.  */
  1096. static
  1097. void    more_down (int frac, int rpt)
  1098. {
  1099.     register
  1100.     int    old    = TOP_NEXT,
  1101.         target    = TOP_NEXT + ((--rpt) * frac),
  1102.         actual;
  1103.  
  1104.     /*
  1105.      * If we haven't reached the end of the file before, we must skip
  1106.      * along, setting pointers first, until we either reach the end of
  1107.      * the file, or we reach the desired target-mark.
  1108.      */
  1109.     if ((last_page < 0) && (target > end_mark))
  1110.     {
  1111.         while ((last_page < 0) && (target > end_mark) && ! ctlx_tst())
  1112.         more_skip(nullC);
  1113.         more_seek (old);    /* fix crt-scroll */
  1114.     }
  1115.  
  1116.     actual = max(0, min(target, more_lastp()));
  1117.  
  1118.     if (actual != TOP_THIS)
  1119.     {
  1120.         more_seek (actual);
  1121.         more_screen ((frac == 1 && rpt == 0 && actual == target)
  1122.             ? HalfPage : FullPage, 1);
  1123.     }
  1124.     else        /* Yell if I didn't go anywhere    */
  1125.         sound_alarm();
  1126. }
  1127.  
  1128. /* <skip>:
  1129.  * Read through current half-page.  A page is ended when
  1130.  *    (a) The number of lines for the display screen have been passed, or
  1131.  *    (b) An end-of-file is found.
  1132.  *
  1133.  * If the 'find_' argument is nonnull, test the character stream as it is
  1134.  * read.  If a match is found within the page, return true, otherwise false.
  1135.  *
  1136.  * Because no fseek is done before beginning to read the half-screen, this
  1137.  * will not search overlapping half-screens:
  1138.  *
  1139.  *    '/' search begins on a mark (screen top), reads up to (but ends
  1140.  *        BEFORE) the mark ending a half-screen.  Backward searches must
  1141.  *        also do a seek-to-mark, and consequently do not read down to the
  1142.  *        mark either.
  1143.  *    'N' search begins AFTER the present mark (screen bottom) and reads
  1144.  *        up to (including) the mark ending the page.
  1145.  *
  1146.  * Thus, on exit from a search, the value of 'now' will be that of the TOP of
  1147.  * the half-screen containing the search target, or the BOTTOM, depending on
  1148.  * the search state.
  1149.  */
  1150. static
  1151. int    more_skip(
  1152.     char    *find_)        /* => 'find_bfr', or null    */
  1153. {
  1154.     int    inx,
  1155.         found    = FALSE;
  1156.     char    msgbuf[CRT_COLS];
  1157.  
  1158.     if (find_)
  1159.         sprintf (msgbuf, "%3.3d  Find: '%s'", TOP_NEXT, find_);
  1160.     else
  1161.         sprintf (msgbuf, "%3.3d  Skipping...", TOP_NEXT);
  1162.     more_msg (msgbuf, FALSE);
  1163.  
  1164.     for (more_0_page(); inpage < HalfPage;)
  1165.     {
  1166.         if (more_read(i_line) < 0)    /* End of file ? */
  1167.             break;
  1168.         else
  1169.         {
  1170.             onpage += lstate_len;
  1171.             inpage++;
  1172.             if (find_ && !found)
  1173.             {
  1174.                 for (inx = lstate_1st; inx < lstate_end; inx++)
  1175.                 {
  1176.                     if (STRVCMP)
  1177.                     {
  1178.                         found = i_line + 1;
  1179.                         break;
  1180.                     }
  1181.                 }
  1182.             }
  1183.         }
  1184.     }
  1185.  
  1186.     /*
  1187.      * The present position should be one display-line above the next
  1188.      * to be displayed:
  1189.      */
  1190. end_of_page:
  1191. #ifdef    DEBUG
  1192.     more_show ("skip(end=%d): [%d:%d,%d] => %d  after  [?:%d,%d] => %d",
  1193.         i_line,
  1194.         SHOW_OFF(now), LINEMARK(now),
  1195.         rstate.rfa, lstate_1st, inpage+LINEMARK(now-1));
  1196. #endif
  1197.     return (found);
  1198. }
  1199.  
  1200. /* <line>:
  1201.  * Put the portion of the output line which lies within the current column
  1202.  * limits onto the screen.
  1203.  */
  1204. static
  1205. void    more_line (void)
  1206. {
  1207.     int    len;
  1208.     char    msg    [MAXBFR],
  1209.         bfr2    [MAXBFR],
  1210.         field_0    [4],    field_1    [8],    field_2    [8],    field_r_l[8],
  1211.         *c_;
  1212.  
  1213.     /*
  1214.      * If a search target was found on the current line, but not within
  1215.      * the column limits, set visible indicators on the closest end of the
  1216.      * line to the target.
  1217.      */
  1218.     if (findmark & FIND_L)
  1219.     {
  1220.         if (o_bfr[0] == EOS)
  1221.             strcpy (o_bfr, " ");
  1222.         crt_high (o_bfr, 1);
  1223.     }
  1224.     if (findmark & FIND_R)
  1225.     {
  1226.         c_    = strnull(o_bfr);
  1227.         len    = strlen(o_bfr);
  1228.         while (len++ < width)
  1229.             strcpy (c_++, " ");
  1230.         crt_high (&o_bfr[width-2], 1);
  1231.     }
  1232.     obfr_ = strnull (c_ = o_bfr);
  1233.  
  1234.     /*
  1235.      * M-option: If set, display one of two formats.  The 'findmark'
  1236.      *    pattern (3 cols) and the line-number (low 2 digits) are always
  1237.      *    shown.  The other two fields depend on the value of M_opt:
  1238.      *
  1239.      *    (1) the ftell-mark, offset of line-within-record
  1240.      *    (2) the record length, length of line-within-record
  1241.      *
  1242.      * If 'multiline' is set, we display both the record number and the
  1243.      * line number.
  1244.      */
  1245.     if (M_opt)
  1246.     {
  1247.     static    char    cent[]    = ".%02d",
  1248.             thou[]    = ".%04d";
  1249.  
  1250.         sprintf (field_0, "%c%c%c",
  1251.             (findmark & FIND_L)  ? '<' : ' ',
  1252.             (findmark & FIND_IN) ? '=' : ' ',
  1253.             (findmark & FIND_R)  ? '>' : ' ');
  1254.  
  1255.         field_2[0] = field_r_l[0] = EOS;
  1256.  
  1257.         if (lstate_1st)    /* Are we doing continuation lines?*/
  1258.         {
  1259.             sprintf (field_1, "%6s", " ");
  1260.             if (multiline)
  1261.             {
  1262.                 sprintf (field_2, thou,
  1263.                     (M_opt == 1    ? lstate_1st
  1264.                             : lstate_len));
  1265.                 strcpy (field_r_l, "...");
  1266.             }
  1267.         }
  1268.         else
  1269.         {
  1270.             sprintf (field_1, "%6d",
  1271.                 (M_opt == 1 ? rstate.rfa : rstate_len));
  1272.             if (multiline)
  1273.             {
  1274.                 if (M_opt == 1 || (lstate_len == rstate_len))
  1275.                     strcpy (field_2, ".....");
  1276.                 else
  1277.                     sprintf (field_2, thou, lstate_len);
  1278.                 sprintf (field_r_l, cent, (rstate.rec % 100));
  1279.             }
  1280.         }
  1281.         sprintf (strnull(field_r_l), cent,
  1282.             ((INFILE_LINE+1) % 100));
  1283.         sprintf (msg, "%s%s%s%s:%s",
  1284.             field_0, field_1, field_2, field_r_l, o_bfr);
  1285.         c_ = msg;
  1286.     }
  1287.  
  1288.     if(D_opt && ((inpage % HalfPage) == 0))    /* FIND_XX    */
  1289.     {
  1290.         strcpy (bfr2, c_);
  1291.         c_ = bfr2;
  1292.         crt_high (c_, strlen(c_));
  1293.     }
  1294.     crt_text (c_, INPAGE_LINE, 0);
  1295.  
  1296.     inpage++;
  1297. }
  1298.  
  1299. /* <char>:
  1300.  * Route a character into the output-display buffer, highlighting overstrikes
  1301.  * or search-targets.
  1302.  *
  1303.  * Track the current column to avoid putting characters into the buffer unless
  1304.  * within the selected column limits.
  1305.  */
  1306. static
  1307. void    more_char(char c)
  1308. {
  1309.     int    on_screen;
  1310.  
  1311.     if (isprint(c))
  1312.         column++;
  1313.  
  1314.     if ((T_opt && isgraph(c))    /* /TRIM: all but space        */
  1315.     || (!T_opt && isprint(c)))    /* /NOTRIM: all printing chars    */
  1316.     {
  1317.         if (column > endcol[INPAGE_LINE])
  1318.         endcol[INPAGE_LINE] = column;
  1319.     }
  1320.  
  1321.     on_screen = (column == 0 || ((column > colmin) && (column < colmax)));
  1322.  
  1323.     if (on_screen)
  1324.     {
  1325.         int    oldc, newc;
  1326.         /*
  1327.          * Buffer latest character.  If I am writing onto the end of
  1328.          * the buffer, 'oldc' will be null.  However, if I have gotten
  1329.          * a backspace or carriage return, I may be overstriking a
  1330.          * character which isn't blank.
  1331.          *
  1332.          * Make merge-logic test to overstrike alphas over punctuation;
  1333.          * highlight overstrikes when no search is active.
  1334.          */
  1335.         if (isprint(c))
  1336.         {
  1337.             obfr_    = o_bfr + (column - colmin - 1);
  1338.             oldc    = toascii(*obfr_),
  1339.             newc    = c;
  1340.             if (isgraph(oldc))
  1341.             {
  1342.                 if ((newc == ' ')
  1343.                 ||  (isalnum(oldc))
  1344.                 ||  (newc == '_') )
  1345.                     newc = oldc;
  1346.             }
  1347.             if (find_flg)    newc |= 0200;
  1348.             *obfr_++ = newc; /* Buffer latest character */
  1349.             if (!oldc)
  1350.                 *obfr_     = EOS;
  1351.             else if (!*find_bfr && (c != ' ') && (oldc != ' '))
  1352.                 obfr_[-1] |= 0200;
  1353.         }
  1354.         /* else, BS, CR or LF */
  1355.         onpage++;        /* Count anything I buffer */
  1356.     }
  1357.  
  1358.     /*
  1359.      * When a search is matched, set up visible indication for the display:
  1360.      * If the string is at least partly visible, highlight those characters. 
  1361.      * If it is not visible at all, set a flag for use in 'more_line' to
  1362.      * highlight the end of the line closest to the string.
  1363.      */
  1364.     if (find_flg)        /* Have I completed a search ?    */
  1365.     {
  1366.         if (on_screen)
  1367.             findmark |= FIND_IN;
  1368.         else if (column <= colmin)
  1369.             findmark |= FIND_L;/* out-of-sight on left    */
  1370.         else
  1371.             findmark |= FIND_R;/* out-of-sight on right    */
  1372.     }
  1373. }
  1374.  
  1375. /* <ctlc>:
  1376.  * Send a string of characters per input character, according to the setting
  1377.  * of the /OVER option:
  1378.  */
  1379. static
  1380. void    more_ctlc (
  1381.     char    *ctl_)
  1382. {
  1383.     if (O_opt == 3)        more_char('.');
  1384.     else
  1385.     {
  1386.         while (*ctl_)    more_char(*ctl_++);
  1387.     }
  1388. }
  1389.  
  1390. /* <conv>:
  1391.  * Given an input record, translate control characters as per options.
  1392.  */
  1393. static
  1394. void    more_conv (void)
  1395. {
  1396. int    inx,
  1397.     old_ok    = 0,
  1398.     col_base,        /* column to set on CR (or embedded FF)  */
  1399.     on_end;            /* TRUE when on last character of record */
  1400. char    c,
  1401.     *ctl_,
  1402.     showctl[4];
  1403.  
  1404.     col_base = 0;
  1405.     find_flg = 0;
  1406.     endcol[INPAGE_LINE] = 0;
  1407.     for (inx = lstate_1st; inx < lstate_end; inx++)
  1408.     {
  1409.         old_ok    = max((find_flg-1),0);
  1410.         if (*find_bfr)
  1411.         {
  1412.             find_flg = STRVCMP;
  1413.             find_flg = max(find_flg, old_ok);
  1414.         }
  1415.         on_end    = (inx >= (lstate_end-1));
  1416.         switch (c = i_bfr[inx])
  1417.         {
  1418.         /*
  1419.          * Rubout is considered a control character, but does not map
  1420.          * in the usual fashion.
  1421.          */
  1422.         case 0x7f:
  1423.             if (O_opt >= 2)    more_ctlc("<DEL>");
  1424.             else        more_ctlc("^?");
  1425.             break;
  1426.         /*
  1427.          * Newline resets the column to zero because it begins a new
  1428.          * display-line.  It always ends the display line (because it
  1429.          * is the only reasonable way to show it).
  1430.          */
  1431.         case '\n':
  1432.             break;
  1433.         /*
  1434.          * Carriage-return resets the column to the beginning of the
  1435.          * current overstrike-region (marked by 'col_base').
  1436.          *
  1437.          * To see better binary records (and simplify the seek-logic)
  1438.          * show carriage control in '^' form if it does not end the
  1439.          * record, but is instead embedded.
  1440.          */
  1441.         case '\r':
  1442.             if ((!O_opt) || on_end)
  1443.             {
  1444.             column    = col_base;
  1445.             obfr_    = o_bfr + strlen (o_bfr);
  1446.             more_char (c);
  1447.             break;
  1448.             }
  1449.         /*
  1450.          * Convert tabs to spaces to simplify left/right scrolling.
  1451.          */
  1452.         case '\t':
  1453.             if (!O_opt)
  1454.             {
  1455.             register int j = 1 + (column | 7);
  1456.             while (column < j)
  1457.                 more_char (' ');
  1458.             break;
  1459.             }
  1460.         /*
  1461.          * Account for backspace:
  1462.          */
  1463.         case '\b':
  1464.             if (!O_opt)
  1465.             {
  1466.             if (column > 0) column --;
  1467.             more_char(c);
  1468.             break;
  1469.             }
  1470.         /*
  1471.          * Convert control characters to a pair, display iff within
  1472.          * column bounds.
  1473.          */
  1474.         default:
  1475.             if (iscntrl(c))
  1476.             {
  1477.             ctl_ = &showctl[2];
  1478.             *ctl_-- = EOS;
  1479.             *ctl_-- = c | 0100;
  1480.             *ctl_   = '^';
  1481.             if (O_opt == 2) switch (c)
  1482.             {
  1483.             case CTL('H'):    ctl_ = "<BS>";    break;
  1484.             case CTL('I'):    ctl_ = "<TAB>";    break;
  1485.             case CTL('K'):    ctl_ = "<VT>";    break;
  1486.             case CTL('L'):    ctl_ = "<FF>";    break;
  1487.             case CTL('M'):    ctl_ = "<CR>";    break;
  1488.             case '\033':    ctl_ = "<ESC>";    break;
  1489.             }
  1490.             more_ctlc (ctl_);
  1491.  
  1492.             if (c == '\r' || c == '\n' || c == '\f')
  1493.                 col_base = column;
  1494.             }
  1495.             else
  1496.             more_char(c);
  1497.         }
  1498.     }
  1499.     more_line ();
  1500. }
  1501.  
  1502. /* <screen>:
  1503.  * Read and display either a full screen (one line less than actual screen
  1504.  * size), or a half screen (on either top or bottom, with appropriate
  1505.  * pre-scrolling).
  1506.  */
  1507. static
  1508. void    more_null (int inx)
  1509. {
  1510.     crt_text ("", inx-crt_top(), 0);
  1511. }
  1512.  
  1513. static
  1514. void    more_screen (
  1515.     int    view_size,
  1516.     int    dirflg)
  1517. {
  1518.     void    (*ffunc)(int) = (dirflg > 0) ? more_null : 0;
  1519.     int    old_top = crt_top(),
  1520.         old_end = crt_end(),
  1521.         new_top = max(0, (old_top + 1 - view_size)),
  1522.         new_end    = i_line + view_size - 1;
  1523.  
  1524.     more_0_page();
  1525. #ifdef    DEBUG
  1526.     more_show ("(screen)(%d,%d) old(%d,%d), new(%d,%d) rec:%d",
  1527.         view_size, dirflg, old_top, old_end, new_top, new_end, i_line);
  1528. #endif
  1529.  
  1530.     if (view_size == HalfPage && dirflg > 0 && old_end > new_top)
  1531.         memcpy (&endcol[0], &endcol[HalfPage], HalfPage*sizeof(endcol[0]));
  1532.  
  1533.     /*
  1534.      * Note: 'crt_scroll' is given a null display routine to simplify
  1535.      * the logic of this program.  Thus, the end-line of the screen is
  1536.      * reset in forward 1/2-screen scrolling.  MAXREC is used in controlling
  1537.      * the scrolling to coerce 'crt_scroll' to be able to show a partly
  1538.      * blank screen.
  1539.      */
  1540.     if (view_size == HalfPage)
  1541.     {
  1542.         crt_scroll (new_end+1, MAXREC, ffunc);
  1543.         BaseLine = HalfPage;
  1544.         view_size++;    /* half-pages overlap by one line    */
  1545.     }
  1546.     /*
  1547.      * The view-size is not changed from full-page to half-page in
  1548.      * backward scrolling, even if only half a page can be displayed.
  1549.      * The 'crt' module will simply not update the screen, even though
  1550.      * this program will read it.
  1551.      */
  1552.     else
  1553.     {
  1554.         int next = (dirflg > 0 ? new_end : i_line);
  1555.         BaseLine = 0;
  1556.         if (dirflg)
  1557.         {
  1558. #ifdef    DEBUG
  1559.             more_show ("(...)scroll to:%d", next);
  1560. #endif
  1561.             crt_scroll (next, MAXREC, 0);
  1562. #ifdef    DEBUG
  1563.             more_show ("(...)old(%d,%d) new(%d,%d), mark %d",
  1564.                 old_top, old_end, crt_top(), crt_end(), now);
  1565. #endif
  1566.         }
  1567. #ifdef    DEBUG
  1568.         else
  1569.             more_show ("(...) no scrolling");
  1570. #endif
  1571.     }
  1572.  
  1573.     while (inpage < view_size)
  1574.     {
  1575.         if (more_read(inpage) < 0)
  1576.         {
  1577.         last_line = INFILE_LINE;
  1578.         break;
  1579.         }
  1580.         more_conv ();
  1581.     }
  1582.     while (inpage < view_size)    /* (If EOF, clear remainder)    */
  1583.     {
  1584.         crt_text ("", INPAGE_LINE, 0);
  1585.         endcol[INPAGE_LINE] = 0;
  1586.         inpage++;
  1587.     }
  1588.  
  1589.     /*
  1590.      * Obtain maximum line-length within the current screen:
  1591.      */
  1592.     for (colend = inpage = 0; inpage < FullPage; inpage++)
  1593.         colend = max(colend, endcol[inpage]);
  1594. }
  1595.  
  1596. /* <this>:
  1597.  * Refresh the current page.  Note that if the file is shorter than one page,
  1598.  * I must restrict the backup to the 0-mark.
  1599.  */
  1600. static
  1601. void    more_this (void)
  1602. {
  1603.     more_next (max(0,TOP_THIS));
  1604. }
  1605.  
  1606. /* <next>:
  1607.  * This routine is used to display a page when jumping around in the file,
  1608.  * refreshing the current page, etc.
  1609.  */
  1610. static
  1611. void    more_next (
  1612.     int    new)
  1613. {
  1614.     int    old = now;
  1615.  
  1616. #ifdef    DEBUG
  1617.     more_show ("(next)(%d), was %d", new, old);
  1618. #endif
  1619.     if (new < 0)    new = 0;
  1620.     more_seek (new);
  1621.     more_screen (FullPage, 1 + (new - old));
  1622. }
  1623.  
  1624. /* <seek>:
  1625.  * Re-position using 'fseek' so that the next record-read will begin at the
  1626.  * top of a half-page screen section.
  1627.  */
  1628. static
  1629. void    more_seek (
  1630.     int    next)        /* next index in 'offset[]' to use    */
  1631. {
  1632.     if (next < 0 || next > end_mark)
  1633.     {
  1634.         char bfr[CRT_COLS];
  1635.         sprintf (bfr, "fseek index(%d) out-of-range (0:%d)",
  1636.             next, end_mark);
  1637.         error (0, bfr);
  1638.     }
  1639. #ifdef    DEBUG
  1640.     more_show ("(seek)  [%d:%d,%d] => %d,  was [%d:%d,%d] => %d",
  1641.         SHOW_OFF(next), LINEMARK(next),
  1642.         SHOW_OFF(now),  LINEMARK(now));
  1643. #endif
  1644.     rseek(fp, offset[now=next].rfa, 0);
  1645.  
  1646.     /*
  1647.      * Force the next call on 'more_read' to return the line which I
  1648.      * am selecting, by adjusting 'lstate_???'.
  1649.      */
  1650.     if (offset[now].cra)    more_getr ();
  1651.  
  1652.     rstate.rec = offset[now].rec;
  1653.     lstate_1st = offset[now].cra - 1;
  1654.     lstate_len = 1;
  1655.     lstate_end = lstate_1st + 1;
  1656.  
  1657.     if (lstate_1st >= 0)    rstate.rec = rstate.rec + 1;
  1658.  
  1659.     i_line     = LINEMARK(now);    /* Reset actual-line-number    */
  1660.     end_flag = FALSE;
  1661.     now--;        /* first record-read will re-increment */
  1662. }
  1663.  
  1664. /* <read>:
  1665.  * Read a new line from the input file (extracting, as needed, from records).
  1666.  * Do all 'ftell' calls to obtain points to repeat or restart the sequential
  1667.  * reads.
  1668.  *
  1669.  * State:
  1670.  *    rstate_len <= length of current record
  1671.  *    lstate_len <= length of line to display
  1672.  *    lstate_1st <= index into 'i_bfr[]' of line to display.
  1673.  *    lstate_end <= index past end of line
  1674.  */
  1675. static
  1676. int    more_read (int rec)        /* Either 'i_line' or 'inpage'    */
  1677. {
  1678.     int    j;
  1679.  
  1680.     more_0_line();
  1681.  
  1682.     lstate_1st += lstate_len;
  1683.     lstate_len = 0;
  1684.     if ((lstate_1st >= rstate_len) || (lstate_1st <= 0))
  1685.     {
  1686.         lstate_1st = 0;
  1687.         more_getr ();
  1688.     }
  1689.  
  1690.     /*
  1691.      * Find the newline which ends the newest line, compute the line-length.
  1692.      */
  1693.     if (rstate_len >= 0)
  1694.     {
  1695.         ++i_line;        /* Found a new record        */
  1696.         for (j = lstate_1st;
  1697.             (j < rstate_len) && (i_bfr[j] != '\n');
  1698.                 j++, lstate_len++);
  1699.         if (j < rstate_len && i_bfr[j] == '\n')    lstate_len++;
  1700.         if (rstate_len != lstate_len && !multiline)
  1701.         {
  1702.             multiline = TRUE;
  1703.             more_0_mark ();
  1704.         }
  1705.     }
  1706.     else
  1707.         lstate_len = -1;    /* eof-length is negative    */
  1708.  
  1709.     lstate_end = lstate_1st + lstate_len;
  1710.  
  1711.     /*
  1712.      * If this line falls on a display mark (top,mid,bot), or an end of
  1713.      * file occurred, latch the filemarks:
  1714.      */
  1715.     if (!(rec % HalfPage) || end_flag)
  1716.     {
  1717.         if (++now >= maxvec)
  1718.         {
  1719.             maxvec += MAXVEC;
  1720.             offset = realloc (offset, maxvec * sizeof(OFFSET));
  1721.         }
  1722.         if (now > end_mark || end_mark == 0)
  1723.         {
  1724.             end_mark = now;
  1725.             offset[now].rec = rstate.rec - 1;
  1726.             offset[now].rfa = rstate.rfa;
  1727.             offset[now].cra = lstate_1st;
  1728.         }
  1729.  
  1730. #ifdef    DEBUG
  1731.         more_show ("read(%d): [%d:%d.%d] => %d",
  1732.             end_mark, SHOW_OFF(now), LINEMARK(now));
  1733. #endif
  1734.     }
  1735.  
  1736.     return (lstate_len);
  1737. }
  1738.  
  1739. /* <getr>:
  1740.  * Read a record from the input file, setting end-of-file flag, record-length,
  1741.  * and stripping parity.  If "/FEED", then read/append as long as CR ends the
  1742.  * input record.
  1743.  */
  1744. static
  1745. void    more_getr (void)
  1746. {
  1747.     int    j;
  1748.     int    first    = TRUE;
  1749.     int    join;
  1750.     int    skip;
  1751.     int    len    = 0;
  1752.     int    size    = i_size;
  1753.     unsigned *rfa_    = &rstate.rfa;
  1754.     static    unsigned dummy_rfa;
  1755.     char    *s_;
  1756.  
  1757.     rstate_len = 0;
  1758.     for (;;)
  1759.     {
  1760.         len = rgetr (fp, &i_bfr[rstate_len], size, rfa_);
  1761.         erstat (fp, fatal_msg, CRT_COLS);
  1762.         if (first)        rstate_len = len;
  1763.         else if (len >= 0)    rstate_len += len;
  1764.         if (len < 0 || !((J_opt > 0) || (S_opt >= 0)))    break;
  1765.  
  1766.         /*
  1767.          * If JOIN option is set, and we have anything in the record
  1768.          * buffer, test for a trailing LF.  If not found, we can merge
  1769.          * successive records.
  1770.          */
  1771.         join = FALSE;
  1772.         if (J_opt > 0)
  1773.         {
  1774.         join = TRUE;
  1775.         if ((rstate_len > 0)
  1776.         && (i_bfr[rstate_len-1] == '\n'))    join = FALSE;
  1777.         }
  1778.  
  1779.         /*
  1780.          * If SQUEEZE option is set (and if we did not JOIN), test the
  1781.          * record buffer to see if it is a simple sequence of
  1782.          * space/carriage control.  If we find only blank line(s), force
  1783.          * these down to a single blank line and set the continuation to
  1784.          * permit an additional record to be appended.
  1785.          *
  1786.          * Normally, this will cause multiple blank lines to be
  1787.          * compressed into a single blank line.  It will not do anything
  1788.          * to records containing (for example) a sequence of LF's
  1789.          * in addition to non-space characters.
  1790.          */
  1791.         if (!join && (S_opt >= 0) && (rstate_len >= 0))
  1792.         {
  1793.         join = TRUE;
  1794.         skip = 1;
  1795.         i_bfr[rstate_len] = EOS;
  1796.         for (s_ = i_bfr; *s_; s_++)
  1797.         {
  1798.             if (*s_ == '\n')
  1799.             skip++;
  1800.             else if (! isspace(*s_))
  1801.             {
  1802.             join = FALSE;
  1803.             break;
  1804.             }
  1805.         }
  1806.         /*
  1807.          * Limit the number of successively-skipped lines to the value
  1808.          * given in '/SQUEEZE'.
  1809.          */
  1810.         if (join)
  1811.         {
  1812.             if (skip > S_opt)    skip = S_opt;
  1813.             i_bfr[skip] = EOS;
  1814.             while (skip > 0)    i_bfr[--skip] = '\n';
  1815.             rstate_len = len = strlen(i_bfr);
  1816.             size = i_size;
  1817.         }
  1818.         }
  1819.         /*
  1820.          * If I couldn't join or squeeze, exit the loop.  Otherwise,
  1821.          * set up for the next combining.
  1822.          */
  1823.         if (!join)            break;
  1824.         first = FALSE;
  1825.         size -= len;
  1826.         rfa_ = &dummy_rfa;
  1827.     }
  1828.  
  1829.     if (rstate_len < 0)
  1830.     {
  1831.         rstate_len = -1;
  1832.         if (last_page < 0)
  1833.         {
  1834.         last_page = onpage ? now-1 : now-2;
  1835.         if (last_page < 0)    last_page = 0;
  1836.         }
  1837. #ifdef    DEBUG
  1838.         more_show ("read(%d): EOF last-rec:%d, last-page:%d",
  1839.             now, rstate.rec, last_page);
  1840. #endif
  1841.         end_flag = TRUE;
  1842.     }
  1843.     else
  1844.     {
  1845.         rstate.rec = rstate.rec + 1;
  1846.         for (j = 0; j < rstate_len; j++)
  1847.         i_bfr[j] = toascii(i_bfr[j]);
  1848.     }
  1849. }
  1850.  
  1851. /* <r_cmd>:
  1852.  * Prompt and read a single character for commands:
  1853.  */
  1854. static
  1855. int    more_r_cmd(void)
  1856. {
  1857. int    command,
  1858.     len,
  1859.     top    = crt_top(),    /* display-line-min (0..n-1)    */
  1860.     end    = crt_end(),    /* display-line-max (0..n-1)    */
  1861.     left    = colmin,    /* leftmost column (1..n)    */
  1862.     right    = colmax-1,    /* rightmost column (1..n)    */
  1863.     width    = crt_width();    /* display-width (<= CRT_COLS)    */
  1864. char    *c_,
  1865.     msg    [CRT_COLS],
  1866.     format    [20],
  1867.     numbfr    [MAXBFR];
  1868.  
  1869.     end    = min(last_line-1, end);
  1870.  
  1871.     if (fatal_msg[0])
  1872.         more_msg (fatal_msg, TRUE);
  1873.     else if (ruler_mode)
  1874.     {
  1875.         int    ruler_col = (M_opt ? mark_width : 0);
  1876.         ruler_x = min(MAXCOL, max(left, ruler_x));
  1877.         ruler_y = min(end, max(top, ruler_y));
  1878.         if (ruler_min != colmin || ruler_max != colmax)
  1879.         {
  1880.             dspc_init (ruler_text, ruler_col, colmin+1);
  1881.         ruler_min = colmin;
  1882.         ruler_max = colmax;
  1883.         }
  1884.         dspc_move (ruler_text, ruler_col + ruler_x - colmin, ruler_y);
  1885.     }
  1886.     else
  1887.     {
  1888.         sprintf (numbfr, "  Lines %d:%d  Cols %d:%d (%d)",
  1889.             top+1, end+1,
  1890.             colmin+1, right, colend);
  1891.         if (M_opt)
  1892.         {
  1893. #ifdef    RMSIO
  1894.         sprintf (strnull(numbfr), ":%d", i_recl);
  1895. #endif
  1896.         if (multiline)
  1897.             sprintf (strnull(numbfr), "  Records %d+",
  1898.                 offset[TOP_THIS].rec+1);
  1899.         }
  1900. #ifdef    DEBUG
  1901.         if (D_opt)
  1902.         strcat (numbfr, " (debug)");
  1903. #endif
  1904.  
  1905.     /* Protect against obscenely-long filenames: */
  1906.         sprintf (format, "%%.%ds %%s", len = width - (strlen(numbfr)+3));
  1907.         sprintf (msg, format, more_name(len), numbfr);
  1908.         more_msg (msg, TRUE);
  1909.     }
  1910.  
  1911.     /*
  1912.      * Read the user's reply, translating alpha's to controls
  1913.      * (making them equivalent).
  1914.      */
  1915.     while (((command = getpad()) == '\r')
  1916.     ||    (command == '\n'));
  1917.     if (isascii(command) && isalpha(command)) command = CTL(command);
  1918.  
  1919.     user_arg = 0;            /* Inactive if zero        */
  1920.     if (isascii(command) && isdigit(command))
  1921.     {
  1922.         sprintf (numbfr, "%c", command);
  1923.         command = more_r_buf (numbfr, "Repeat:", "BDFIJHLMORU");
  1924.         if (command > 128)
  1925.             command += 128;    /* 128..255 => 256..getpad    */
  1926.         else if (isalpha(command))
  1927.             command = CTL(command);
  1928.         c_ = scanint (numbfr, &user_arg);
  1929.         if (c_ == numbfr || c_[1])
  1930.             command = -1;    /* Force an error-return    */
  1931.     }
  1932.     return toshift(command);
  1933. }
  1934.  
  1935. /* <r_buf>:
  1936.  * Read a string-buffer, for either search target, or for numeric argument.
  1937.  * If the latter, 'delim_' is set, and we must do special actions.
  1938.  */
  1939. static
  1940. int    more_r_buf (
  1941.     char    *co_,            /* => Output buffer    */
  1942.     char    *m1_,            /* Prompt-message    */
  1943.     char    *delim_)        /* If non-null, list of terminators */
  1944. {
  1945.     int    len    = strlen(m1_);
  1946.     char    prompt    [MAXBFR];
  1947.  
  1948.     strcpy (prompt, m1_);
  1949.     crt_high (prompt, len);
  1950.     strcpy (co_, edtcmd (*co_,
  1951.             delim_, (delim_ ? 8 : 0),
  1952.                 lpp-1, len+2, "BROWSE", co_, prompt));
  1953.     len = strlen(co_)-1;
  1954.     return (0xff & co_[max(len,0)]);
  1955. }
  1956.  
  1957. /* <msg>:
  1958.  * Put some text on the last line of the screen (highlighted), and position
  1959.  * the cursor at the end of the string.
  1960.  */
  1961. static
  1962. int    more_msg (char *c_, int last)
  1963. {
  1964.     int    len;
  1965.     char    bfr    [MAXBFR];
  1966.  
  1967.     strcpy (bfr, c_);
  1968.     crt_high (bfr, len = strlen(bfr));
  1969.     crt_text (bfr, lpp-1, 1);
  1970.     if (last)
  1971.     {
  1972.         len += 2;
  1973.         crt_move (lpp, len);
  1974.     }
  1975.     return (len);
  1976. }
  1977.  
  1978. #ifdef    DEBUG
  1979. /* <show>:
  1980.  * debug: show "printf" on screen-end
  1981.  */
  1982. static
  1983. void    more_show (char *format, ...)
  1984. {
  1985.     va_list    ap;
  1986.     char    bfr[1024];
  1987.  
  1988.     if (D_opt)
  1989.     {
  1990.         sleep(1);
  1991.         va_start (ap, format);
  1992.         vsprintf (bfr, format, ap);
  1993.         va_end (ap);
  1994.         crt_text (bfr, lpp-1, 0);
  1995.         if (D_opt > 1)
  1996.             getpad();
  1997.         else
  1998.             sleep(3);
  1999.     }
  2000. }
  2001. #endif
  2002.  
  2003. /* <0_bg>:
  2004.  * Special terminal initialization.  These routines may be called via 'crt_init'
  2005.  * to adjust the screen on the basis of BROWSE options.
  2006.  */
  2007.  
  2008. /* Set Bitgraph terminal to native mode, home and clear. */
  2009. static
  2010. int    more_0_bg(short    *lpp_, short *width_)
  2011. {
  2012.     int    j;
  2013.  
  2014.     for (j = 0; j < 3; j++)
  2015.     {
  2016.         putraw ("\033:0e");
  2017.         putraw ("\033[H");
  2018.         putraw ("\033[J");
  2019.     }
  2020.     *lpp_    = 64;
  2021.     *width_    = 80;
  2022.     crt__NL0 (FALSE);
  2023.     return 0;
  2024. }
  2025.  
  2026. /* <0_vt>:
  2027.  */
  2028. static
  2029. int    more_0_vt (short *lpp_, short *width_)
  2030. {
  2031.     if (W_opt)
  2032.         *width_ = 132;
  2033.     return 0;
  2034. }
  2035.  
  2036. /*
  2037.  * Define routines to make this source standalone.
  2038.  */
  2039. #ifndef    main_incl
  2040. void    warn (char *format, ...)
  2041. {
  2042.     va_list    ap;
  2043.     char    msg[CRT_COLS+MAX_PATH];
  2044.  
  2045.     whoami (msg, 3);
  2046.     strcat (msg, "-w-");
  2047.     va_start(ap, format);
  2048.     vsprintf (strnull(msg), format, ap);
  2049.     va_end(ap);
  2050.     putraw (msg);
  2051. }
  2052.  
  2053. void    error (int status, char *s_)
  2054. {
  2055.     char    msg[80],
  2056.         who[80];
  2057.  
  2058.     whoami (who, 3);    strcat (who, "-f-");
  2059.     if (s_)
  2060.     {
  2061.         sprintf (msg, "%s-%.60s\n", who, s_);
  2062.         putraw (msg);
  2063.     }
  2064.     else
  2065.         perror (who);
  2066.     if (did_crt_init) crt_quit(FALSE);
  2067.     exit (status ? status : (STS$M_INHIB_MSG | STS$K_ERROR));
  2068. }
  2069. #endif
  2070.  
  2071. #ifdef    main_incl
  2072. static
  2073. void    logfile(int cmd, int count)
  2074. {
  2075.     if (count)
  2076.         LOGFILE(("  $ %d%c", count, cmd))
  2077.     else
  2078.         LOGFILE(("  $ %c", cmd))
  2079. }
  2080. #endif/*main_incl*/
  2081.