This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
return inode numbers as strings where necessary
authorZefram <zefram@fysh.org>
Sat, 11 Nov 2017 07:40:20 +0000 (07:40 +0000)
committerZefram <zefram@fysh.org>
Sat, 11 Nov 2017 07:43:45 +0000 (07:43 +0000)
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.

pod/perldelta.pod
pod/perlfunc.pod
pp_sys.c

index e65aefa..1ba60a6 100644 (file)
@@ -33,6 +33,18 @@ A persistent lexical array or hash variable can now be initialized,
 by an expression such as C<state @a = qw(x y z)>.  Initialization of a
 list of persistent lexical variables is still not possible.
 
+=head2 Full-size inode numbers
+
+On platforms where inode numbers are of a type larger than perl's native
+integer numerical types, L<stat|perlfunc/stat> will preserve the full
+content of large inode numbers by returning them in the form of strings of
+decimal digits.  Exact comparison of inode numbers can thus be achieved by
+comparing with C<eq> rather than C<==>.  Comparison with C<==>, and other
+numerical operations (which are usually meaningless on inode numbers),
+work as well as they did before, which is to say they fall back to
+floating point, and ultimately operate on a fairly useless rounded inode
+number if the real inode number is too big for the floating point format.
+
 =head1 Security
 
 XXX Any security-related notices go here.  In particular, any security
index b571faf..5cced5a 100644 (file)
@@ -8231,6 +8231,15 @@ L<C<lstat>|/lstat FILEHANDLE>, or filetest are returned.  Example:
 (This works on machines only for which the device number is negative
 under NFS.)
 
+On some platforms inode numbers are of a type larger than perl knows how
+to handle as integer numerical values.  If necessary, an inode number will
+be returned as a decimal string in order to preserve the entire value.
+If used in a numeric context, this will be converted to a floating-point
+numerical value, with rounding, a fate that is best avoided.  Therefore,
+you should prefer to compare inode numbers using C<eq> rather than C<==>.
+C<eq> will work fine on inode numbers that are represented numerically,
+as well as those represented as strings.
+
 Because the mode contains both the file type and its permissions, you
 should mask off the file type portion and (s)printf using a C<"%o">
 if you want to see the real permissions.
index cd4deb8..f7e930f 100644 (file)
--- 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);