home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 May / macformat-024.iso / Shareware City / Developers / nshellmegasource1.50 / mega src / commands / wc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-30  |  11.9 KB  |  540 lines  |  [TEXT/KAHL]

  1. /* ========== the commmand file: ==========
  2.  
  3.     wc.c
  4.     
  5.     Copyright (c) 1993,1994 Newport Software Development
  6.     
  7.     You may distribute unmodified copies of this file for
  8.     noncommercial purposes.  You may use this file as a
  9.     reference when writing your own nShell(tm) commands.
  10.     
  11.     All other rights are reserved.
  12.     
  13.    ========== the commmand file: ========== */
  14.  
  15. #ifdef __MWERKS__            // CodeWarrior requires an A4 setup
  16. #include <A4Stuff.h>
  17. #endif
  18.  
  19. #define    FILE_BUF_SIZE        2048
  20.  
  21. #define    CONSOLE_BUF_SIZE    200
  22.  
  23. #include "nshc.h"
  24. #include "arg_utl.proto.h"
  25. #include "fss_utl.proto.h"
  26. #include "str_utl.proto.h"
  27. #include "nshc_utl.proto.h"
  28.  
  29. /* ======================================== */
  30.  
  31. // our data record, NewHandled and attached to nshc_parms->data
  32.  
  33. typedef struct {
  34.  
  35.     int        by_file;        // 1 if files are listed, 0 if stdin
  36.     
  37.     int        got_fss;        // 0 if FSSpec calls are not available
  38.     int        arg;            // position in arg list (if by file)
  39.     short    fref;            // file ref. number, 0 if no file open
  40.     int        fcount;            // number of files processed
  41.     int        inword;            // 1 if we are in the middle of a word
  42.     
  43.     int        got_options;    // 1 if any option was given
  44.     int        line_option;    // show-line option was given
  45.     int        word_option;    // show-word option was given
  46.     int        bytes_option;    // show-bytes option was given
  47.  
  48.     long    lines;            // number of carridge returns (seen a carridge lately?)
  49.     long    lines_total;    // total number of carridge returns for all files
  50.  
  51.     long    words;            // number of "words" (actually things between whitespace)
  52.     long    words_total;    // total number of words for all files
  53.  
  54.     long    bytes;            // number of bytes
  55.     long    bytes_total;    // total number of bytes for all files
  56.     
  57. } t_wc_data;
  58.  
  59. typedef t_wc_data **t_wc_handle;
  60.  
  61. /* ======================================== */
  62.  
  63. // prototypes - utility
  64.  
  65. void wc_bad( t_nshc_parms *nshc_parms, int code );
  66. void wc_bad_file( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, StringPtr msg );
  67. int  wc_by_file( t_nshc_parms *nshc_parms );
  68. void wc_good( t_nshc_parms *nshc_parms );
  69. int     wc_whitespace( char c );
  70.  
  71. // prototypes - print routines
  72.  
  73. void wc_print_one( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_wc_data **ourData, int arg );
  74. void wc_print_total( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_wc_data **ourData );
  75.  
  76. // prototypes - file access routines
  77.  
  78. void wc_open( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_wc_data **ourData );
  79. void wc_process_file( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_wc_data **ourData );
  80.  
  81. // console access routines
  82.  
  83. void wc_process_console( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_wc_data **ourData );
  84.  
  85. // prototypes - state machine
  86.  
  87. void wc_continue( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls );
  88. void wc_start( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls );
  89. void wc_stop( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls );
  90.  
  91. /* ======================================== */
  92.  
  93. // utility
  94.  
  95. int    wc_by_file( t_nshc_parms *nshc_parms )
  96. {
  97.  
  98.     // if we have any operands, then we count by file
  99.     
  100.     int    arg;
  101.     
  102.     arg = 1;
  103.     
  104.     while ( arg < nshc_parms->argc ) {
  105.         if ( nshc_is_operand( nshc_parms, arg ) )
  106.             return( 1 );
  107.         arg++;
  108.         }
  109.         
  110.     return( 0 );
  111. }
  112.  
  113. void wc_bad(  t_nshc_parms *nshc_parms, int code )
  114. {
  115.     nshc_parms->action = nsh_stop;
  116.     nshc_parms->result = code;
  117. }
  118.  
  119. void wc_bad_file(  t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, StringPtr msg )
  120. {
  121.     nshc_calls->NSH_putStr_err("\pwc: File access error (");
  122.     nshc_calls->NSH_putStr_err(msg);
  123.     nshc_calls->NSH_putStr_err("\p)\r");
  124.  
  125.     nshc_parms->action = nsh_stop;
  126.     nshc_parms->result = NSHC_ERR_GENERAL;
  127. }
  128.  
  129. void wc_good(  t_nshc_parms *nshc_parms )
  130. {
  131.     nshc_parms->action = nsh_stop;
  132.     nshc_parms->result = 0;
  133. }
  134.  
  135. int    wc_whitespace( char c )
  136. {
  137.     switch (c) {
  138.         case ' ':
  139.         case '\t':
  140.         case '\r':
  141.             return(1);
  142.         default:
  143.             return(0);
  144.         }
  145. }
  146.  
  147. /* ======================================== */
  148.  
  149. // print routines
  150.  
  151. /* ======================================== */
  152.  
  153. void wc_print_one( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_wc_data **ourData, int arg )
  154. {
  155.     int    all;
  156.     
  157.     all = !(**ourData).got_options;
  158.     
  159.     if (all || (**ourData).line_option )
  160.         nshc_calls->NSH_printf( "%7ld ", (**ourData).lines );
  161.     
  162.     if (all || (**ourData).word_option )
  163.         nshc_calls->NSH_printf( "%7ld ", (**ourData).words );
  164.     
  165.     if (all || (**ourData).bytes_option )
  166.         nshc_calls->NSH_printf( "%7ld ", (**ourData).bytes );
  167.  
  168.     if (arg)
  169.         nshc_calls->NSH_puts( &nshc_parms->arg_buf[nshc_parms->argv[arg]] );
  170.         
  171.     nshc_calls->NSH_putchar('\r');
  172. }
  173.  
  174. void wc_print_total( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_wc_data **ourData )
  175. {
  176.     int    all;
  177.     
  178.     all = !(**ourData).got_options;
  179.  
  180.     if (all || (**ourData).line_option )
  181.         nshc_calls->NSH_printf( "%7ld ", (**ourData).lines_total );
  182.     
  183.     if (all || (**ourData).word_option )
  184.         nshc_calls->NSH_printf( "%7ld ", (**ourData).words_total );
  185.     
  186.     if (all || (**ourData).bytes_option )
  187.         nshc_calls->NSH_printf( "%7ld ", (**ourData).bytes_total );
  188.         
  189.     nshc_calls->NSH_putStr("\ptotal\r");
  190. }
  191.  
  192. /* ======================================== */
  193.  
  194. // file access routines
  195.  
  196. /* ======================================== */
  197.  
  198. void wc_open( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_wc_data **ourData )
  199. {
  200.     int        result;
  201.     long    dirID;
  202.     Boolean    isDir;
  203.     FSSpec    fss;
  204.     
  205.     (**ourData).fref = 0;
  206.  
  207.     // a good time to initialize counts, etc.
  208.         
  209.     (**ourData).lines = 0;
  210.     (**ourData).words = 0;
  211.     (**ourData).bytes = 0;
  212.     (**ourData).inword = 0;
  213.  
  214.     // =====> convert path to fsspec
  215.     
  216.     if ( nshc_is_operand( nshc_parms, (**ourData).arg ) ) {
  217.         result = arg_to_fss( nshc_parms, nshc_calls, (**ourData).arg, &fss );
  218.         (**ourData).arg++;
  219.         }
  220.     else {
  221.         (**ourData).arg++;
  222.         return;
  223.         }
  224.     
  225.     if (result) {
  226.         wc_bad( nshc_parms, result );
  227.         return;
  228.         }
  229.     
  230.     result = fss_to_DirID( &fss, &dirID, &isDir );
  231.         
  232.     if (( result == noErr) && isDir) {
  233.         nshc_calls->NSH_putStr_err("\pwc: Skiping directory = ");
  234.         nshc_calls->NSH_putStr_err((StringPtr)fss.name);
  235.         nshc_calls->NSH_putchar('\r');
  236.         return;
  237.         }
  238.             
  239.     if ( result == fnfErr ) {
  240.         nshc_calls->NSH_putStr_err("\pwc: File not found = ");
  241.         nshc_calls->NSH_putStr_err((StringPtr)fss.name);
  242.         nshc_calls->NSH_putchar('\r');
  243.         return;
  244.         }
  245.             
  246.     if (!result)
  247.         result = fss_OpenDF((**ourData).got_fss, &fss, fsRdPerm, &(**ourData).fref);
  248.     
  249.     if ( result ) {
  250.         wc_bad_file( nshc_parms, nshc_calls, (StringPtr)fss.name );
  251.         return;
  252.         }
  253. }
  254.  
  255. /* ======================================== */
  256.  
  257. void wc_process_file( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_wc_data **ourData )
  258. {
  259.     int        close;
  260.     int        temp;
  261.     int        result;
  262.     long    bcount;
  263.     char    buf[FILE_BUF_SIZE];
  264.     char    c;
  265.     int        i;
  266.     
  267.     result = close = 0;
  268.     
  269.     // =====> copy the data fork
  270.         
  271.     bcount = FILE_BUF_SIZE;
  272.     result = FSRead( (**ourData).fref, &bcount, &buf );
  273.     
  274.     if (( result == noErr ) || ( result == eofErr )) {
  275.     
  276.         if (result == eofErr) {
  277.             close = 1;
  278.             result = 0;
  279.             }
  280.         
  281.         if (bcount && !result) {
  282.         
  283.             (**ourData).bytes += bcount;
  284.  
  285.             for (i=0 ; i<bcount; i++ ) {
  286.                 c = buf[i];
  287.                 if ( c == '\r' ) (**ourData).lines++;
  288.                 temp = wc_whitespace( c );
  289.                 if ( temp && (**ourData).inword )
  290.                     (**ourData).words++;
  291.                 (**ourData).inword = !temp;        
  292.                 }
  293.                 
  294.             }
  295.         
  296.         }
  297.     else
  298.         wc_bad_file( nshc_parms, nshc_calls, "\pread resource" );
  299.     
  300.     // =====> close the input file
  301.     
  302.     if (close) {
  303.     
  304.         // close
  305.  
  306.         if ( (**ourData).fref ) {
  307.             if ( temp = FSClose( (**ourData).fref ) )
  308.                 result = temp;
  309.             (**ourData).fref = 0;
  310.             }
  311.             
  312.         // if an error occured, bail.  otherwise compute & print
  313.  
  314.         if (result)
  315.             wc_bad_file( nshc_parms, nshc_calls, "\pclose file" );
  316.         else {
  317.         
  318.             if ( (**ourData).inword )    // if we finish in a word, it's still a word
  319.                 (**ourData).words++;
  320.                 
  321.             (**ourData).fcount++;
  322.             
  323.             (**ourData).lines_total += (**ourData).lines;
  324.             (**ourData).words_total += (**ourData).words;
  325.             (**ourData).bytes_total += (**ourData).bytes;
  326.             
  327.             wc_print_one( nshc_parms, nshc_calls, ourData, (**ourData).arg - 1 );
  328.         
  329.             }
  330.         
  331.         }
  332. }
  333.  
  334. /* ======================================== */
  335.  
  336. // console access routines
  337.  
  338. /* ======================================== */
  339.  
  340. #define    LINES_PER_PASS    10
  341.  
  342. void wc_process_console( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_wc_data **ourData )
  343. {
  344.     long    bcount;
  345.     char    buf[CONSOLE_BUF_SIZE];
  346.     char    c;
  347.     int        i;
  348.     int        temp;
  349.     int        pass;
  350.     
  351.     // if characters are waiting, pick up a few lines
  352.         
  353.     pass = 1;
  354.     
  355.     while ( pass++ < LINES_PER_PASS ) {
  356.     
  357.         bcount = nshc_calls->NSH_gets( buf, CONSOLE_BUF_SIZE );
  358.         
  359.         // if no characters are waiting, return to app
  360.         
  361.         if (!bcount) return;
  362.         
  363.         // otherwise, proccess the line
  364.                     
  365.         i = 0;
  366.         
  367.         while ( c = buf[i++] ) {
  368.         
  369.             (**ourData).bytes++;
  370.     
  371.             if ( c == '\r' ) (**ourData).lines++;
  372.             
  373.             temp = wc_whitespace( c );
  374.             if ( temp && (**ourData).inword )
  375.                 (**ourData).words++;
  376.             (**ourData).inword = !temp;        
  377.                 
  378.             }
  379.  
  380.         // end of input, display and exit
  381.  
  382.         if ( bcount == -1 ) {
  383.         
  384.             if ( (**ourData).inword )    // if we finish in a word, it's still a word
  385.                 (**ourData).words++;
  386.             
  387.             wc_print_one( nshc_parms, nshc_calls, ourData, 0 );
  388.             wc_good( nshc_parms );
  389.             
  390.             return;
  391.     
  392.             }
  393.     }
  394. }
  395.  
  396. /* ======================================== */
  397.  
  398. // state machine - core routines
  399.  
  400. /* ======================================== */
  401.  
  402. void wc_start( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls )
  403. {
  404.     t_wc_handle    ourData;
  405.  
  406.     nshc_parms->action = nsh_idle;
  407.     nshc_parms->result = NSHC_NO_ERR;
  408.     
  409.     ourData = (t_wc_handle)NewHandleClear( sizeof(t_wc_data) );
  410.  
  411.     if (ourData) {
  412.  
  413.         // setup for by_file (or by stdin)
  414.     
  415.         (**ourData).by_file = wc_by_file( nshc_parms );
  416.         
  417.         (**ourData).arg = 1;
  418.  
  419.         // test if we can use FSSpec calls
  420.                 
  421.         (**ourData).got_fss = fss_test();
  422.         
  423.         // record user options
  424.         
  425.         (**ourData).line_option = nshc_got_option( nshc_parms, 'l' );
  426.         (**ourData).word_option = nshc_got_option( nshc_parms, 'w' );
  427.         (**ourData).bytes_option = nshc_got_option( nshc_parms, 'c' );
  428.         
  429.         (**ourData).got_options = (**ourData).line_option ||
  430.                                   (**ourData).word_option ||
  431.                                   (**ourData).bytes_option;
  432.                                  
  433.         nshc_parms->data = (Handle)ourData;
  434.         nshc_parms->action = nsh_continue;
  435.         }
  436.     else {
  437.         nshc_calls->NSH_puts("wc: Could not allocate storage.\r");
  438.         nshc_parms->result = NSHC_ERR_MEMORY;
  439.         }
  440. }
  441.  
  442. /* ======================================== */
  443.  
  444. void wc_continue( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls )
  445. {
  446.     t_wc_handle    ourData;
  447.     
  448.     if (ourData = (t_wc_handle)nshc_parms->data) {
  449.  
  450.         if ( (**ourData).by_file ) {
  451.             
  452.             // if we are not in the middle of a file, open one
  453.  
  454.             if ( !(**ourData).fref )
  455.                 wc_open( nshc_parms, nshc_calls, ourData );
  456.             
  457.             // read and proccess some of the open file
  458.  
  459.             if ( (**ourData).fref )
  460.                 wc_process_file( nshc_parms, nshc_calls, ourData );
  461.             
  462.             // and see if we are done
  463.  
  464.             if ( !(**ourData).fref && ( (**ourData).arg >= nshc_parms->argc ) ) {
  465.                 wc_good( nshc_parms );
  466.                 return;
  467.                 }
  468.  
  469.             }
  470.         else
  471.             wc_process_console( nshc_parms, nshc_calls, ourData );
  472.  
  473.         }
  474. }
  475.  
  476. /* ======================================== */
  477.  
  478. void wc_stop( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls )
  479. {        
  480.     short        fRef;
  481.     t_wc_handle    ourData;
  482.     int            temp;
  483.     int            result;
  484.     
  485.     result = 0;
  486.     
  487.     if (ourData = (t_wc_handle)nshc_parms->data) {
  488.  
  489.         // these are unsuccessfull closes only - so no file totals are printed
  490.     
  491.         if ( (**ourData).fref ) {
  492.             if ( temp = FSClose( (**ourData).fref ) )
  493.                 result = temp;
  494.             (**ourData).fref = 0;
  495.             }
  496.             
  497.         if (result)
  498.             wc_bad_file( nshc_parms, nshc_calls, "\pclosing files" );
  499.             
  500.         // if there have been no errors, print the grand total
  501.         
  502.         if ( !nshc_parms->result && ( (**ourData).fcount > 1 ) )
  503.             wc_print_total( nshc_parms, nshc_calls, ourData );
  504.             
  505.         DisposeHandle(nshc_parms->data);
  506.         }
  507.         
  508.         
  509.     nshc_parms->action = nsh_idle;
  510. }
  511.  
  512. /* ======================================== */
  513.  
  514. void main(t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls)
  515. {
  516. #ifdef __MWERKS__
  517.     long oldA4  = SetCurrentA4();
  518. #endif
  519.     
  520.     if ( !nshc_bad_version( nshc_parms, nshc_calls, NSHC_VERSION ) ) {
  521.         
  522.         switch (nshc_parms->action) {
  523.             case nsh_start:
  524.                 wc_start(nshc_parms,nshc_calls);
  525.                 break;
  526.             case nsh_continue:
  527.                 wc_continue(nshc_parms,nshc_calls);
  528.                 break;
  529.             default:
  530.                 wc_stop(nshc_parms,nshc_calls);
  531.                 break;
  532.             }
  533.             
  534.         }
  535.     
  536. #ifdef __MWERKS__
  537.     SetA4(oldA4);
  538. #endif
  539. }
  540.