/*======================================================================
*     
*  Title:        VMSGREP - A generalized regular expression parser for VMS
*                
*  Version:      1-001
*                
*  Abstract:     GREP is used to search for regular patterns in VMS files.
*                
*  Environment:  VMS
*                
*  Date:         21-AUG-1986
*  
*---------------------------------------------------------------------*/

#include stdio
#include descrip
#include climsgdef
#include rmsdef
#include ctype
#include "tools.h"

#define MAX_LINE 515               /*    Maxiumum size of an input line
                                    */

#define MAX_EXPR 64                /*    The maximum number of regular
                                    *    expressions separated by |
                                    */

/* THIS MACRO PLACES CORRECT STRING SIZE IN THE LENGTH FIELD OF DESCRIPTOR */
#define size_descrip(dsc) dsc.dsc$w_length = (short)strlen( dsc.dsc$a_pointer )

/* THIS MACRO TESTS IF A NUMBER IS ODD */
#define isodd(n) ((n) & 1)

/* THE FOLLOWING ARE EXTERNAL FORTRAN ROUTINES */
int fortran_read();
int fortran_open();
int fortran_close();

/*   The following global flags are true if a switch was set
 *   in the command line, false otherwise.
 */
 
int   vflag, yflag, cflag, nflag, hflag, oflag, printed_header;
int   numfiles;
CHAR  files[20][256], pattern[256], outfile[256];

/* ------------------------------------------------------------------- */

main()
{
   int    lib$find_file(), str$trim();

   int    ff, jj, linenum, count, num_matches;
   short  len_file;
   CHAR   line[MAX_LINE], outline[MAX_LINE], file_name[256];
   FILE   *input;
   int    exprc, context, status;
   TOKEN  *exprv[MAX_EXPR];

   $DESCRIPTOR( dsc_file, files );
   $DESCRIPTOR( dsc_file_name, file_name );
   $DESCRIPTOR( dsc_line, line );

   /*  GET THE COMMAND LINE OPTIONS  */

   get_options();

   /*  TRANSLATE THE PATTERN STRING.  */

   if ((exprc = get_expr( exprv, MAX_EXPR, pattern )) == 0 )
      print_error( "No pattern on command line" );

   ff = 0;

   while (numfiles--)
      {
      dsc_file.dsc$a_pointer = &files[ff++][0];
      size_descrip( dsc_file );

      dsc_file_name.dsc$w_length = 255;
      context = 0;

      while (status = lib$find_file( &dsc_file, &dsc_file_name, &context ),
             isodd( status ))
         {
         str$trim( &dsc_file_name, &dsc_file_name, &len_file );
         file_name[len_file] = '\0';
         size_descrip( dsc_file_name );

         if (!fortran_open( &dsc_file_name ))
            {
            fprintf( stderr, "Unable to open %s\n", file_name );
            exit();
            }

         printed_header = FALSE;
         count = 0;
         linenum = 1;

         while (fortran_read( &dsc_line ))
            {
            strcpy( outline, line );

            if (!yflag)
               stoupper(line);

            for (jj = exprc, num_matches = 0 ; !num_matches && --jj >= 0 ;)
               {
               if (matchs( line , exprv[jj] ))
                  num_matches = 1;
               }

            if ( num_matches ) 
               {
               count++;
               if (!vflag) 
                  pr_match( file_name, linenum, outline );
               }
            else
               {
               if (vflag)
                  pr_match( file_name, linenum, outline );
               }

            linenum++;
            }
         pr_count( file_name, --linenum, count );
         fortran_close();

         dsc_file_name.dsc$w_length = 255;
         }

      /* IF THERE IS AN ERROR OTHER THAN NO MORE FILES FROM FILE SPEC */

      if (!isodd( status ) && status != RMS$_NMF )
         {
         str$trim( &dsc_file_name, &dsc_file_name, &len_file );
         file_name[len_file] = '\0';
         fprintf( stderr, "Unable to find %s\n", file_name );
         exit();
         }
      }
   fclose( stdout );
   exit();
}

/* ---------------------------------------------------------------------- */

pr_count( fname, lines, count)
int     lines, count;
CHAR    *fname;
{

   /*    Process the -c flag by printing out a count and,
    *    if more than one file was listed on the command line,
    *    the file name too.
    */

   if (!cflag)
      return;

   fprintf( stderr, "%s - %d records, %d matches\n", fname, lines, count );
}

/* -------------------------------------------------------------------- */

pr_match( file_name, linenum, line )
int     linenum;
CHAR    *line, *file_name;
{
   /* IF NO FILE NAME HEADER HAS BEEN PRINTED YET THEN DO IT */

   if (!oflag) return;

   if (!printed_header && hflag)
      {
      fprintf( stdout, "\n******************************\n" );
      fprintf( stdout, "%s\n\n", file_name );
      printed_header = TRUE;
      }

   /*   IF A MATCH IS FOUND PRINT THE CORRECT THING   */
   /*   AS SPECIFIED BY THE COMMAND LINE SWITCHES     */

   if (nflag)
      fprintf( stdout, "%6d\t%s", linenum, line );
   else
      fprintf( stdout, "%s", line );

   return;
}

/* ----------------------------------------------------------------------- */

get_options()
{
   /*
    *   Parse command line setting appropriate switches and
    *   getting all of the files and the pattern
    */

   /* FUNCTIONS */
   int cli$get_value(), cli$present();   

   /* LOCAL DECLARATIONS */
   short int len_file, len_pattern, len_outfile;
   int       status;
   CHAR      sfile[5] =    "FILE";
   CHAR      spattern[8] = "PATTERN";
   CHAR      sexact[6] =   "EXACT";
   CHAR      sheading[8] = "HEADING";
   CHAR      slog[4] =     "LOG";
   CHAR      snumbers[8] = "NUMBERS";
   CHAR      soutput[7] =  "OUTPUT";
   CHAR      snot[4] =     "NOT";

   /* LOCAL DESCRIPTORS */
   $DESCRIPTOR( dsc_file, files );
   $DESCRIPTOR( dsc_sfile, sfile );
   $DESCRIPTOR( dsc_pattern, pattern );
   $DESCRIPTOR( dsc_spattern, spattern );
   $DESCRIPTOR( dsc_sexact, sexact );
   $DESCRIPTOR( dsc_sheading, sheading );
   $DESCRIPTOR( dsc_slog, slog );
   $DESCRIPTOR( dsc_snumbers, snumbers );
   $DESCRIPTOR( dsc_output, outfile );
   $DESCRIPTOR( dsc_soutput, soutput );
   $DESCRIPTOR( dsc_snot, snot );

   /* ZERO ALL OF THE FLAGS */

   vflag = 0;
   cflag = 0;
   nflag = 0;
   hflag = 0;
   oflag = 0; 
   yflag = 0; 

   /* GET THE FILES */
   numfiles = 0;

   while (status = cli$get_value( &dsc_sfile, &dsc_file, &len_file ),
          status != CLI$_ABSENT )
      {
      files[numfiles][len_file] = '\0';

      numfiles++;

      /* IF THAT IS THE LAST FILE OR MAX REACHED THEN FINISH */
      if ( numfiles > 20 || !isodd( status ))
         break;

      /* SET THE FILE DESCRIPTOR TO POINT TO THE NEXT FILE  */
      /* AND SET THE LENGTH BACK TO 255                     */
      dsc_file.dsc$a_pointer = &files[numfiles][0];
      dsc_file.dsc$w_length = 255;
      } 

   /* GET THE PATTERN */
   status = cli$get_value( &dsc_spattern, &dsc_pattern, &len_pattern );
   pattern[len_pattern] = '\0';
   
   /* GET THE OUTPUT FILE IF THERE IS ONE */
   if (isodd( cli$present( &dsc_soutput ) )) 
      {
      oflag = 1;

      status = cli$get_value( &dsc_soutput, &dsc_output, &len_outfile );

      if (isodd( status ))
         {
         outfile[len_outfile] = '\0';  /* TERMINATE THE STRING */

         /* ADD .LIS TO THE FILE TYPE IF THERE IS NO FILE TYPE IN FILE */
         add_file_type( outfile, "LIS" );
   
         /* ASSIGN output TO THE OUTPUT FILE - IF THERE IS ONE */
         if ((stdout = fopen( outfile, "w", "rfm=var", "rat=cr" )) == NULL)
            print_error( "Unable to open output file %s", outfile );
         }
      }

   /* SET GLOBAL FLAGS CORRESPONDING TO SPECIFIC SWITCHES
    * IF THOSE SWITCHES ARE SET IN THE COMMAND LINE
    */

   if ( cli$present( &dsc_sexact ) == CLI$_PRESENT ) 
      yflag = 1;
   
   if (isodd( cli$present( &dsc_sheading ) ) &&
          (numfiles > 1 ||
             strchr( &files[0][0], '*' ) ||
                strchr( &files[0][0], '%' )))
      hflag = 1;
   
   if ( cli$present( &dsc_slog ) == CLI$_PRESENT ) 
      cflag = 1;
   
   if ( cli$present( &dsc_snumbers ) == CLI$_PRESENT ) 
      nflag = 1;
   
   if ( cli$present( &dsc_snot ) == CLI$_PRESENT ) 
      vflag = 1;

   return;
}

/* ---------------------------------------------------------------------- */

int do_or( lp, expr, maxexpr )
CHAR    *lp;
TOKEN   **expr;
int     maxexpr;
{
   int    found;
   TOKEN  *pat;
   CHAR   *op;

   found = 0;

   /*  
    *     Extract regular expressions separated by OR_SYMs from
    *     lp and put them into expr. Extract only up to
    *     maxexpr expressions. 
    */

   if (!yflag)
      stoupper( lp );

   while (op = index(OR_SYM, lp))
      {
      if (found <= maxexpr && (pat = makepat(lp, OR_SYM)) )
         {
         *expr++ = pat;
         found++;
         }
      lp = op;

      if ( pat == 0 )
         goto FATAL_ERR;
      }

   if (found <= maxexpr  &&  (pat = makepat( lp, OR_SYM)))
      {
      found++;
      *expr = pat;
      }

   if ( pat == 0 )
      {
FATAL_ERR:
      fprintf( stderr, "Illegal expression: %x\n", lp);
      exit();
      }

   return (found);
}

/* ------------------------------------------------------------------------ */ 

get_expr( expr, maxexpr, defexpr )
TOKEN   *expr[];
int     maxexpr;
CHAR    *defexpr;
{
   int     count;

#ifdef DEBUG
   int    i;
#endif

   /*   Get regular expressions separated by | 
    *   The expressions are converted into pattern templates and
    *   pointers to the templates are put into the array expr[]
    *   (which works similar to argv).
    *
    *   Return the number of expressions found (which can be used     
    *   in a similar fashions to argc).
    */

   count = 0;

   /*     
    *     *defexpr is the expression itself.
    */

   if (count += do_or( defexpr, &expr[count], maxexpr - count))   
      defexpr = " ";

#ifdef DEBUG

   /*     Print out all the regular expressions after they have been
    *     converted into pattern templates (see tools.c).
    */

   for (i = count; --i >= 0 ; )
      {
      pr_tok( expr[i] );
      fprintf( stdout, "---------------------------------------------\n");
      }

#endif

   return (count);
}

/* ------------------------------------------------------------------------ */ 

print_error( message )
CHAR *message;
{
   fprintf( stderr, "%s\n", message );
   exit();
}

/* ------------------------------------------------------------------------ */ 

add_file_type( name, def_type )
CHAR *name, *def_type;
{
   int ii;

   ii = strlen( name );

   /* RETURN IF THERE IS A "." ALREADY AFTER ANY DIRECORY OR DEVICE SPEC */
   while( ii && name[ii] != ']' && name[ii] != ':' && name[ii] != '>' )
      {
      if ( name[ii--] == '.' )
         return;
      }

   /* ADD A PERIOD IF THERE IS NO PERIOD IN THE def_type */
   if (strchr( def_type, '.' ) == NULL)
      strcat( name, "." );

   /* ADD THE FILE TYPE */
   strcat( name, def_type );

return;
}
