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