/*********************************************************/ 
/* This sample SAS INFILE/FILE exit only selects a       */
/* record when the data (at a particular column in the   */
/* record) compare exactly to a user-specified string.   */
/* The SAMPVAL option can be used to specify the string, */
/* and the SAMPCOL option can be used to specify the     */
/* the column number.                                    */
/*                                                       */
/* additional options:                                   */
/*                                                       */
/*  SAMPEQ=  selects the record only when the data are   */
/*            equal to the user string (the default).    */
/*  SAMPNEQ= selects the record only when the data are   */
/*            not equal to the user string.              */
/*                                                       */
/*  Sample code to invoke this sample exit:              */
/*                                                       */
/*     DATA _NULL_;                                      */
/*          INFILE fileref SMP1 SAMPVAL=abc  SAMPCOL=10; */                                    
/*          INPUT;                                       */ 
/*     RUN;                                              */ 
/* This routine has been written using SAS/C language.   */ 
**********************************************************/ 

   /* header files and declarations                      */ 
#include                                            
#include                                          
#include                                          
#include                                         
typedef char *ptr;                                          
typedef char char8 8 ;                                      

   /* declare SAS routines as assembler                  */ 
typedef void __asm(*u_vlfptr)();                            

   /* define exit function codes                         */ 
#define EXITINIT    0                                       
#define EXITPARS    4                                       
#define EXITOPEN    8                                       
#define EXITREAD   12                                       
#define EXITCONC   16                                       
#define EXITWRIT   20                                       
#define EXITCLOS   24                                       

   /* UEBCB: user exit bag control block                 */ 
struct UEBCB                                                
  {                                                         
   ptr exitidb;          /* exit routine idb             */
   ptr exitfptr;         /* exit routine entry point     */
   int memalen;          /* length of memory             */
   ptr memabove;         /* memory above line            */
   int memblen;          /* length of memory             */
   ptr membelow;         /* memory below line            */
   ptr exitbag;          /* pointer to the UEBCB         */
   char8 ddname;         /* DDname                       */
   ptr varrtn;           /* variable create routine      */
   ptr errmsg;           /* pointer to null terminated   */
                         /* error message string         */
   char flag1;           /* flag byte                    */
#define EX_NEXT 0x80     /* get next record from exit    */
#define EX_DEL  0x40     /* delete this record           */
#define EX_EOF  0x20     /* end of file reached          */
#define EX_EOFC 0x10     /* call after end of file       */
#define EX_ALC  0x08     /* will use SAS service         */
                         /* routines                     */
   char flag2;           /* flag byte                    */
   char flag3;           /* flag byte                    */
   char flag4;           /* flag byte                    */
   u_vlfptr alloc;       /* allocate without switch      */
   u_vlfptr free;        /* free without switch          */
   ptr     pida;         /* poolid                       */
   ptr     pidb;         /* poolid                       */
   u_vlfptr alloc1;      /* allocate routine with        */
                         /* switch                       */
   u_vlfptr free1;       /* free routine with switch     */
   u_vlfptr varrtn1;     /* variable routine with        */
                         /* switch                       */
   long    rsv;          /* reserved by the SAS System   */
   u_vlfptr log;         /* log routine                  */    
   u_vlfptr log1;        /* log routine without switch   */    
  };                                                           

   /* user exit info structure                           */    
struct UEXIT                                                   
  {                                                            
  char    string[255];  /* string to compare             */
  int     strlen;       /* length of string              */    
  int     col;          /* starting column               */    
  int     numrec;       /* record number                 */    
  char   msgdata[256];  /* message area                  */
  char    flag;         /* flag                          */    
#define COMPNEQ  0x80   /* use NE compare                */    
#define COMPEQ   0x40   /* use EQ compare (default)      */    
  };                                                           

   /*----------------------------------------------------*/    
   /* Parms passed to SMP1EXT are                        */    
   /*                                                    */    
   /*    func-function code (int)                        */ 
   /*    uebcb-pointer to exit bag (struct UEBCB*).      */ 
   /*                                                    */    
   /* There is a variable list of arguments depending    */    
   /* on the function code being processed.              */    
   /*----------------------------------------------------*/    
int  smp1ext(func,uebcb)                                       
int func;                                                      
struct UEBCB* uebcb;                                           

{                                                              
 va_list ap;        /* required for varying argument     */    
                    /* list                              */    
 int     rc;                                                   
 struct UEXIT* uexit;                                          
 static char mess1[] = "INVALID OPTION";                       

   /* set up for varying argument list                   */    
 va_start(ap,uebcb);                                           
 switch (func)      /* start of switch                   */    
 {                                                             

   /*---------------------EXITINIT-----------------------*/   
   /*  Additional arguments are                          */   
   /*                                                    */   
   /*     memabove-amount of work area needed above      */
   /*                     the 16M line (int *)           */   
   /*     membelow-amount of work area needed below      */
   /*                     the 16M line (int *).          */   
   /*                                                    */   
   /*----------------------------------------------------*/   
case EXITINIT:               /* INIT function            */    
{                                                            
 int *memabove,*membelow;    /* pointer to ints          */    
 membelow=va_arg(ap,int*);   /* get 3rd argument         */    
 memabove=va_arg(ap,int*);   /* get 4th argument         */    

   /* get memory for our user exit info structure        */    
   /* and inform the SAS System                          */           
*memabove=sizeof(struct UEXIT );                                   

   /* no memory below the 16M line is needed             */           
*membelow=0;                                                       

uebcb->flag1 |= EX_ALC; /* user exit will use SAS        */           
                        /* service routines              */           
                                                                      
rc=0;                   /* set good return code          */           
break;                  /* Done with EXITINIT            */           
}                                                                   

     /*-------------------- EXITPARS---------------------*/           
     /*  Additional arguments are                        */
     /*                                                  */   
     /*     optnamel-length of option name (int)         */
     /*     optname-pointer to option name (char*)       */
     /*     optvall-option value length or 0 (int)       */
     /*     optval-pointer to option value (char*).      */
     /*--------------------------------------------------*/           
  case EXITPARS:            /* PARSE function            */           
  {                                                                   

      /* declare some local variables                    */           
   int  optnamel,optvall;                                             
   char *optname,*optval;                                             
   char * stopchar;                                                  

      /* get pointer to the UEXIT info structure         */           
   uexit=(struct UEXIT*) uebcb->memabove;                             

      /* get the optional args ...                       */ 
   optnamel=va_arg(ap,int);                                
   optname=va_arg(ap,char*);                               
   optvall=va_arg(ap,int);                                 
   optval=va_arg(ap,char*);                                

      /* if option name is SMP1, set default flag        */ 
   if (optnamel==4 && !memcmp(optname,"SMP1",4))            
    {                                                       
     uexit->flag |= COMPEQ; /* default to compare        */ 
                            /* equal                     */ 
     rc=0;                                                  
    }                                                       

      /* if option name is SAMPVAL, get                  */ 
      /* string value                                    */ 
   else if (optnamel==7 \                             
        && !memcmp(optname,"SAMPVAL",7) \             
        && optvall!=0)                                      
    {                                                       
     uexit->strlen=optvall;                                 
     memcpy(uexit->string,optval,optvall);                  
     rc=0;                                                  
    }                                                       

      /* if option name is SAMPCOL, get column value     */ 
   else if (optnamel==7 \                             
             && !memcmp(optname,"SAMPCOL",7) \        
             && optvall!=0) 
    {                                                      

        /* convert string value to integer               */
     stopchar=optval+optvall ;                             
        /* save it in the info structure                 */
     uexit->col=strtol(optval,&stopchar,10) ;              
     rc=0;                                                 
    }                                                      

      /* if option name is SAMPNEQ, set flag             */
   else if (optnamel==7 \                            
             && !(memcmp(optname,"SAMPNEQ",7)))            
    {                                                      
     uexit->flag &= ~COMPEQ;                               
     uexit->flag |= COMPNEQ;                               
     rc=0;                                                 
    }                                                      

      /* if option name is SAMPEQ, set flag              */
   else if (optnamel==6 \                            
             && !(memcmp(optname,"SAMPEQ",6)))             
    {                                                      
     uexit->flag &= ~COMPNEQ;                              
     uexit->flag |= COMPEQ;                                
     rc=0;                                                 
    }                                                      

      /* invalid options, set error and return           */
   else                                                    
    { 
     rc=8;                                                    
     uebcb->errmsg = (char*) mess1;                           
    }                                                         
  break;           /* done with exitpars                 */   
    }                                                         

     /*-------------------- EXITREAD---------------------*/   
     /*  Additional arguments are                        */   
     /*                                                  */   
     /*     reca-pointer to record address (ptr *)       */
     /*     recl-pointer to record length (int *).       */
     /*--------------------------------------------------*/   
  case EXITREAD:                                              
  {                                                           
      /* declare local variables                         */   
   int* recl;                                                 
   char**  reca;                                              
   int  reclen,comprc,len,col;                                
   char*   rec;                                               
   int done = 0;                                              
                                                              
   uebcb->flag1 &= ~EX_DEL; /* clear flag                */   

      /* get pointer to the UEXIT info structure         */   
   uexit = (struct UEXIT*) uebcb->memabove;                   

      /* get the optional arguments                      */   
   reca=va_arg(ap,char**);    /* pointer to record       */   
                              /* address                 */
   recl=va_arg(ap,int*);      /* pointer to record       */
                              /* address                 */
   rec=*reca;                 /* record address          */
   reclen=*recl;              /* record length           */
   uexit->numrec++;           /* increment record        */
                              /* number                  */
   rc=0;                                                   

      /* if the starting column is greater than record   */
      /* length, do not look for a string                */
   col=uexit->col;                                         
   if (col < reclen )                                      
    {                     
                                 
        /* take the lesser of the two                    */
     len=((reclen-col)>uexit->strlen ? uexit->strlen       
          : reclen-col);                                   
     comprc=memcmp(rec+col-1,uexit->string,len);           

        /* if record is selected only when it compares   */
        /* not equal and it is equal, mark it deleted    */
     if (uexit->flag & COMPNEQ && comprc != 0)             
      {                                                    
       done=1;                                           
      }                                                    

        /* if record is selected only when it compares   */
        /* equal and it is not equal, mark it deleted    */
     if (!done && uexit->flag & COMPEQ && comprc==0)       
     {                                                     
      done=1;                                              
     }                                                     

        /* write message to the SAS log if a record was  */
        /* rejected                                      */
     if (!done)                                            
     {                                                     
      sprintf(uexit->msgdata,                              
              "record number %d has not been selected",    
              uexit->numrec);                              

        /* Call SAS log service to write message to      */
        /* SAS log. Use the alternate entry point that   */
        /* does environment switching.                   */
      ((*uebcb->log1))(uebcb,&uexit->msgdata);             

      uebcb->flag1 |= EX_DEL; /*  flag it as deleted     */    
     }                                                     
    }                                                      

        /* Error: column is greater than record length   */
    else                                                   
    {                                                      
      sprintf(uexit->msgdata,                              
              "column is greater than reclen %d for \
record number %d",                                         
               reclen, uexit->numrec);                     

        /* Call SAS log service to write message to      */
        /* SAS log. Use the alternate entry point that   */
        /* does environment switching.                   */
      (*(uebcb->log1))(uebcb,&uexit->msgdata);             
    }                                                      
    break;        /* done with exitread                  */
   }                                                       

     /*-------------------EXITCLOS-----------------------*/
     /*  no additional arguments are provided            */
     /*--------------------------------------------------*/
  case EXITCLOS:                                           

     /*Just terminate the C function                     */
   exit();                                                 
   break;                                                  

  default:      /* all other EXIT function codes         */
  rc=0;                                                  
  break;                                                   
 }              /* end of switch                         */
 return(rc);    /* return with return code set           */
}