Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site topaz.RUTGERS.EDU Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!genrad!panda!talcott!harvard!seismo!columbia!topaz!vijay From: vijay@topaz.RUTGERS.EDU (P. Vijay) Newsgroups: net.bugs Subject: Re: YAAB (Yet another awk bug) - Fix for '%*' bug Message-ID: <3643@topaz.RUTGERS.EDU> Date: Fri, 13-Sep-85 04:40:50 EDT Article-I.D.: topaz.3643 Posted: Fri Sep 13 04:40:50 1985 Date-Received: Sat, 14-Sep-85 16:54:32 EDT References: <224@ur-cvsvax.UUCP> , <2577@pegasus.UUCP> Organization: Rutgers Univ., New Brunswick, N.J. Lines: 175 > 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++;