summaryrefslogtreecommitdiff
path: root/pp_sys.c
diff options
context:
space:
mode:
authorZefram <zefram@fysh.org>2017-11-11 07:40:20 +0000
committerZefram <zefram@fysh.org>2017-11-11 07:43:45 +0000
commit2e8ea15a11326145a7df027b5b2507ff3d7483ba (patch)
tree3ca50d19499e90392e60c21592332864cbba17db /pp_sys.c
parentd8f3f638c281aaee77472982fef7e6333a98aea8 (diff)
downloadperl-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.c62
1 files changed, 55 insertions, 7 deletions
diff --git a/pp_sys.c b/pp_sys.c
index cd4deb803c..f7e930f63b 100644
--- a/pp_sys.c
+++ b/pp_sys.c
@@ -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);