home *** CD-ROM | disk | FTP | other *** search
/ Phoenix CD 2.0 / Phoenix_CD.cdr / 01e / more_ddj.zip / MORE.C < prev    next >
Text File  |  1986-09-27  |  18KB  |  701 lines

  1.    /* BROWSE.C            26-Sep-86 28415          Accesses: 5
  2.  
  3.     Keywords: HOLUB OCT86 C FILE BROWSING UTILITY BROWSE C CHEST
  4.  
  5.     A file-browsing utility by Allen Holub (October 1986 C-Chest column).
  6.     Listing 1 -- more.c Listing 2 -- b_getc.c
  7.                 --28413 bytes
  8.  
  9.  
  10.                     Listing 1 -- more.c
  11.     ---------------------------------------------------------------------- */
  12.    #include <stdio.h>
  13.    #include <ctype.h>
  14.    #include <fcntl.h>
  15.    #include <process.h>
  16.  
  17.    /*       MORE.C       Page input to stdout.
  18.     *
  19.     *       (C) 1986, Allen I. Holub. All rights reserved.
  20.     *
  21.     *       Usage:  more [-<offset>] file...
  22.     *
  23.     *       Exit status: 0 always
  24.     */
  25.  
  26.   /*-----------------------------------------------------------------*/
  27.  
  28.   extern int  b_getc  ();          /* Direct console input function */
  29.                       /* source in /src/tools/b_getc.c */
  30.   extern int  look    ();          /* Char. lookahead function.       */
  31.                       /* source in /src/tools/look.asm */
  32.   extern long    ftell       ( FILE *);      /* Standard library functions:   */
  33.   extern long    atol       ( char* );
  34.   extern FILE    *fopen       ( char*, char* );
  35.   extern int    fclose       ( FILE* );
  36.   extern int    spawnl       ( int, char*, char*, );
  37.   extern char    *getenv    ( char* );
  38.   extern char    *fgets       ( char*, int, FILE* );
  39.   extern long    filelength ( int );
  40.  
  41.   /*-----------------------------------------------------------------*/
  42.  
  43.   #define CAN      0x18      /* ^X   */
  44.   #define ESC      0x1b      /* ^[   */
  45.  
  46.   #define max(a,b) ((a) > (b) ? (a) : (b))
  47.   #define min(a,b) ((a) < (b) ? (a) : (b))
  48.  
  49.   #define BSIZE     256   /* Maximum length of a line in the file */
  50.   #define PAGESIZE  23      /* # of lines to output before stopping */
  51.  
  52.   #define E(x)          fprintf(stderr,"%s\n", x)
  53.   #define HAS_DOT(p)      strchr(p,'.')
  54.  
  55.   FILE      *Ifile      = stdin;    /* Current input file            */
  56.   char      *Ifile_name      = "/dev/con"; /* Name of file associated w/ Ifile */
  57.   int      Repeat_count      = 1;        /* Repeat count for most recent cmd */
  58.   long      Line          = 0;        /* # of output Lines printed so far */
  59.   long      Flen          = 0;        /* Length of input file in chars    */
  60.   long      Start_here      = 0;        /* Seek to here when prog starts    */
  61.  
  62.   /*-----------------------------------------------------------------
  63.    *  Stack used to keep track of start of lines. Maximum number
  64.    *  of lines is determined by STACKSIZE.
  65.    */
  66.  
  67.   typedef long          STACKTYPE;
  68.   #define STACKSIZE      (1024*6)      /* Must be divisible by 2 */
  69.  
  70.   STACKTYPE          Stack[ STACKSIZE ];
  71.   STACKTYPE          *Sp = Stack + STACKSIZE;
  72.  
  73.   #define STACKFULL      (Sp <= Stack)
  74.   #define STACKEMPTY      (Sp >= Stack + STACKSIZE )
  75.   #define CLEAR_STACK()   Sp = Stack + STACKSIZE ;
  76.   #define TOS          (STACKEMPTY ? 0 : *Sp)
  77.   #define BACK_SCRN      *( min( Sp+(PAGESIZE-1), Stack+(STACKSIZE-1)) )
  78.  
  79.   #define erase_line()      line( ' ', 0 )  /* Draw a line of spaces    */
  80.  
  81.   /*------------------------------------------------------------------*/
  82.  
  83.   help()
  84.   {
  85.       register int      i;
  86.  
  87.       /*   Print a help message with a box around it, special IBM graphics
  88.        *   characters are used for the box
  89.        */
  90.  
  91.       putc( 0xd6, stderr );
  92.       for( i = 56 ; --i >= 0; putc( 0xc4, stderr) )
  93.       ;
  94.       E("\267");
  95.       E("\272 b ............... go (B)ack a page                     \272");
  96.       E("\272 e ............... go to end of file                    \272");
  97.       E("\272 n ............... go to (N)ext file                    \272");
  98.       E("\272 o ............... print (O)ffset from start of file    \272");
  99.       E("\272 q ............... (Q)uit (return to DOS)               \272");
  100.       E("\272 s ............... (S)kip one line (w/o printing)       \272");
  101.       E("\272 r ............... (R)ewind file (go back to beginning) \272");
  102.       E("\272 ! ............... execute a program (type blank line   \272");
  103.       E("\272                   at prompt to execute last)           \272");
  104.       E("\272 / ............... search for regular expression        \272");
  105.       E("\272                   (type blank line at prompt for last) \272");
  106.       E("\272 ESC ............. Scroll until any key is hit          \272");
  107.       E("\272 CR .............. next line                            \272");
  108.       E("\272 SP .............. next screen                          \272");
  109.       E("\272 anything else ... print this list                      \272");
  110.       E("\272                                                        \272");
  111.      E("\272 All commands may be preceeded by a count.              \272");
  112.  
  113.      putc( 0xd3, stderr );
  114.      for( i = 56 ; --i >= 0; putc( 0xc4 ,stderr) )
  115.      ;
  116.  
  117.      E("\275");
  118.  }
  119.  
  120.  /*------------------------------------------------------------------*/
  121.  
  122.  usage()
  123.  {
  124.      E("more:  Copyright (C) 1986, Allen I. Holub. All rights reserved.");
  125.      E("\nUseage: more [+<num>] [file...] \n");
  126.      E("Print all files in list on the screen, pausing every 23 lines");
  127.      E("If + is specified, more will start printing at character <num>");
  128.      E("One of the following commands is executed after each page:");
  129.  
  130.      help();
  131.      exit(1);
  132.  }
  133.  
  134.  /*----------------------------------------------------------------------*/
  135.  
  136.  push( file_posn )
  137.  long     file_posn;         /* Push file_posn onto the stack     */
  138.  {
  139.      if( STACKFULL )     /* If the stack is full, compress it */
  140.          comp_stk();
  141.  
  142.      *( --Sp ) = file_posn;
  143.  }
  144.  
  145.  /*------------------------------------------------------------------*/
  146.  
  147.  long     pop()
  148.  {
  149.      /*     Pop one entry off the stack and return the file
  150.       *     position.
  151.       */
  152.  
  153.      return STACKEMPTY ? 0 : *Sp++ ;
  154.  }
  155.  
  156.  /*------------------------------------------------------------------*/
  157.  
  158.  comp_stk()
  159.  {
  160.      /*     Compress the stack by removing every other entry.
  161.       *     This routine is called when the stack is full (we've
  162.       *     read more lines than the stack can hold).
  163.       */
  164.  
  165.      register STACKTYPE     *dest, *src;
  166.  
  167.      fprintf(stderr,"\007Stack Full:  Compressing\n");
  168.  
  169.      src = dest = Stack + STACKSIZE;
  170.  
  171.      while( (src -= 2) >= Stack )
  172.          *--dest = *src;
  173.  
  174.      Sp = dest;
  175.  }
  176.  
  177.  /*------------------------------------------------------------------*/
  178.  
  179.  getcon()
  180.  {
  181.      /*     Get one character from the console using a direct
  182.       *     bios call. Map \r into \n if one is encountered.
  183.       */
  184.  
  185.      register int     c;
  186.  
  187.      c = b_getc() & 0x7f;     /* Get a character from console */
  188.      putchar(c);         /* Echo character         */
  189.  
  190.      return ( c == '\r' ) ? '\n' : c ;
  191.  }
  192.  
  193.  /*----------------------------------------------------------------------*/
  194.  
  195.  clear_io()
  196.  {
  197.      /*     Clears the entire I/O queue, both at the bios and the
  198.       *     bdos level.
  199.       */
  200.  
  201.      while( look() )
  202.          b_getc();
  203.  
  204.  #ifdef NEVER
  205.      while( kbhit() )
  206.          getchar();
  207.  #endif
  208.  
  209.  }
  210.  
  211.  /*----------------------------------------------------------------------*/
  212.  
  213.  khit()              /* Return true if a key has been hit on */
  214.  {                 /* the physical keyboard (as compared     */
  215.      if( look() )         /* with a character available on stdin) */
  216.      {
  217.          clear_io();
  218.          return 1;
  219.      }
  220.      return 0;
  221.  }
  222.  
  223.  /*----------------------------------------------------------------------*/
  224.  
  225.  char     *getbuf( p )
  226.  register char     *p;
  227.  {
  228.      /*     Get a line of input using direct console I/O and put it
  229.       *     into buf. Return a pointer to the first whitespace on the
  230.       *     line or to end of line if none. This routine is for
  231.       *     getting commands from the user, not for getting normal
  232.       *     input. ^H is supported as a destructive backspace but no
  233.       *     other editing is available.
  234.       */
  235.  
  236.      register int     c;
  237.      int         gottail = 0;
  238.      char         *start  = p;
  239.      char         *tail     = "" ;
  240.  
  241.      clear_io();
  242.  
  243.      while( (c=getcon()) != '\n' )
  244.      {
  245.          if( c == '\b' )
  246.          {
  247.              if( p <= start )
  248.                  fputs( " \007", stderr );
  249.              else
  250.              {
  251.                  --p;
  252.                  fputs( " \b", stderr );
  253.              }
  254.          }
  255.          else
  256.          {
  257.              if( isspace(c) && !gottail )
  258.                  gottail = (int)( tail = p );
  259.              *p++ = c;
  260.          }
  261.      }
  262.  
  263.      *p = '\0';
  264.      return( p <= start ? NULL : tail );
  265.  }
  266.  
  267.  /*------------------------------------------------------------------*/
  268.  
  269.  percent(s)
  270.  char     *s;
  271.  {
  272.      /* Print the percentage of the file that we've seen so far  */
  273.  
  274.      printf("%4.1f%%%s", ((double)TOS / (double)Flen) * 100.00, s );
  275.  }
  276.  
  277.  /*------------------------------------------------------------------*/
  278.  
  279.  int     getcmd()
  280.  {
  281.      /*     Get a command from the keyboard, using direct
  282.       *     bios I/O. Commands take the form [num]<c>. Returns
  283.       *     the command. Repeat_count is initialized to hold [num]
  284.       *     or 1 if no num is entered.
  285.       */
  286.  
  287.      int     c;
  288.  
  289.      clear_io();
  290.      percent("");
  291.      printf(", line %ld (? for commands): ", Line );
  292.  
  293.      Repeat_count = 0;
  294.      while( '0' <= (c = getcon()) && c <= '9' )
  295.          Repeat_count = (Repeat_count * 10) + (c - '0');
  296.  
  297.      if( Repeat_count == 0 )
  298.          Repeat_count = 1;
  299.  
  300.      erase_line();
  301.  
  302.      if( c == 0x03 )             /* ^C == abort  */
  303.          exit( 1 );
  304.  
  305.      return( c );
  306.  }
  307.  
  308.  /*------------------------------------------------------------------*/
  309.  
  310.  char     *inputline( suppress )
  311.  {
  312.      /* Get a line from the file being processed and put it into
  313.       * buf. Push the start of line character onto the stack.
  314.       * return 0 on end of file, a pointer to the line (ie. to buf)
  315.       * otherwise.
  316.       */
  317.  
  318.      register int     rval;
  319.      register long     start_of_line ;
  320.      static   char     buf[BSIZE];
  321.  
  322.      start_of_line = ftell( Ifile );
  323.  
  324.      if( rval = (int) fgets(buf, BSIZE, Ifile) )
  325.      {
  326.          Line++;
  327.          push( start_of_line );
  328.          if( !suppress )
  329.              fputs( buf, stdout );
  330.      }
  331.  
  332.      return rval ? buf : NULL ;
  333.  }
  334.  
  335.  /*------------------------------------------------------------------*/
  336.  
  337.  printpage()
  338.  {
  339.      /* Print an entire page from the input file             */
  340.  
  341.      register int     i;
  342.  
  343.      for( i = PAGESIZE-1; --i >= 0 && inputline(0); )
  344.          ;
  345.  }
  346.  
  347.  /*-------------------------------------------------------------------*/
  348.  
  349.  search()
  350.  {
  351.      /*     Prompt for a pattern and then search for it in the
  352.       *     file. Stop searching if the pattern is found or if
  353.       *     any key is hit. The previous pattern is remembered
  354.       *     in a local array so, if CR is entered instead of a
  355.       *     pattern, the previous pattern is used.
  356.       */
  357.  
  358.      static   char     pat[128], opat[128] ;
  359.      char         *iline;
  360.      extern  int     *makepat();
  361.      int         *template;
  362.  
  363.      printf("/");
  364.  
  365.      if( !getbuf( pat ) )
  366.          strcpy( pat, opat );
  367.  
  368.      if( !(template = makepat( pat, 0 )) )
  369.          printf("Illegal regular expression: %s\n", pat );
  370.      else
  371.      {
  372.          erase_line();
  373.          printf("/%s\n", pat );
  374.  
  375.          while( (iline = inputline(1))    &&  !khit() )
  376.          {
  377.              percent("\r");
  378.              if( matchs( iline, template, 0) )
  379.                  break;
  380.          }
  381.  
  382.          unmakepat( template );
  383.          fseek( Ifile, pop(), 0 );  /* back up one line  */
  384.          --Line;
  385.          line ( 0xcd, 1);
  386.          printpage();
  387.      }
  388.  
  389.      strcpy( opat, pat );
  390.  }
  391.  
  392.  /*----------------------------------------------------------------------*/
  393.  
  394.  execute()
  395.  {
  396.      /*     Spawn off a child process. When the process terminates
  397.       *     print a message and redraw the current page. Note that
  398.       *     spawn() is used (rather than system()) so you can't
  399.       *     execute a batch file or a built-in command. This
  400.       *     subroutine will set the CMDLINE environment variable
  401.       *     to a null string for the sake of those routines that
  402.       *     are executing under the shell which will use it.
  403.       */
  404.  
  405.      static   char     buf[128];
  406.      char         *tail = " ";
  407.  
  408.      static   char     obuf[128], *otail = obuf;
  409.  
  410.      register char     *p;
  411.      register int     c;
  412.      int         got_tail = 0;
  413.  
  414.      printf("!");
  415.  
  416.      if( !(tail = getbuf(buf)) )         /* If no command entered, */
  417.      {                     /* use the same one we    */
  418.          tail = otail;             /* used last time       */
  419.          memcpy( buf, obuf, 128 );
  420.          printf( "\n!%s %s\n", buf, tail );
  421.      }
  422.      else
  423.      {
  424.          if( *tail )
  425.              *tail++ = '\0';
  426.      }
  427.  
  428.      if( HAS_DOT(buf) )
  429.      {
  430.          /* Spawnlp will actually try to execute any file that you
  431.           * give it. If you say to execute an ASCII file, it will
  432.           * load that file into memory, try to execute it, and die
  433.           * a horrible death. We attempt to avoid this by checking
  434.           * for a dot in the file name. You may want to put a
  435.           * more rigorous test here.
  436.           */
  437.  
  438.          fprintf(stderr,"\007<%s> is not a command\n", buf);
  439.      }
  440.      else
  441.      {
  442.          putenv("CMDLINE=");
  443.          if( spawnlp(P_WAIT, buf, buf, tail, NULL) == -1)
  444.                fprintf(stderr,"Can't execute <%s %s>\n", buf, tail );
  445.  
  446.      }
  447.  
  448.      printf("Hit any key to continue ....");
  449.      getcon();
  450.      erase_line();
  451.      putchar('\n');
  452.  
  453.      otail = tail;
  454.      memcpy( obuf, buf, 128 );
  455.  }
  456.  
  457.  /*----------------------------------------------------------------------*/
  458.  
  459.  line( c , newline )
  460.  {
  461.      /*     Print a line of characters to mark top of page. 0xcd
  462.       *     is the IBM graphics character for a horizontal double
  463.       *     line. The cursor is put at the beginning of next line
  464.       *     if "newline" is true, else it's put at beginning of
  465.       *     current line.
  466.       */
  467.  
  468.      register int     i;
  469.  
  470.      putchar('\r');
  471.  
  472.      for( i = 79; --i >= 0 ; putchar( c ) )
  473.          ;
  474.  
  475.      putchar( newline ? '\n' : '\r' );
  476.  
  477.  }
  478.  
  479.  /*------------------------------------------------------------------*/
  480.  
  481.  backapage( count )
  482.  {
  483.      /*     Go back count pages and print the resulting page.
  484.      */
  485.  
  486.      register int     i;
  487.  
  488.      i = ((count+1) * PAGESIZE) -1;
  489.  
  490.      while( --i >= 0 )
  491.      {
  492.          Line--;
  493.          pop();
  494.      }
  495.  
  496.      line ( 0xcd, 1);
  497.      fseek( Ifile, pop(), 0 );
  498.      Line = max( Line - 1, 0 );
  499.      printpage();
  500.  }
  501.  
  502.  /*------------------------------------------------------------------*/
  503.  
  504.  docmd( cmd, ateof )
  505.  {
  506.      /*     Do a single command, return 1 if next file is requested.
  507.       *     Actually call exit on a "quit" command or ^C.
  508.       */
  509.  
  510.      register int     rval = 0;
  511.      register int     i;
  512.      long         posn;
  513.  
  514.      do {
  515.          switch( cmd )
  516.          {
  517.          case CAN:     break;      /* NOP          */
  518.          case 'q':     exit(0);     /* abort         */
  519.  
  520.          case '\n':             /* FORWARD MOTION     */
  521.              if( ateof )         /* one line         */
  522.                  rval = 1;
  523.              else
  524.                  inputline(0);
  525.              break;
  526.  
  527.          case ' ':             /* one page         */
  528.              if( ateof )
  529.                  rval = 1;
  530.  
  531.              printpage();
  532.              break;
  533.  
  534.          case 'e':             /* To end of file     */
  535.              erase_line();
  536.              while( inputline(1) && !khit() )
  537.                  percent("\r");
  538.              break;
  539.  
  540.          case 's':             /* one line w/o printing */
  541.              if( ateof )
  542.                  rval = 1;
  543.              else
  544.              {
  545.                  erase_line();
  546.                  inputline(1);
  547.                  percent("\r");
  548.              }
  549.              break;
  550.  
  551.          case ESC:               /* scroll till key is hit */
  552.              if( ateof )
  553.                  rval = 1;
  554.              else
  555.                  while( inputline(0) && !khit() )
  556.                      clear_io();
  557.  
  558.              clear_io();
  559.              Repeat_count = 0;     /* Ignore repeat count  */
  560.              break;          /* if it's set          */
  561.  
  562.          case 'n':             /* to next file     */
  563.              rval = 1;
  564.              break;
  565.  
  566.          case '/':             /* search for pattern     */
  567.              search();
  568.              break;
  569.  
  570.          case 'r':             /* to start of file     */
  571.              line( 0xcd, 1 );
  572.              CLEAR_STACK();
  573.              Line = 0;
  574.              fseek( Ifile, 0L, 0 );
  575.              printpage();
  576.              break;
  577.  
  578.          case 'b':             /* to previous page     */
  579.              backapage( Repeat_count );
  580.              Repeat_count = 0;
  581.              break;
  582.  
  583.          case 'o':             /* print file position  */
  584.  
  585.              printf("Top line = %ld, ",     BACK_SCRN );
  586.              printf("Bottom line = %ld\n",     TOS       );
  587.              break;
  588.  
  589.          case '!':
  590.              /* Close the file and spawn another shell.
  591.               * when we come back, reopen the file
  592.               * and position to the same place we
  593.               * were before. This is necessary because of
  594.               * a bug in Microsoft C ver. 3.0's spawn functions
  595.               * (they trash the IOB). It will cause problems
  596.               * if standard input is used as the input source
  597.               * (as in a pipe) because we won't be able to
  598.               * successfully reopen stdin.
  599.               */
  600.  
  601.              Repeat_count = 0;     /* Ignore repeat count */
  602.              fclose( Ifile );
  603.              execute();
  604.              posn = pop();
  605.  
  606.              if( Ifile = fopen(Ifile_name, "r") )
  607.              {
  608.                  fseek( Ifile, posn, 0 );
  609.                  backapage( 0 );
  610.              }
  611.              else
  612.              {
  613.                  fprintf(stderr,"more: can't open %s\n",
  614.                                  Ifile_name);
  615.                  rval = 1;
  616.              }
  617.              break;
  618.  
  619.          default :             /* Print the help msg.  */
  620.              help();
  621.              cmd = getcmd();     /* get a new command     */
  622.              Repeat_count++;
  623.              break;
  624.          }
  625.  
  626.      } while( --Repeat_count > 0 );
  627.  
  628.      return( rval );
  629.  }
  630.  
  631.  /*----------------------------------------------------------------------*/
  632.  
  633.  dofile( fname )
  634.  char     *fname;
  635.  {
  636.      /*     Process lines from an input file having the indicated
  637.       *     name.
  638.       */
  639.  
  640.      if( (Ifile_name = fname)  &&  !(Ifile = fopen(fname, "r")) )
  641.          fprintf(stderr, "more: can't open %s\n", fname );
  642.      else
  643.      {
  644.          Flen = filelength( fileno(Ifile) );
  645.          fseek( Ifile, Start_here, 0 );
  646.  
  647.          CLEAR_STACK();
  648.          docmd(' ', 0 );         /* dump the first page */
  649.  
  650.          for(;;)
  651.          {
  652.              for(;;)
  653.              {
  654.                  if( docmd( getcmd(), 0) )
  655.                      return;
  656.  
  657.                  if( feof(Ifile) )
  658.                      break;
  659.              }
  660.  
  661.              E("\n\020\020\020 LAST LINE IN FILE \021\021\021");
  662.              if( docmd( getcmd(), 1) )
  663.                  break;
  664.          }
  665.  
  666.          fclose( Ifile );
  667.      }
  668.  }
  669.  
  670.  /*------------------------------------------------------------------*/
  671.  
  672.  main(argc, argv)
  673.  char     **argv;
  674.  {
  675.      ctlc();
  676.      reargv(&argc, &argv);
  677.  
  678.      if( argc > 1 )
  679.      {
  680.          if( argv[1][0] == '-' )
  681.              usage();
  682.  
  683.          else if (argv [1][0] == '+' )
  684.          {
  685.              Start_here = atol( &argv[1][1] );
  686.              printf("Starting at character %ld\n", Start_here );
  687.              push  ( Start_here );
  688.              ++argv;
  689.              --argc;
  690.          }
  691.      }
  692.  
  693.      if( argc <= 1 )
  694.          dofile( NULL );
  695.      else
  696.          for(; --argc > 0 ; dofile(*++argv) )
  697.              ;
  698.  
  699.      exit(0);
  700.  }
  701.