/*********************************************************/ /* 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 */ }