diff --git a/NEWS b/NEWS index d45052569b01..c45169506bbe 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,13 @@ This documents significant changes in the 1.0 branch of ksh 93u+m. For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0 Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library. +2024-02-03: + +- Fixed: the arithmetic representation of negative integers with a base other + than 10 is no longer incorrectly treated as unsigned long. For example, + typeset -i16 n=-12; echo $n + now correctly outputs '-16#c' and no longer ouputs '16#fffffffffffffff4'. + 2024-01-27: - Fixed: tilde expansion discipline functions (see 2021-03-16) were not diff --git a/src/cmd/ksh93/COMPATIBILITY b/src/cmd/ksh93/COMPATIBILITY index c4b2a2dcb34b..90894551b1a1 100644 --- a/src/cmd/ksh93/COMPATIBILITY +++ b/src/cmd/ksh93/COMPATIBILITY @@ -200,6 +200,12 @@ For more details, see the NEWS file and for complete details, see the git log. the 'set' discipline is now triggered, as it was always documented. (Post-1.0 fix introduced in ksh 93u+m/1.0.9) +37. Non-base-10 signed negative integer values are now output as negative + numbers and no longer incorrectly treated as unsigned long integers + regardless of their type length. For example, 'typeset -i16 n=-12; + echo $n' now correctly prints -16#c instead of 16#fffffffffffffff4. + (Post-1.0 fix introduced in ksh 93u+m/1.0.9) + ____________________________________________________________________________ KSH-93 VS. KSH-88 diff --git a/src/cmd/ksh93/edit/completion.c b/src/cmd/ksh93/edit/completion.c index a3084ff52f85..f375ddcb312b 100644 --- a/src/cmd/ksh93/edit/completion.c +++ b/src/cmd/ksh93/edit/completion.c @@ -652,7 +652,7 @@ int ed_fulledit(Edit_t *ep) hist_flush(sh.hist_ptr); } cp = strcopy((char*)ep->e_inbuf,e_runvi); - cp = strcopy(cp, fmtbase((intmax_t)ep->e_hline,10,0)); + cp = strcopy(cp, fmtint(ep->e_hline,1)); #if SHOPT_VSH ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0); #else diff --git a/src/cmd/ksh93/edit/emacs.c b/src/cmd/ksh93/edit/emacs.c index 853708d6f2fa..734e889eec48 100644 --- a/src/cmd/ksh93/edit/emacs.c +++ b/src/cmd/ksh93/edit/emacs.c @@ -1346,7 +1346,7 @@ static void xcommands(Emacs_t *ep,int count) } return; -# define itos(i) fmtbase((intmax_t)(i),0,0) /* want signed conversion */ +# define itos(i) fmtint(i,0) /* want signed conversion */ case cntl('H'): /* ^X^H show history info */ { diff --git a/src/cmd/ksh93/include/version.h b/src/cmd/ksh93/include/version.h index 233c7aa5fda3..91ff5b698d76 100644 --- a/src/cmd/ksh93/include/version.h +++ b/src/cmd/ksh93/include/version.h @@ -18,7 +18,7 @@ #define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */ #define SH_RELEASE_SVER "1.0.9-beta" /* semantic version number: https://semver.org */ -#define SH_RELEASE_DATE "2024-01-27" /* must be in this format for $((.sh.version)) */ +#define SH_RELEASE_DATE "2024-02-03" /* must be in this format for $((.sh.version)) */ #define SH_RELEASE_CPYR "(c) 2020-2024 Contributors to ksh " SH_RELEASE_FORK /* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */ diff --git a/src/cmd/ksh93/sh.1 b/src/cmd/ksh93/sh.1 index 9e83bb5e28a3..e4b89923c82d 100644 --- a/src/cmd/ksh93/sh.1 +++ b/src/cmd/ksh93/sh.1 @@ -3149,20 +3149,22 @@ Integer constants follow the ANSI C programming language integer constant conventions although only single byte character constants are recognized and character casts are not recognized. -In addition constants can be of the form -\*(OK\f2base\f3#\^\f1\*(CK\f2n\^\fP +.PP +In addition, integer constants can be of the form +\f2base\f3#\^\f1\f2n\^\fP +or (for negative numbers) +\f3-\^\f2base\f3#\^\f1\f2n\^\fP, where .I base\^ is a decimal number between two and sixty-four -representing the arithmetic base -and +representing the arithmetic base, and .I n\^ -is a number in that base. +is an unsigned integer in that base. The digits above 9 are represented by the lower case letters, the upper case letters, .BR @ , and -.B _ +.BR _ , respectively. For bases less than or equal to 36, upper and lower case characters can be used interchangeably. diff --git a/src/cmd/ksh93/sh/args.c b/src/cmd/ksh93/sh/args.c index a2ddc8fad9fb..cee50bf0b3e4 100644 --- a/src/cmd/ksh93/sh/args.c +++ b/src/cmd/ksh93/sh/args.c @@ -751,7 +751,7 @@ struct argnod *sh_argprocsub(struct argnod *argp) chmod(sh.fifo,S_IRUSR|S_IWUSR); /* mkfifo + chmod works regardless of umask */ sfputr(sh.stk,sh.fifo,0); #endif /* SHOPT_DEVFD */ - sfputr(sh.stk,fmtbase((intmax_t)pv[fd],10,0),0); + sfputr(sh.stk,fmtint(pv[fd],1),0); ap = (struct argnod*)stkfreeze(sh.stk,0); sh.inpipe = sh.outpipe = 0; /* turn off job control */ diff --git a/src/cmd/ksh93/sh/array.c b/src/cmd/ksh93/sh/array.c index 2715805b08cf..e7dde7f9cf60 100644 --- a/src/cmd/ksh93/sh/array.c +++ b/src/cmd/ksh93/sh/array.c @@ -2,7 +2,7 @@ * * * This software is part of the ast package * * Copyright (c) 1982-2012 AT&T Intellectual Property * -* Copyright (c) 2020-2023 Contributors to ksh 93u+m * +* Copyright (c) 2020-2024 Contributors to ksh 93u+m * * and is licensed under the * * Eclipse Public License, Version 2.0 * * * @@ -807,7 +807,7 @@ static struct index_array *array_grow(Namval_t *np, struct index_array *arp,int int newsize = arsize(arp,maxi+1); if (maxi >= ARRAY_MAX) { - errormsg(SH_DICT,ERROR_exit(1),e_subscript, fmtbase((intmax_t)maxi,10,0)); + errormsg(SH_DICT,ERROR_exit(1),e_subscript,fmtint(maxi,1)); UNREACHABLE(); } i = (newsize-1)*sizeof(union Value)+newsize; diff --git a/src/cmd/ksh93/sh/init.c b/src/cmd/ksh93/sh/init.c index 453755f8327a..b9e342d9b947 100644 --- a/src/cmd/ksh93/sh/init.c +++ b/src/cmd/ksh93/sh/init.c @@ -712,7 +712,7 @@ static Sfdouble_t nget_rand(Namval_t* np, Namfun_t *fp) static char* get_rand(Namval_t* np, Namfun_t *fp) { intmax_t n = (intmax_t)nget_rand(np,fp); - return fmtbase(n, 10, 0); + return fmtint(n,1); } void sh_reseed_rand(struct rand *rp) @@ -762,7 +762,7 @@ static void put_lineno(Namval_t* np,const char *val,int flags,Namfun_t *fp) static char* get_lineno(Namval_t* np, Namfun_t *fp) { intmax_t n = (intmax_t)nget_lineno(np,fp); - return fmtbase(n, 10, 0); + return fmtint(n,1); } static char* get_lastarg(Namval_t* np, Namfun_t *fp) diff --git a/src/cmd/ksh93/sh/io.c b/src/cmd/ksh93/sh/io.c index 08beece76b3e..d63d7f40414a 100644 --- a/src/cmd/ksh93/sh/io.c +++ b/src/cmd/ksh93/sh/io.c @@ -2256,7 +2256,7 @@ static void sftrack(Sfio_t* sp, int flag, void* data) #ifdef DEBUG if(flag==SF_READ || flag==SF_WRITE) { - char *z = fmtbase((intmax_t)sh.current_pid,0,0); + char *z = fmtint(sh.current_pid,0); write(ERRIO,z,strlen(z)); write(ERRIO,": ",2); write(ERRIO,"attempt to ",11); diff --git a/src/cmd/ksh93/sh/macro.c b/src/cmd/ksh93/sh/macro.c index ef05b560cca1..937c309b56da 100644 --- a/src/cmd/ksh93/sh/macro.c +++ b/src/cmd/ksh93/sh/macro.c @@ -83,7 +83,7 @@ typedef struct _mac_ #define isescchar(s) ((s)>S_QUOTE) #define isqescchar(s) ((s)>=S_QUOTE) #define isbracechar(c) ((c)==RBRACE || (_c_=sh_lexstates[ST_BRACE][c])==S_MOD1 ||_c_==S_MOD2) -#define ltos(x) fmtbase((intmax_t)(x),0,0) +#define ltos(x) fmtint(x,0) /* type of macro expansions */ #define M_BRACE 1 /* ${var} */ diff --git a/src/cmd/ksh93/sh/name.c b/src/cmd/ksh93/sh/name.c index 0faa5ebcfe49..ac87dba3ae25 100644 --- a/src/cmd/ksh93/sh/name.c +++ b/src/cmd/ksh93/sh/name.c @@ -2773,6 +2773,7 @@ char *nv_getval(Namval_t *np) if(numeric) { Sflong_t ll; + int base; if(!up->cp) return "0"; if(nv_isattr (np,NV_DOUBLE)==NV_DOUBLE) @@ -2829,16 +2830,12 @@ char *nv_getval(Namval_t *np) } else ll = *(up->lp); - if((numeric=nv_size(np))==10) - { - if(nv_isattr(np,NV_UNSIGN)) - { - sfprintf(sh.strbuf,"%I*u",sizeof(ll),ll); - return sfstruse(sh.strbuf); - } - numeric = 0; - } - return fmtbase(ll,numeric, numeric&&numeric!=10); + base = nv_size(np); + if(base==10) + return fmtint(ll, nv_isattr(np,NV_UNSIGN)); + /* render a possibly signed non-base-10 integer with its base# prefix */ + sfprintf(sh.strbuf, nv_isattr(np,NV_UNSIGN) ? "%#..*I*u" : "%#..*I*d", base, sizeof ll, ll); + return sfstruse(sh.strbuf); } done: /* diff --git a/src/cmd/ksh93/tests/arith.sh b/src/cmd/ksh93/tests/arith.sh index 40a509dd8fc3..a991a09d0043 100755 --- a/src/cmd/ksh93/tests/arith.sh +++ b/src/cmd/ksh93/tests/arith.sh @@ -1049,5 +1049,13 @@ if [[ $got != '122' ]] # TODO: should be 123 then err_exit "arithmetic assignment to LINENO fails (got $got)" fi +# ====== +# non-base-10 numbers may be negative +# https://github.com/ksh93/ksh/issues/696 +exp=-20#j12 +integer 20 got=$exp +[[ $got == "$exp" ]] || err_exit "negative base-20 number (expected '$exp', got '$got')" +unset got + # ====== exit $((Errors<125?Errors:125)) diff --git a/src/cmd/ksh93/tests/printf.sh b/src/cmd/ksh93/tests/printf.sh index b04ccd564cef..bab80f56ff67 100755 --- a/src/cmd/ksh93/tests/printf.sh +++ b/src/cmd/ksh93/tests/printf.sh @@ -504,5 +504,14 @@ fi unset format gd +# ====== +# negative non-base-10 numbers were mangled as they were incorrectly treated as unsigned +# https://github.com/ksh93/ksh/issues/696 +integer 20 n=20#j12 +printf -v got '%s %s %d %..20d %s' "$n" "$((n *= -1))" n n "$n" +exp='20#j12 -7622 -7622 -j12 -20#j12' +[[ $got == "$exp" ]] || err_exit "issue 696 reproducer (expected $(printf %q "$exp"), got $(printf %q "$got"))" +unset n + # ====== exit $((Errors<125?Errors:125)) diff --git a/src/lib/libast/Mamfile b/src/lib/libast/Mamfile index 181f51e61439..5816801c5f78 100644 --- a/src/lib/libast/Mamfile +++ b/src/lib/libast/Mamfile @@ -1312,12 +1312,6 @@ make install virtual done misc/fmtrec.c exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Icomp -Iinclude -Istd -c misc/fmtrec.c done fmtrec.o - make fmtbase.o - make string/fmtbase.c - prev include/ast.h - done string/fmtbase.c - exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Icomp -Iinclude -Istd -D__OBSOLETE__=20120101 -c string/fmtbase.c - done fmtbase.o make fmtbuf.o make string/fmtbuf.c prev include/ast.h @@ -4303,7 +4297,7 @@ make install virtual exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Icomp -Iinclude -Istd -c vmalloc/vmgetmem.c done vmgetmem.o exec - ${AR} rc libast.a state.o opendir.o readdir.o rewinddir.o seekdir.o telldir.o getcwd.o fastfind.o hashalloc.o hashdump.o hashfree.o hashlast.o hashlook.o hashscan.o hashsize.o hashview.o hashwalk.o memhash.o memsum.o strhash.o strkey.o strsum.o stracmp.o strnacmp.o ccmap.o ccmapid.o ccnative.o chresc.o chrtoi.o - exec - ${AR} rc libast.a streval.o strexpr.o strmatch.o strcopy.o modei.o modex.o strmode.o strlcat.o strlcpy.o strlook.o strncopy.o strsearch.o strpsearch.o stresc.o stropt.o strtape.o strpcmp.o strnpcmp.o strvcmp.o strnvcmp.o tok.o tokline.o tokscan.o pathaccess.o pathcat.o pathcanon.o pathcheck.o pathpath.o pathexists.o pathfind.o pathicase.o pathkey.o pathprobe.o pathrepl.o pathnative.o pathposix.o pathtemp.o pathtmp.o pathstat.o pathgetlink.o pathsetlink.o pathbin.o pathshell.o pathcd.o pathprog.o ftwalk.o ftwflags.o fts.o astintercept.o conformance.o getenv.o setenviron.o optget.o optjoin.o optesc.o optctx.o strsort.o struniq.o magic.o mime.o mimetype.o signal.o sigflag.o systrace.o error.o errorf.o errormsg.o errorx.o localeconv.o setlocale.o translate.o catopen.o iconv.o lc.o lctab.o mc.o base64.o recfmt.o recstr.o reclen.o fmtrec.o fmtbase.o fmtbuf.o fmtclock.o fmtdev.o fmtelapsed.o fmterror.o fmtesc.o fmtfmt.o fmtfs.o fmtident.o fmtint.o fmtip4.o fmtip6.o fmtls.o fmtmatch.o fmtmode.o fmtnum.o fmtperm.o fmtre.o fmttime.o + exec - ${AR} rc libast.a streval.o strexpr.o strmatch.o strcopy.o modei.o modex.o strmode.o strlcat.o strlcpy.o strlook.o strncopy.o strsearch.o strpsearch.o stresc.o stropt.o strtape.o strpcmp.o strnpcmp.o strvcmp.o strnvcmp.o tok.o tokline.o tokscan.o pathaccess.o pathcat.o pathcanon.o pathcheck.o pathpath.o pathexists.o pathfind.o pathicase.o pathkey.o pathprobe.o pathrepl.o pathnative.o pathposix.o pathtemp.o pathtmp.o pathstat.o pathgetlink.o pathsetlink.o pathbin.o pathshell.o pathcd.o pathprog.o ftwalk.o ftwflags.o fts.o astintercept.o conformance.o getenv.o setenviron.o optget.o optjoin.o optesc.o optctx.o strsort.o struniq.o magic.o mime.o mimetype.o signal.o sigflag.o systrace.o error.o errorf.o errormsg.o errorx.o localeconv.o setlocale.o translate.o catopen.o iconv.o lc.o lctab.o mc.o base64.o recfmt.o recstr.o reclen.o fmtrec.o fmtbuf.o fmtclock.o fmtdev.o fmtelapsed.o fmterror.o fmtesc.o fmtfmt.o fmtfs.o fmtident.o fmtint.o fmtip4.o fmtip6.o fmtls.o fmtmatch.o fmtmode.o fmtnum.o fmtperm.o fmtre.o fmttime.o exec - ${AR} rc libast.a fmtuid.o fmtgid.o fmtsignal.o fmtscale.o fmttmx.o fmttv.o fmtversion.o strelapsed.o strperm.o struid.o strgid.o strtoip4.o strtoip6.o stk.o swapget.o swapmem.o swapop.o swapput.o sigdata.o sigcrit.o sigunblock.o procopen.o procclose.o procrun.o procfree.o tmdate.o tmequiv.o tmfix.o tmfmt.o tmform.o tmgoff.o tminit.o tmleap.o tmlex.o tmlocale.o tmmake.o tmpoff.o tmscan.o tmsleep.o tmtime.o tmtype.o tmweek.o tmword.o tmzone.o tmxdate.o tmxduration.o tmxfmt.o tmxgettime.o tmxleap.o tmxmake.o tmxscan.o tmxsettime.o tmxsleep.o tmxtime.o tmxtouch.o tvcmp.o tvgettime.o tvsettime.o tvsleep.o tvtouch.o cmdarg.o vecargs.o vecfile.o vecfree.o vecload.o vecstring.o univdata.o touch.o mnt.o debug.o memccpy.o memchr.o memcmp.o memcpy.o memdup.o memmove.o memset.o mkdir.o mkfifo.o mknod.o rmdir.o remove.o rename.o link.o unlink.o strdup.o strtod.o strtold.o strtol.o strtoll.o strtoul.o strtoull.o strton.o strtonll.o strntod.o strntold.o strnton.o exec - ${AR} rc libast.a strntonll.o strntol.o strntoll.o strntoul.o strntoull.o strcasecmp.o strncasecmp.o strerror.o mktemp.o tmpnam.o fsync.o execlp.o execve.o execvp.o execvpe.o spawnveg.o killpg.o getlogin.o putenv.o setenv.o unsetenv.o lstat.o statvfs.o eaccess.o gross.o omitted.o readlink.o symlink.o getpgrp.o setpgid.o setsid.o fcntl.o open.o getdents.o getwd.o dup2.o errno.o getgroups.o mount.o system.o iblocks.o modedata.o tmdata.o memfatal.o sfkeyprintf.o sfdcdio.o sfdcdos.o sfdcfilter.o sfdcseekable.o sfdcslow.o sfdcsubstr.o sfdctee.o sfdcunion.o sfdcmore.o sfdcprefix.o wc.o wc2utf8.o dirname.o fmtmsglib.o fnmatch.o ftw.o getdate.o getsubopt.o glob.o nftw.o re_comp.o resolvepath.o realpath.o regcmp.o regexp.o strftime.o strptime.o swab.o tempnam.o wordexp.o mktime.o regalloc.o regclass.o regcoll.o regcomp.o regcache.o regdecomp.o regerror.o regexec.o regfatal.o reginit.o exec - ${AR} rc libast.a regnexec.o regsubcomp.o regsubexec.o regsub.o regrecord.o regrexec.o regstat.o dtclose.o dtdisc.o dthash.o dtlist.o dtmethod.o dtopen.o dtstat.o dtstrhash.o dttree.o dtuser.o dtview.o dtwalk.o dtnew.o dtcomp.o sfclose.o sfclrlock.o sfdisc.o sfdlen.o sfexcept.o sfgetl.o sfgetu.o sfcvt.o sfecvt.o sffcvt.o sfextern.o sffilbuf.o sfflsbuf.o sfprints.o sfgetd.o sfgetr.o sfllen.o sfmode.o sfmove.o sfnew.o sfpkrd.o sfnotify.o sfnputc.o sfopen.o sfpeek.o sfpoll.o sfpool.o sfpopen.o sfprintf.o sfputd.o sfputl.o sfputr.o sfputu.o sfrd.o sfread.o sfreserve.o sfscanf.o sfseek.o sfset.o sfsetbuf.o sfsetfd.o sfsize.o sfsk.o sfstack.o sfstrtod.o sfsync.o sfswap.o sftable.o sftell.o sftmp.o sfungetc.o sfvprintf.o sfvscanf.o sfwr.o sfwrite.o sfpurge.o sfraise.o sfwalk.o sfgetm.o sfputm.o sfresize.o _sfclrerr.o _sfeof.o _sferror.o _sffileno.o _sfopen.o _sfstacked.o _sfvalue.o _sfgetc.o _sfgetl.o _sfgetl2.o _sfgetu.o _sfgetu2.o _sfdlen.o _sfllen.o _sfslen.o _sfulen.o _sfputc.o _sfputd.o _sfputl.o _sfputm.o diff --git a/src/lib/libast/include/ast.h b/src/lib/libast/include/ast.h index 983f8a8ac7d2..b6ee243a530f 100644 --- a/src/lib/libast/include/ast.h +++ b/src/lib/libast/include/ast.h @@ -2,7 +2,7 @@ * * * This software is part of the ast package * * Copyright (c) 1985-2012 AT&T Intellectual Property * -* Copyright (c) 2020-2023 Contributors to ksh 93u+m * +* Copyright (c) 2020-2024 Contributors to ksh 93u+m * * and is licensed under the * * Eclipse Public License, Version 2.0 * * * @@ -323,7 +323,6 @@ extern int chrexp(const char*, char**, int*, int); extern int chrtoi(const char*); extern char* conformance(const char*, size_t); extern int eaccess(const char*, int); -extern char* fmtbase(intmax_t, int, int); extern char* fmtbuf(size_t); extern char* fmtclock(Sfulong_t); extern char* fmtelapsed(unsigned long, int); diff --git a/src/lib/libast/man/fmt.3 b/src/lib/libast/man/fmt.3 index 3e00b236e1bb..e4f0968ab6bb 100644 --- a/src/lib/libast/man/fmt.3 +++ b/src/lib/libast/man/fmt.3 @@ -44,7 +44,7 @@ fmt \- string formatting routines #include #include -char* fmtbase(long \fInumber\fP, int \fIbase\fP, int \fIprefix\fP); +char* fmtint(intmax_t \fInumber\fP, int \fIunsign\fP); char* fmtdev(struct stat* \fIst\fP); char* fmtelapsed(unsigned long \fIcount\fP, int \fIpersec\fP) char* fmterror(int \fIerrno\fP); @@ -73,19 +73,14 @@ routine that converts in the other direction. There is nothing spectacular about this collection other than that it provides a single place where the exact format is spelled out. .PP -.L fmtbase -formats a base -.I base -representation for +.L fmtint +is a performance-optimized function for formatting a base-10 integer .IR number . If -.I "prefix != 0" -then the base prefix is included in the formatted string. -If -.I "number == 0" -or -.I "base == 0" -then the output is signed base 10. +.I "unsign != 0" +then the +.IR number +is treated as unsigned. .PP .L fmtdev returns the device handle name specified by the diff --git a/src/lib/libast/string/fmtbase.c b/src/lib/libast/string/fmtbase.c deleted file mode 100644 index 36f0c1090b91..000000000000 --- a/src/lib/libast/string/fmtbase.c +++ /dev/null @@ -1,48 +0,0 @@ -/*********************************************************************** -* * -* This software is part of the ast package * -* Copyright (c) 1985-2011 AT&T Intellectual Property * -* Copyright (c) 2020-2022 Contributors to ksh 93u+m * -* and is licensed under the * -* Eclipse Public License, Version 2.0 * -* * -* A copy of the License is available at * -* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html * -* (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d) * -* * -* Glenn Fowler * -* David Korn * -* Phong Vo * -* Martijn Dekker * -* * -***********************************************************************/ -/* - * Glenn Fowler - * AT&T Bell Laboratories - * - * return base b representation for n - * if p!=0 then base prefix is included - * otherwise if n==0 or b==0 then output is signed base 10 - */ - -#include - -char* -fmtbase(intmax_t n, int b, int p) -{ - char* buf; - int z; - - if (!p) - { - if (!n) - return "0"; - if (!b) - return fmtint(n, 0); - if (b == 10) - return fmtint(n, 1); - } - buf = fmtbuf(z = 72); - sfsprintf(buf, z, p ? "%#..*I*u" : "%..*I*u", b, sizeof(n), n); - return buf; -}