diff options
author | Zefram <zefram@fysh.org> | 2017-11-11 07:40:20 +0000 |
---|---|---|
committer | Zefram <zefram@fysh.org> | 2017-11-11 07:43:45 +0000 |
commit | 2e8ea15a11326145a7df027b5b2507ff3d7483ba (patch) | |
tree | 3ca50d19499e90392e60c21592332864cbba17db /pp_sys.c | |
parent | d8f3f638c281aaee77472982fef7e6333a98aea8 (diff) | |
download | perl-2e8ea15a11326145a7df027b5b2507ff3d7483ba.tar.gz |
return inode numbers as strings where necessary
We previously used a lossy conversion of inode numbers to floating point,
where they're too big to fit the IV/UV format. That sucks; a rounded
inode number is nearly useless. Instead, fall back to returning a
string of decimal digits. That preserves the entire value, for code
that looks at it in the right way, and collapses to the former fallback
in other situations.
Diffstat (limited to 'pp_sys.c')
-rw-r--r-- | pp_sys.c | 62 |
1 files changed, 55 insertions, 7 deletions
@@ -2995,13 +2995,61 @@ PP(pp_stat) EXTEND(SP, max); EXTEND_MORTAL(max); mPUSHi(PL_statcache.st_dev); -#if ST_INO_SIZE > IVSIZE - mPUSHn(PL_statcache.st_ino); -#elif ST_INO_SIGN <= 0 - mPUSHi(PL_statcache.st_ino); -#else - mPUSHu(PL_statcache.st_ino); -#endif + { + /* + * We try to represent st_ino as a native IV or UV where + * possible, but fall back to a decimal string where + * necessary. The code to generate these decimal strings + * is quite obtuse, because (a) we're portable to non-POSIX + * platforms where st_ino might be signed; (b) we didn't + * necessarily detect at Configure time whether st_ino is + * signed; (c) we're portable to non-POSIX platforms where + * ino_t isn't defined, so have no name for the type of + * st_ino; and (d) sprintf() doesn't necessarily support + * integers as large as st_ino. + */ + bool neg; + Stat_t s; + GCC_DIAG_IGNORE(-Wtype-limits); + neg = PL_statcache.st_ino < 0; + GCC_DIAG_RESTORE; + if (neg) { + s.st_ino = (IV)PL_statcache.st_ino; + if (LIKELY(s.st_ino == PL_statcache.st_ino)) { + mPUSHi(s.st_ino); + } else { + char buf[sizeof(s.st_ino)*3+1], *p; + s.st_ino = PL_statcache.st_ino; + for (p = buf + sizeof(buf); p != buf+1; ) { + Stat_t t; + t.st_ino = s.st_ino / 10; + *--p = '0' + (int)(t.st_ino*10 - s.st_ino); + s.st_ino = t.st_ino; + } + while (*p == '0') + p++; + *--p = '-'; + mPUSHp(p, buf+sizeof(buf) - p); + } + } else { + s.st_ino = (UV)PL_statcache.st_ino; + if (LIKELY(s.st_ino == PL_statcache.st_ino)) { + mPUSHu(s.st_ino); + } else { + char buf[sizeof(s.st_ino)*3], *p; + s.st_ino = PL_statcache.st_ino; + for (p = buf + sizeof(buf); p != buf; ) { + Stat_t t; + t.st_ino = s.st_ino / 10; + *--p = '0' + (int)(s.st_ino - t.st_ino*10); + s.st_ino = t.st_ino; + } + while (*p == '0') + p++; + mPUSHp(p, buf+sizeof(buf) - p); + } + } + } mPUSHu(PL_statcache.st_mode); mPUSHu(PL_statcache.st_nlink); |