YAAB (Yet another awk bug) - Fix for '%*' bug
P. Vijay
vijay at topaz.RUTGERS.EDU
Fri Sep 13 18:40:50 AEST 1985
> DESCRIPTION:
> Awk's version of 'printf' core dumps or prints garbage when a field width
> variable is used. The manual page refers to printf(3S) so I assume this
> feature of printf should be implemented.
>
> REPEAT BY:
> % echo 5 |awk '{printf("%*s\n",$1,"a")}'
> Segmentation fault (core dumped)
CAUSE:
All printf(fmt,v1,v2,...,vn) statements are tokenised.
Eventually, format() is called for each individual tokens, with the
format string pointer being updated every invocation. The format
string is parsed in a rather simplistic fashion, which does not look
out for '%*' like constructs. So, a statement like 'printf("%*s",$1,"a")'
results in a call 'sprintf("%*s",$1)', which ends up accessing some
bogus pointer somewhere, thus dumping core.
FIX:
Make the format string parsing more sophisticated, and set
booleans to indicate presence of variable fieldwidth and/or precision.
Also, conditional to these booleans, call sprintf with appropriate
arguments.
CAVEAT:
Since this situation does not occur very often ("%*" in
printf), and since printf itself is typically a heavily called routine
in awk programs, the parsing for '%*' is done in a not so robust
fashion. Calls such as 'printf("%**.22s\n",$1,"Foo")' might cause the
awk program to croak without indicating the proper error.
----------P-A-T-C-H----T-O----RUN.C----F-O-L-L-O-W-S------------
*** run.c.ORIG Fri Feb 10 06:53:37 1984
--- run.c Fri Sep 13 04:04:26 1985
***************
*** 321,326
int flag = 0;
awkfloat xf;
os = s;
p = buf = (char *)malloc(RECSIZE);
while (*s) {
--- 321,339 -----
int flag = 0;
awkfloat xf;
+ /* [P. Vijay - Sep 1985] Fix the problem of awk dumping core when
+ '*' is present either in the precision or field width spec. in
+ the format string.
+
+ Reason: Presently this routine does not check for the presence
+ of '*' in the said places, and as a result, sprintf is called
+ with insufficient params. [e.g., "sprintf("%*s\n",x.opt->sval)"].
+ */
+
+ int VarFieldWidth; /* Set 'true' if '*' used to spec. fld. wid. */
+ int VarPrecision; /* Set 'true' if '*' used to spec. precision */
+ int FieldWidth , Precision; /* Hold value of respective specifier */
+
os = s;
p = buf = (char *)malloc(RECSIZE);
while (*s) {
***************
*** 334,340
continue;
}
for (t=fmt; (*t++ = *s) != '\0'; s++)
! if (*s >= 'a' && *s <= 'z' && *s != 'l')
break;
*t = '\0';
if (t >= fmt + sizeof(fmt))
--- 347,353 -----
continue;
}
for (t=fmt; (*t++ = *s) != '\0'; s++)
! if (*s >= 'a' && *s <= 'z' && *s != 'l')
break;
*t = '\0';
if (t >= fmt + sizeof(fmt))
***************
*** 339,344
*t = '\0';
if (t >= fmt + sizeof(fmt))
error(FATAL, "format item %.20s... too long", os);
switch (*s) {
case 'f': case 'e': case 'g':
flag = 1;
--- 352,367 -----
*t = '\0';
if (t >= fmt + sizeof(fmt))
error(FATAL, "format item %.20s... too long", os);
+
+ /* [P. Vijay - Sep 1985] Check for presence of '%*', '%*.',
+ and/or '%.*'. This is a QuikFix!! No checking for specs.
+ such as '%**.12', etc. Someday, this should be fixed to
+ do the task in a more robust fashion...
+ */
+
+ VarFieldWidth = fmt[1] == '*';
+ VarPrecision = (*(s-2) == '.') && (*(s-1) == '*');
+
switch (*s) {
case 'f': case 'e': case 'g':
flag = 1;
***************
*** 368,373
p += strlen(p);
continue;
}
if (a == NULL)
error(FATAL, "not enough arguments in printf(%s)", os);
x = execute(a);
--- 391,410 -----
p += strlen(p);
continue;
}
+ if (VarFieldWidth){
+ if (a == NULL)
+ error(FATAL, "not enough arguments in printf(%s)", os);
+ x = execute(a);
+ FieldWidth = getfval(x.optr);
+ a = a->nnext;
+ }
+ if (VarPrecision){
+ if (a == NULL)
+ error(FATAL, "not enough arguments in printf(%s)", os);
+ x = execute(a);
+ Precision = getfval(x.optr);
+ a = a->nnext;
+ }
if (a == NULL)
error(FATAL, "not enough arguments in printf(%s)", os);
x = execute(a);
***************
*** 374,383
a = a->nnext;
if (flag != 4) /* watch out for converting to numbers! */
xf = getfval(x.optr);
! if (flag==1) sprintf(p, fmt, xf);
! else if (flag==2) sprintf(p, fmt, (long)xf);
! else if (flag==3) sprintf(p, fmt, (int)xf);
! else if (flag==4) sprintf(p, fmt, x.optr->sval==NULL ? "" : getsval(x.optr));
tempfree(x);
p += strlen(p);
s++;
--- 411,434 -----
a = a->nnext;
if (flag != 4) /* watch out for converting to numbers! */
xf = getfval(x.optr);
!
! /* [P. Vijay - Sep 1985] To avoid a mess of repetitive
! code sequences, define 'Xsprintf' macro to do the
! actual sprintf's.
! */
!
! #define Xsprintf(str,fmt,var)\
! if (!VarFieldWidth)\
! if (!VarPrecision) sprintf(str,fmt,(var));\
! else sprintf(str,fmt,Precision,(var));\
! else\
! if (!VarPrecision) sprintf(str,fmt,FieldWidth,(var));\
! else sprintf(str,fmt,FieldWidth,Precision,(var))
!
! if (flag==1) Xsprintf(p, fmt, xf);
! else if (flag==2) Xsprintf(p, fmt, (long)xf);
! else if (flag==3) Xsprintf(p, fmt, (int)xf);
! else if (flag==4) Xsprintf(p, fmt, x.optr->sval==NULL ? "" : getsval(x.optr));
tempfree(x);
p += strlen(p);
s++;
More information about the Net.bugs
mailing list