134226Sbostic /* 234233Sbostic * Copyright (c) 1988 Regents of the University of California. 334233Sbostic * All rights reserved. 434226Sbostic * 534233Sbostic * Redistribution and use in source and binary forms are permitted 634233Sbostic * provided that this notice is preserved and that due credit is given 734233Sbostic * to the University of California at Berkeley. The name of the University 834233Sbostic * may not be used to endorse or promote products derived from this 934233Sbostic * software without specific prior written permission. This software 1034233Sbostic * is provided ``as is'' without express or implied warranty. 1134226Sbostic */ 1234226Sbostic 1334233Sbostic #if defined(LIBC_SCCS) && !defined(lint) 14*34330Sbostic static char sccsid[] = "@(#)vfprintf.c 5.26 (Berkeley) 05/19/88"; 1534233Sbostic #endif /* LIBC_SCCS and not lint */ 1634233Sbostic 1734261Sbostic #include <sys/types.h> 1834233Sbostic #include <varargs.h> 1934226Sbostic #include <stdio.h> 2034233Sbostic #include <ctype.h> 2134226Sbostic 2234328Sbostic /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ 2334328Sbostic #define MAXEXP 308 2434328Sbostic /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ 2534328Sbostic #define MAXFRACT 39 2634226Sbostic 2734328Sbostic #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 2834328Sbostic 2934323Sbostic #define PUTC(ch) {++cnt; putc((char)ch, fp);} 3034236Sbostic 3134318Sbostic #define ARG() \ 3234318Sbostic _ulong = flags&LONGINT ? va_arg(argp, long) : \ 3334318Sbostic flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 3434235Sbostic 3534318Sbostic /* have to deal with the negative buffer count kludge */ 3634318Sbostic #define NEGATIVE_COUNT_KLUDGE 3734318Sbostic 3834318Sbostic #define LONGINT 0x01 /* long integer */ 3934318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 4034318Sbostic #define SHORTINT 0x04 /* short integer */ 4134318Sbostic #define ALT 0x08 /* alternate form */ 4234318Sbostic #define LADJUST 0x10 /* left adjustment */ 4334318Sbostic 4434323Sbostic _doprnt(fmt0, argp, fp) 4534323Sbostic u_char *fmt0; 4634233Sbostic va_list argp; 4734235Sbostic register FILE *fp; 4834226Sbostic { 4934323Sbostic register u_char *fmt; 5034323Sbostic register int ch, cnt, n; 5134323Sbostic register char *t; 5234235Sbostic double _double; 5334314Sbostic u_long _ulong; 5434328Sbostic int base, flags, fpprec, prec, size, width; 5534328Sbostic char padc, sign, *digs, buf[BUF], *_cvt(); 5634226Sbostic 5734323Sbostic fmt = fmt0; 5834243Sbostic digs = "0123456789abcdef"; 5934322Sbostic for (cnt = 0;; ++fmt) { 6034318Sbostic n = fp->_cnt; 6134318Sbostic for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt) 6234318Sbostic if (--n < 0 6334318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE 6434318Sbostic && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 6534318Sbostic #endif 6634318Sbostic || ch == '\n' && fp->_flag&_IOLBF) { 6734318Sbostic fp->_cnt = n; 6834318Sbostic fp->_ptr = t; 6934318Sbostic (void)_flsbuf(ch, fp); 7034318Sbostic n = fp->_cnt; 7134318Sbostic t = fp->_ptr; 7234318Sbostic } 7334318Sbostic else 7434314Sbostic *t++ = ch; 7534318Sbostic fp->_cnt = n; 7634318Sbostic fp->_ptr = t; 7734318Sbostic if (!ch) 7834314Sbostic return(cnt); 7934314Sbostic 8034328Sbostic flags = fpprec = width = 0; 8134233Sbostic prec = -1; 8234233Sbostic padc = ' '; 8334318Sbostic sign = '\0'; 8434226Sbostic 8534318Sbostic rflag: switch (*++fmt) { 8634318Sbostic case ' ': 8734318Sbostic sign = ' '; 8834318Sbostic goto rflag; 8934233Sbostic case '#': 9034318Sbostic flags |= ALT; 9134318Sbostic goto rflag; 9234233Sbostic case '*': 9334235Sbostic /* 9434235Sbostic * ``A negative field width argument is taken as a 9534235Sbostic * - flag followed by a positive field width.'' 9634235Sbostic * -- ANSI X3J11 9734235Sbostic * They don't exclude field widths read from args. 9834235Sbostic */ 9934235Sbostic if ((width = va_arg(argp, int)) >= 0) 10034318Sbostic goto rflag; 10134235Sbostic width = -width; 10234235Sbostic /*FALLTHROUGH*/ 10334235Sbostic case '-': 10434318Sbostic flags |= LADJUST; 10534318Sbostic goto rflag; 10634233Sbostic case '+': 10734314Sbostic sign = '+'; 10834318Sbostic goto rflag; 10934233Sbostic case '.': 11034235Sbostic if (*++fmt == '*') 11134318Sbostic n = va_arg(argp, int); 11234318Sbostic else if (isascii(*fmt) && isdigit(*fmt)) { 11334318Sbostic n = 0; 11434233Sbostic do { 11534318Sbostic n = 10 * n + *fmt - '0'; 11634318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 11734233Sbostic --fmt; 11834226Sbostic } 11934235Sbostic else { 12034318Sbostic --fmt; 12134235Sbostic prec = 0; 12234318Sbostic goto rflag; 12334235Sbostic } 12434318Sbostic prec = n < 0 ? -1 : n; 12534318Sbostic goto rflag; 12634233Sbostic case '0': 12734233Sbostic padc = '0'; 12834235Sbostic /*FALLTHROUGH*/ 12934233Sbostic case '1': case '2': case '3': case '4': 13034233Sbostic case '5': case '6': case '7': case '8': case '9': 13134318Sbostic n = 0; 13234233Sbostic do { 13334318Sbostic n = 10 * n + *fmt - '0'; 13434318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 13534318Sbostic width = n; 13634233Sbostic --fmt; 13734319Sbostic goto rflag; 13834235Sbostic case 'L': 13934329Sbostic flags |= LONGDBL; 14034318Sbostic goto rflag; 14134235Sbostic case 'h': 14234318Sbostic flags |= SHORTINT; 14334318Sbostic goto rflag; 14434233Sbostic case 'l': 14534318Sbostic flags |= LONGINT; 14634318Sbostic goto rflag; 14734314Sbostic case 'c': 14834319Sbostic buf[0] = va_arg(argp, int); 14934314Sbostic size = 1; 15034319Sbostic t = buf; 15134314Sbostic goto pforw; 15234314Sbostic case 'd': 15334318Sbostic case 'i': 15434318Sbostic ARG(); 15534318Sbostic if ((long)_ulong < 0) { 15634318Sbostic _ulong = -_ulong; 15734314Sbostic sign = '-'; 15834241Sbostic } 15934241Sbostic base = 10; 16034327Sbostic goto number; 16134261Sbostic case 'e': 16234236Sbostic case 'E': 16334235Sbostic case 'f': 16434261Sbostic case 'g': 16534243Sbostic case 'G': 16634243Sbostic _double = va_arg(argp, double); 16734328Sbostic /* 16834328Sbostic * don't bother to do unrealistic precision; just 16934328Sbostic * pad it with zeroes later. This keeps buffer size 17034328Sbostic * rational. 17134328Sbostic */ 17234328Sbostic if (prec > MAXFRACT) { 17334328Sbostic fpprec = prec - MAXFRACT; 17434328Sbostic prec = MAXFRACT; 17534328Sbostic } 17634327Sbostic size = _cvt(_double, prec, flags, *fmt, padc, &sign, 17734323Sbostic buf, buf + sizeof(buf)) - buf; 17834319Sbostic t = buf; 17934327Sbostic /* 18034327Sbostic * zero-padded sign put out here; blank padded sign 18134327Sbostic * placed in number in _cvt(). 18234327Sbostic */ 18334327Sbostic if (sign && padc == '0') { 18434327Sbostic PUTC(sign); 18534327Sbostic --width; 18634327Sbostic } 18734314Sbostic goto pforw; 18834235Sbostic case 'n': 18934329Sbostic if (flags&LONGINT) 19034318Sbostic *va_arg(argp, long *) = cnt; 19134318Sbostic else if (flags&SHORTINT) 19234318Sbostic *va_arg(argp, short *) = cnt; 19334318Sbostic else 19434318Sbostic *va_arg(argp, int *) = cnt; 19534235Sbostic break; 19634226Sbostic case 'o': 19734318Sbostic ARG(); 19834226Sbostic base = 8; 19934327Sbostic goto nosign; 20034235Sbostic case 'p': 20134320Sbostic /* 20234321Sbostic * ``The argument shall be a pointer to void. The 20334321Sbostic * value of the pointer is converted to a sequence 20434321Sbostic * of printable characters, in an implementation- 20534321Sbostic * defined manner.'' 20634321Sbostic * -- ANSI X3J11 20734320Sbostic */ 20834327Sbostic /*NOSTRICT*/ 20934320Sbostic _ulong = (u_long)va_arg(argp, void *); 21034320Sbostic base = 16; 21134327Sbostic goto nosign; 21234226Sbostic case 's': 21334314Sbostic if (!(t = va_arg(argp, char *))) 21434314Sbostic t = "(null)"; 21534321Sbostic if (prec >= 0) { 21634321Sbostic /* 21734321Sbostic * can't use strlen; can only look for the 21834321Sbostic * NUL in the first `prec' characters, and 21934321Sbostic * strlen() will go further. 22034321Sbostic */ 22134321Sbostic char *p, *memchr(); 22234321Sbostic 22334321Sbostic if (p = memchr(t, 0, prec)) { 22434321Sbostic size = p - t; 22534321Sbostic if (size > prec) 22634321Sbostic size = prec; 22734321Sbostic } 22834321Sbostic else 22934321Sbostic size = prec; 23034321Sbostic } 23134321Sbostic else 23234321Sbostic size = strlen(t); 23334328Sbostic goto pforw; 23434226Sbostic case 'u': 23534318Sbostic ARG(); 23634226Sbostic base = 10; 23734327Sbostic goto nosign; 23834226Sbostic case 'X': 23934226Sbostic digs = "0123456789ABCDEF"; 24034233Sbostic /*FALLTHROUGH*/ 24134226Sbostic case 'x': 24234318Sbostic ARG(); 24334314Sbostic base = 16; 24434326Sbostic /* leading 0x/X only if non-zero */ 24534326Sbostic if (!_ulong) 24634324Sbostic flags &= ~ALT; 24734327Sbostic 24834327Sbostic /* unsigned conversions */ 24934327Sbostic nosign: sign = NULL; 25034326Sbostic /* 251*34330Sbostic * ``... diouXx conversions ... if a precision is 252*34330Sbostic * specified, the 0 flag will be ignored.'' 253*34330Sbostic * -- ANSI X3J11 254*34330Sbostic */ 255*34330Sbostic number: if (prec >= 0) 256*34330Sbostic padc = ' '; 257*34330Sbostic /* 25834326Sbostic * ``The result of converting a zero value with an 25934326Sbostic * explicit precision of zero is no characters.'' 26034326Sbostic * -- ANSI X3J11 26134326Sbostic */ 262*34330Sbostic if (!_ulong && !prec) { 263*34330Sbostic size = 0; 264*34330Sbostic goto pforw; 265*34330Sbostic } 26634327Sbostic 26734328Sbostic t = buf + BUF - 1; 26834233Sbostic do { 26934314Sbostic *t-- = digs[_ulong % base]; 27034314Sbostic _ulong /= base; 27134314Sbostic } while(_ulong); 27234328Sbostic for (size = buf + BUF - 1 - t; size < prec; ++size) 27334324Sbostic *t-- = '0'; 27434328Sbostic digs = "0123456789abcdef"; 27534327Sbostic 27634327Sbostic /* alternate mode for hex and octal numbers */ 27734324Sbostic if (flags&ALT) 27834324Sbostic switch (base) { 27934324Sbostic case 16: 28034324Sbostic /* avoid "00000x35" */ 28134327Sbostic if (padc == ' ') { 28234327Sbostic *t-- = *fmt; 28334327Sbostic *t-- = '0'; 28434328Sbostic size += 2; 28534327Sbostic } 28634327Sbostic else { 28734324Sbostic PUTC('0'); 28834324Sbostic PUTC(*fmt); 28934328Sbostic width -= 2; 29034324Sbostic } 29134324Sbostic break; 29234324Sbostic case 8: 29334324Sbostic if (t[1] != '0') { 29434324Sbostic *t-- = '0'; 29534328Sbostic ++size; 29634324Sbostic } 29734324Sbostic break; 29834314Sbostic } 29934327Sbostic 30034327Sbostic if (sign) { 30134327Sbostic /* avoid "0000-3" */ 30234328Sbostic if (padc == ' ') { 30334327Sbostic *t-- = sign; 30434328Sbostic ++size; 30534328Sbostic } 30634328Sbostic else { 30734327Sbostic PUTC(sign); 30834328Sbostic --width; 30934328Sbostic } 31034327Sbostic } 31134328Sbostic ++t; 31234327Sbostic 31334328Sbostic pforw: if (!(flags&LADJUST) && width) 31434328Sbostic for (n = size + fpprec; n++ < width;) 31534263Sbostic PUTC(padc); 31634328Sbostic if (fp->_cnt - (n = size) >= 0) { 31734328Sbostic cnt += n; 31834328Sbostic fp->_cnt -= n; 31934328Sbostic bcopy(t, fp->_ptr, n); 32034328Sbostic fp->_ptr += n; 32134328Sbostic } 32234328Sbostic else for (; n--; ++t) 32334314Sbostic PUTC(*t); 32434328Sbostic while (fpprec--) 32534328Sbostic PUTC('0'); 32634328Sbostic if (flags&LADJUST) 32734328Sbostic for (n = size + fpprec; ++n < width;) 32834328Sbostic PUTC(' '); 32934226Sbostic break; 33034233Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 33134314Sbostic return(cnt); 33234226Sbostic default: 33334263Sbostic PUTC(*fmt); 33434226Sbostic } 33534226Sbostic } 33634314Sbostic /*NOTREACHED*/ 33734226Sbostic } 33834242Sbostic 33934261Sbostic #define EFORMAT 0x01 34034261Sbostic #define FFORMAT 0x02 34134261Sbostic #define GFORMAT 0x04 34234314Sbostic #define DEFPREC 6 34334261Sbostic 34434323Sbostic static char * 34534327Sbostic _cvt(number, prec, flags, fmtch, padc, sign, startp, endp) 34634242Sbostic double number; 34734261Sbostic register int prec; 34834323Sbostic int flags; 34934323Sbostic u_char fmtch; 35034327Sbostic char padc, *sign, *startp, *endp; 35134242Sbostic { 35234248Sbostic register char *p; 35334261Sbostic register int expcnt, format; 35434248Sbostic double fract, integer, tmp, modf(); 35534261Sbostic int decpt; 35634323Sbostic char *savep; 35734242Sbostic 35834314Sbostic if (prec == -1) 35934242Sbostic prec = DEFPREC; 36034242Sbostic 36134314Sbostic if (number < 0) { 36234327Sbostic *sign = '-'; 36334248Sbostic number = -number; 36434248Sbostic } 36534242Sbostic 36634327Sbostic /* if blank padded, add sign in as part of the number */ 36734327Sbostic if (*sign && padc == ' ') 36834327Sbostic *startp++ = *sign; 36934327Sbostic 37034261Sbostic switch(fmtch) { 37134261Sbostic case 'e': 37234261Sbostic case 'E': 37334261Sbostic format = EFORMAT; 37434261Sbostic break; 37534261Sbostic case 'f': 37634261Sbostic format = FFORMAT; 37734261Sbostic break; 37834261Sbostic case 'g': 37934261Sbostic case 'G': 38034261Sbostic format = GFORMAT; 38134261Sbostic fmtch -= 2; 38234261Sbostic } 38334261Sbostic 38434248Sbostic /* 38534248Sbostic * if the alternate flag is set, or, at least one digit of precision 38634248Sbostic * was requested, add a decimal point, unless it's the g/G format 38734314Sbostic * in which case we require two digits of precision, as it counts 38834248Sbostic * precision differently. 38934248Sbostic */ 39034318Sbostic decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 39134248Sbostic 39234248Sbostic expcnt = 0; 39334323Sbostic p = endp - 1; 39434248Sbostic fract = modf(number, &integer); 39534248Sbostic if (integer) { 39634248Sbostic register char *p2; 39734248Sbostic 39834248Sbostic /* get integer part of number; count decimal places */ 39934248Sbostic for (; integer; ++expcnt) { 40034248Sbostic tmp = modf(integer / 10, &integer); 40134248Sbostic *p-- = (int)((tmp + .03) * 10) + '0'; 40234242Sbostic } 40334248Sbostic 40434248Sbostic /* copy, in reverse order, to start of buffer */ 40534248Sbostic p2 = startp; 40634248Sbostic *p2++ = *++p; 40734248Sbostic 40834248Sbostic /* 40934248Sbostic * if the format is g/G, and the resulting exponent will be 41034248Sbostic * greater than the precision, use e/E format. If e/E format, 41134248Sbostic * put in a decimal point as needed, and decrement precision 41234248Sbostic * count for each digit after the decimal point. 41334248Sbostic */ 41434248Sbostic if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 41534248Sbostic if (format&GFORMAT) { 41634248Sbostic format |= EFORMAT; 41734248Sbostic 41834248Sbostic /* first digit is precision for g/G format */ 41934248Sbostic if (prec) 42034248Sbostic --prec; 42134248Sbostic } 42234248Sbostic if (decpt) 42334248Sbostic *p2++ = '.'; 42434248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 42534248Sbostic 42634318Sbostic /* precision ran out, round */ 42734248Sbostic if (p < endp) { 42834248Sbostic if (*p > '4') { 42934248Sbostic for (savep = p2--;; *p2-- = '0') { 43034248Sbostic if (*p2 == '.') 43134248Sbostic --p2; 43234248Sbostic if (++*p2 <= '9') 43334248Sbostic break; 43434248Sbostic } 43534248Sbostic p2 = savep; 43634248Sbostic } 43734248Sbostic fract = 0; 43834248Sbostic } 43934242Sbostic } 44034248Sbostic /* 44134314Sbostic * g/G in f format; if out of precision, replace digits with 44234314Sbostic * zeroes, note, have to round first. 44334248Sbostic */ 44434248Sbostic else if (format&GFORMAT) { 44534248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 44634248Sbostic /* precision ran out; round and then add zeroes */ 44734248Sbostic if (p < endp) { 44834248Sbostic if (*p > '4') { 44934248Sbostic for (savep = p2--; ++*p2 > '9'; 45034248Sbostic *p2-- = '0'); 45134248Sbostic p2 = savep; 45234248Sbostic } 45334248Sbostic do { 45434248Sbostic *p2++ = '0'; 45534248Sbostic } while (++p < endp); 45634248Sbostic fract = 0; 45734248Sbostic } 45834248Sbostic if (decpt) 45934248Sbostic *p2++ = '.'; 46034242Sbostic } 46134248Sbostic /* f format */ 46234248Sbostic else { 46334248Sbostic for (; ++p < endp; *p2++ = *p); 46434248Sbostic if (decpt) 46534248Sbostic *p2++ = '.'; 46634248Sbostic } 46734248Sbostic p = p2; 46834248Sbostic } 46934248Sbostic /* 47034318Sbostic * if no fraction, the number was zero, and if no precision, can't 47134318Sbostic * show anything after the decimal point. 47234248Sbostic */ 47334248Sbostic else if (!fract || !prec) { 47434248Sbostic *startp++ = '0'; 47534318Sbostic if (decpt && !(format&GFORMAT)) 47634248Sbostic *startp++ = '.'; 47734318Sbostic *startp = '\0'; 47834323Sbostic return(startp); 47934248Sbostic } 48034248Sbostic /* 48134248Sbostic * if the format is g/G, and the resulting exponent will be less than 48234248Sbostic * -4 use e/E format. If e/E format, compute exponent value. 48334248Sbostic */ 48434248Sbostic else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 48534248Sbostic format |= EFORMAT; 48634248Sbostic if (fract) 48734248Sbostic for (p = startp; fract;) { 48834248Sbostic fract = modf(fract * 10, &tmp); 48934248Sbostic if (!tmp) { 49034248Sbostic --expcnt; 49134248Sbostic continue; 49234248Sbostic } 49334248Sbostic *p++ = (int)tmp + '0'; 49434248Sbostic break; 49534248Sbostic } 49634242Sbostic else 49734248Sbostic *p++ = '0'; 49834248Sbostic 49934248Sbostic /* g/G format, decrement precision for first digit */ 50034248Sbostic if (format&GFORMAT && prec) 50134248Sbostic --prec; 50234248Sbostic 50334248Sbostic /* add decimal after first non-zero digit */ 50434248Sbostic if (decpt) 50534248Sbostic *p++ = '.'; 50634242Sbostic } 50734248Sbostic /* 50834248Sbostic * f format or g/G printed as f format; don't worry about decimal 50934248Sbostic * point, if g/G format doesn't need it, will get stripped later. 51034248Sbostic */ 51134242Sbostic else { 51234248Sbostic p = startp; 51334248Sbostic *p++ = '0'; 51434248Sbostic *p++ = '.'; 51534248Sbostic } 51634248Sbostic 51734319Sbostic /* finish out requested precision */ 51834319Sbostic while (fract && prec-- > 0) { 51934319Sbostic fract = modf(fract * 10, &tmp); 52034319Sbostic *p++ = (int)tmp + '0'; 52134319Sbostic } 52234319Sbostic while (prec-- > 0) 52334319Sbostic *p++ = '0'; 52434248Sbostic 52534248Sbostic /* 52634248Sbostic * if any fractional value left, "round" it back up to the beginning 52734248Sbostic * of the number, fixing the exponent as necessary, and avoiding the 52834248Sbostic * decimal point. 52934248Sbostic */ 53034248Sbostic if (fract) { 53134248Sbostic (void)modf(fract * 10, &tmp); 53234248Sbostic if (tmp > 4) { 53334248Sbostic for (savep = p--;; *p-- = '0') { 53434248Sbostic if (*p == '.') 53534248Sbostic --p; 53634248Sbostic if (p == startp) { 53734248Sbostic *p = '1'; 53834248Sbostic ++expcnt; 53934248Sbostic break; 54034248Sbostic } 54134248Sbostic if (++*p <= '9') 54234248Sbostic break; 54334242Sbostic } 54434248Sbostic p = savep; 54534242Sbostic } 54634248Sbostic } 54734248Sbostic 54834248Sbostic /* 54934248Sbostic * if a g/G format and not alternate flag, lose trailing zeroes, 55034248Sbostic * if e/E or g/G format, and last char is decimal point, lose it. 55134248Sbostic */ 55234318Sbostic if (!(flags&ALT)) { 55334248Sbostic if (format&GFORMAT) 55434248Sbostic for (; p[-1] == '0'; --p); 55534248Sbostic if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 55634248Sbostic --p; 55734248Sbostic } 55834248Sbostic 55934248Sbostic /* if an e/E format, add exponent */ 56034248Sbostic if (format&EFORMAT) { 56134248Sbostic *p++ = fmtch; 56234248Sbostic if (--expcnt < 0) { 56334248Sbostic expcnt = -expcnt; 56434248Sbostic *p++ = '-'; 56534242Sbostic } 56634248Sbostic else 56734248Sbostic *p++ = '+'; 56834248Sbostic *p++ = expcnt / 10 + '0'; 56934248Sbostic *p++ = expcnt % 10 + '0'; 57034242Sbostic } 57134323Sbostic return(p); 57234242Sbostic } 573