From: Kevin Myers on
Under SAS 9.2 I have several user defined functions that have been =
compiled using PROC FCMP. These functions work fine when I use them in =
data step code. However, when I attempt to use any of these functions =
in a macro via %SYSFUNC, I get an error message that the function cannot =
be found, e.g.:

116 data _null_;
117
118 n=3Dnchars("asdf ");
119 put n=3D;
120
121 c=3Dxsubstr("asdf ",-4,2);
122 put c=3D;
123
124 n=3Dxindexc("asdf ","sd",1,1,"","","","");
125 put n=3D;
126
127 run;
n=3D5
c=3Dsd
n=3D2
NOTE: DATA statement used (Total process time):
real time 0.15 seconds
cpu time 0.15 seconds
?
ERROR: The NCHARS function referenced in the %SYSFUNC or %QSYSFUNC macro =
function is not found.
128
129 %put %sysfunc(nchars(asdf));
130 %put %sysfunc(xsubstr(asdf,2,2));
ERROR: The XSUBSTR function referenced in the %SYSFUNC or %QSYSFUNC =
macro function is not found.
131 %put %sysfunc(xindexc(asdf,sd,1,1,%str(),%str(),%str(),%str()));
ERROR: The XINDEXC function referenced in the %SYSFUNC or %QSYSFUNC =
macro function is not found.

My nchars function is left over from V8 days, and is easily replaced =
with the lengthc function in V9, but the other 2 functions (for which I =
previously used SAS/Toolkit functions in V8) are not so easily replaced, =
as there are still no equivalent built-in functions in V9.2, and I no =
longer have access to SAS/Toolkit in 9.2.

Shouldn't these functions work with %SYSFUNC? Is this a known bug? Any =
fixes/workarounds/alternate suggestions? The proc fcmp code for these =
functions is provided below.

Thanks,
s/KAM


proc fcmp outlib=3DSASUtils.KAMUtils.V8Functions;

function nChars(inStr $);
return (lengthc(inStr));
endsub;

function xSubstr(inStr $, aStart, length) $;
if aStart<0 then start=3Dlengthc(inStr) + aStart + 1; else =
start=3DaStart;
if missing(length) then return (substrn(inStr,start));
else return (substrn(inStr,start,length));
endsub;

function xIndexC(inStr $, search $, aStart, aValNo, aQuotes1 $, =
aQuotes2 $, aParens1 $, aParens2 $);

length start valNo 8 quotes1 quotes2 parens1 parens2 $256;
length cInstr cQuotes1 cQuotes2 cParens1 cParens2 valNo2 8;
length inChar $1 strTemp $256;
length cTemp inc cPos quoteNo parenNo nParens 8;
array parens[1] /NOSYMBOLS;

start=3DaStart; valNo=3DaValNo;
quotes1=3DaQuotes1; quotes2=3DaQuotes2; parens1=3DaParens1; =
parens2=3DaParens2;

cInstr=3Dlengthc(inStr);
call dynamic_array(parens,cInstr);

/* get start position and occurance arg values */

if missing(start) then start=3D1;
else if start<0 then start=3DcInstr+ceil(start)+1;
else start=3Dfloor(start);

if missing(valNo) then valNo=3D1;
else if valNo<0 then valNo=3Dceil(valNo);
else valNo=3Dfloor(valNo);

if start<=3DcInstr and valNo>=3D1 or start>=3D1 and valNo<=3D-1 then =
do;

/* adjust start position and determine increment */

if valNo>0 then do;
inc=3D1;
if start<1 then start=3D1;
end;
else do;
inc=3D-1;
if start>cInstr then start=3DcInstr;
end;

/* get optional quote and parenthesis arg values */

cQuotes1=3Dlengthn(quotes1);
if lengthn(quotes2)=3D0 then quotes2=3Dquotes1;
cQuotes2=3Dlengthn(quotes2);

cParens1=3Dlengthn(parens1);
cParens2=3Dlengthn(parens2);

if inc<0 then do;

/* if searching backwards swap quote and paren arg values */

strTemp=3Dquotes1; quotes1=3Dquotes2; quotes2=3DstrTemp;
cTemp=3DcQuotes1; cQuotes1=3DcQuotes2; cQuotes2=3DcTemp;

strTemp=3Dparens1; parens1=3Dparens2; parens2=3DstrTemp;
cTemp=3DcParens1; cParens1=3DcParens2; cParens2=3DcTemp;

end;

valNo2=3Dabs(valNo);
quoteNo=3D0; /* not yet in quoted string */
nParens=3D0; /* not yet in parentheses */

cPos=3Dstart; dPos=3D0;

*put inStr=3D search=3D start=3D valNo=3D quotes1=3D quotes2=3D =
parens1=3D parens2=3D;
*put cInstr=3D inc=3D cQuotes1=3D cQuotes2=3D cParens1=3D =
cParens2=3D;
*put valNo2=3D quoteNo=3D nParens=3D;

do while (cPos>=3D1 and cPos<=3DcInstr and not dPos);

inChar=3Dsubstrn(inStr,cPos,1);
*put / cPos=3D inChar=3D quoteNo=3D nParens=3D;

if quoteNo then do;

/* process quoted string */

/* if closing quote found signal non-quoted mode */

if quoteNo<=3DcQuotes2 and inChar=3Dsubstrn(quotes2,quoteNo) then =
do;
quoteNo=3D0; *put quoteNo=3D; end;

end; /* if quoteNo */

else /* if not quoteNo */ do;

/* not in quoted string */

/* if quote found then save closing quote */
/* which also signals quoted string mode */

if indexc(quotes1,inChar) then do;
quoteNo=3Dindexc(quotes1,inChar);
*put quoteNo=3D;
end;

/* if open parenthesis found then save closing paren */
/* in parens array and increment paren levels counter */
/* which also signals parenthesized string mode */

else if indexc(parens1,inChar) then do;
nParens+1;
parenNo=3Dindexc(parens1,inChar);
parens[nParens]=3DparenNo;
*put nParens=3D parenNo=3D;
end;

else if nParens then do;

/* process parenthesized string */

/* if closing parenthesis found then decrement */
/* paren levels counter and set new closing paren */

if parenNo<=3DcParens2 and inChar=3Dsubstrn(parens2,parenNo,1) =
then do;
nParens+(-1);
if nParens then parenNo=3Dparens[nParens];
*put nParens=3D parenNo=3D;
end;

end; /* else if nParens */

/* if search character found then set return value */

else if indexc(search,inChar) then do;
valNo2+(-1);
if valNo2<1 then dPos=3DcPos;
*put valNo2=3D dPos=3D;
end;

end; /* if not quoteNo */

/* increment input character pointer */
cPos+inc;

end; /* while (cPos>=3D1 and cPos<=3DcInstr and not dPos) */

if not dPos then do; dPos=3DcPos; *put dPos=3D; end;

end; /* if start not past EOS */

else if start<1 then do; dPos=3D0; *put dPos=3D; end;
else if start>lengthc(inStr) then do; dPos=3Dlengthc(inStr)+1; *put =
dPos=3D; end;
else do; dPos=3Dstart; *put dPos=3D; end; /* valNo=3D0 */

return (dPos);

endsub;

run;