(root)/
findutils-4.9.0/
lib/
listfile.c
       1  /* listfile.c -- display a long listing of a file
       2     Copyright (C) 1991-2022 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.
      16  */
      17  /* config.h must be included first. */
      18  #include <config.h>
      19  
      20  /* system headers. */
      21  #include <alloca.h>
      22  #include <errno.h>
      23  #include <fcntl.h>
      24  #include <grp.h>
      25  #include <pwd.h>
      26  #include <stdio.h>
      27  #include <stdlib.h>
      28  #include <string.h>
      29  #include <sys/stat.h>
      30  #include <sys/types.h>
      31  #include <time.h>
      32  #include <unistd.h> /* for readlink() */
      33  
      34  /* gnulib headers. */
      35  #include "areadlink.h"
      36  #include "error.h"
      37  #include "filemode.h"
      38  #include "human.h"
      39  #include "mbswidth.h"
      40  #include "idcache.h"
      41  #include "pathmax.h"
      42  #include "stat-size.h"
      43  
      44  /* find headers. */
      45  #include "system.h"
      46  #include "die.h"
      47  #include "listfile.h"
      48  
      49  /* Since major is a function on SVR4, we can't use `ifndef major'.  */
      50  #ifdef MAJOR_IN_MKDEV
      51  # include <sys/mkdev.h>
      52  #else
      53  # ifdef MAJOR_IN_SYSMACROS
      54  #  include <sys/sysmacros.h>
      55  # else
      56  #  ifndef major                    /* Might be defined in sys/types.h.  */
      57  #   define major(dev)  (((dev) >> 8) & 0xff)
      58  #   define minor(dev)  ((dev) & 0xff)
      59  #  endif
      60  # endif
      61  #endif
      62  
      63  
      64  static bool print_name (register const char *p, FILE *stream, int literal_control_chars);
      65  
      66  /* We have some minimum field sizes, though we try to widen these fields on systems
      67   * where we discover examples where the field width we started with is not enough. */
      68  static int inode_number_width = 9;
      69  static int block_size_width = 6;
      70  static int nlink_width = 3;
      71  static int owner_width = 8;
      72  static int group_width = 8;
      73  /* We don't print st_author even if the system has it. */
      74  static int major_device_number_width = 3;
      75  static int minor_device_number_width = 3;
      76  static int file_size_width = 8;
      77  
      78  static bool print_num(FILE *stream, unsigned long num, int *width)
      79  {
      80    const int chars_out = fprintf (stream, "%*lu", *width, num);
      81    if (chars_out >= 0)
      82      {
      83        if (*width < chars_out)
      84          *width = chars_out;
      85        return true;
      86      }
      87    return false;
      88  }
      89  
      90  
      91  /* NAME is the name to print.
      92     RELNAME is the path to access it from the current directory.
      93     STATP is the results of stat or lstat on it.
      94     Use CURRENT_TIME to decide whether to print yyyy or hh:mm.
      95     Use OUTPUT_BLOCK_SIZE to determine how to print file block counts
      96     and sizes.
      97     STREAM is the stdio stream to print on.  */
      98  
      99  void
     100  list_file (const char *name,
     101             int dir_fd,
     102             const char *relname,
     103             const struct stat *statp,
     104             time_t current_time,
     105             int output_block_size,
     106             int literal_control_chars,
     107             FILE *stream)
     108  {
     109    char modebuf[12];
     110    struct tm const *when_local;
     111    char const *user_name;
     112    char const *group_name;
     113    char hbuf[LONGEST_HUMAN_READABLE + 1];
     114    bool output_good = true;
     115    int chars_out;
     116    int failed_at = 000;
     117  
     118  #if HAVE_ST_DM_MODE
     119    /* Cray DMF: look at the file's migrated, not real, status */
     120    strmode (statp->st_dm_mode, modebuf);
     121  #else
     122    strmode (statp->st_mode, modebuf);
     123  #endif
     124  
     125    chars_out = fprintf (stream, "%*s", inode_number_width,
     126                         human_readable ((uintmax_t) statp->st_ino, hbuf,
     127                                         human_ceiling,
     128                                         1u, 1u));
     129    if (chars_out < 0)
     130      {
     131        output_good = false;
     132        failed_at = 100;
     133      }
     134    else if (chars_out > inode_number_width)
     135      {
     136        inode_number_width = chars_out;
     137      }
     138    if (output_good)
     139      {
     140        if (EOF == putc(' ', stream))
     141          {
     142            output_good = false;
     143            failed_at = 150;
     144          }
     145        chars_out = fprintf (stream, "%*s",
     146                             block_size_width,
     147                             human_readable ((uintmax_t) ST_NBLOCKS (*statp), hbuf,
     148                                             human_ceiling,
     149                                             ST_NBLOCKSIZE, output_block_size));
     150        if (chars_out < 0)
     151          {
     152            output_good = false;
     153            failed_at = 200;
     154          }
     155        else
     156          {
     157            if (chars_out > block_size_width)
     158              block_size_width = chars_out;
     159          }
     160      }
     161  
     162    if (output_good)
     163      {
     164        if (EOF == putc(' ', stream))
     165          {
     166            output_good = false;
     167            failed_at = 250;
     168          }
     169      }
     170    if (output_good)
     171      {
     172        /* modebuf includes the space between the mode and the number of links,
     173           as the POSIX "optional alternate access method flag".  */
     174        if (fputs (modebuf, stream) < 0)
     175          {
     176            output_good = false;
     177            failed_at = 275;
     178          }
     179      }
     180    if (output_good)
     181      {
     182        /* This format used to end in a space, but the output of "ls"
     183           has only one space between the link count and the owner name,
     184           so we removed the trailing space.  Happily this also makes it
     185           easier to update nlink_width. */
     186        chars_out =  fprintf (stream, "%*lu",
     187  			    nlink_width, (unsigned long) statp->st_nlink);
     188        if (chars_out < 0)
     189          {
     190            output_good = false;
     191            failed_at = 300;
     192          }
     193        else
     194  	{
     195  	  if (chars_out > nlink_width)
     196  	    nlink_width = chars_out;
     197  	}
     198      }
     199  
     200    if (output_good)
     201      {
     202        if (EOF == putc(' ', stream))
     203          {
     204            output_good = false;
     205            failed_at = 250;
     206          }
     207        user_name = getuser (statp->st_uid);
     208        if (user_name)
     209          {
     210            int len = mbswidth (user_name, 0);
     211            if (len > owner_width)
     212              owner_width = len;
     213            output_good = (fprintf (stream, "%-*s ", owner_width, user_name) >= 0);
     214            if (!output_good)
     215              failed_at = 400;
     216          }
     217        else
     218          {
     219            chars_out = fprintf (stream, "%-8lu ", (unsigned long) statp->st_uid);
     220            if (chars_out > owner_width)
     221              owner_width = chars_out;
     222            output_good = (chars_out > 0);
     223            if (!output_good)
     224              failed_at = 450;
     225          }
     226      }
     227  
     228    if (output_good)
     229      {
     230        group_name = getgroup (statp->st_gid);
     231        if (group_name)
     232          {
     233            int len = mbswidth (group_name, 0);
     234            if (len > group_width)
     235              group_width = len;
     236            output_good = (fprintf (stream, "%-*s ", group_width, group_name) >= 0);
     237            if (!output_good)
     238              failed_at = 500;
     239          }
     240        else
     241          {
     242            chars_out = fprintf (stream, "%-*lu",
     243                                 group_width, (unsigned long) statp->st_gid);
     244            if (chars_out > group_width)
     245              group_width = chars_out;
     246            output_good = (chars_out >= 0);
     247            if (output_good)
     248              {
     249                if (EOF == putc(' ', stream))
     250                  {
     251                    output_good = false;
     252                    failed_at = 525;
     253                  }
     254              }
     255            else
     256              {
     257                if (!output_good)
     258                  failed_at = 550;
     259              }
     260          }
     261      }
     262  
     263    if (output_good)
     264      {
     265        if (S_ISCHR (statp->st_mode) || S_ISBLK (statp->st_mode))
     266          {
     267  #ifdef HAVE_STRUCT_STAT_ST_RDEV
     268            if (!print_num (stream,
     269                            (unsigned long) major (statp->st_rdev),
     270                            &major_device_number_width))
     271              {
     272                output_good = false;
     273                failed_at = 600;
     274              }
     275            if (output_good)
     276              {
     277                if (fprintf (stream, ", ") < 0)
     278                  {
     279                    output_good = false;
     280                    failed_at = 625;
     281                  }
     282              }
     283            if (output_good)
     284              {
     285                if (!print_num (stream,
     286                                (unsigned long) minor (statp->st_rdev),
     287                                &minor_device_number_width))
     288                  {
     289                    output_good = false;
     290                    failed_at = 650;
     291                  }
     292              }
     293  #else
     294            if (fprintf (stream, "%*s  %*s",
     295                         major_device_number_width,
     296                         minor_device_number_width) < 0)
     297              {
     298                output_good = false;
     299                failed_at = 700;
     300              }
     301  #endif
     302          }
     303        else
     304          {
     305            const int blocksize = output_block_size < 0 ? output_block_size : 1;
     306            chars_out = fprintf (stream, "%*s",
     307                                 file_size_width,
     308                                 human_readable ((uintmax_t) statp->st_size, hbuf,
     309                                                 human_ceiling,
     310                                                 1, blocksize));
     311            if (chars_out < 0)
     312              {
     313                output_good = false;
     314                failed_at = 800;
     315              }
     316            else
     317              {
     318                if (chars_out > file_size_width)
     319                  {
     320                    file_size_width = chars_out;
     321                  }
     322              }
     323          }
     324      }
     325  
     326    if (output_good)
     327      {
     328        if (EOF == putc(' ', stream))
     329          {
     330            output_good = false;
     331            failed_at = 850;
     332          }
     333      }
     334  
     335    if (output_good)
     336      {
     337        if ((when_local = localtime (&statp->st_mtime)))
     338          {
     339            char init_bigbuf[256];
     340            char *buf = init_bigbuf;
     341            size_t bufsize = sizeof init_bigbuf;
     342  
     343            /* Use strftime rather than ctime, because the former can produce
     344               locale-dependent names for the month (%b).
     345  
     346               Output the year if the file is fairly old or in the future.
     347               POSIX says the cutoff is 6 months old;
     348               approximate this by 6*30 days.
     349               Allow a 1 hour slop factor for what is considered "the future",
     350               to allow for NFS server/client clock disagreement.  */
     351            char const *fmt =
     352              ((current_time - 6 * 30 * 24 * 60 * 60 <= statp->st_mtime
     353                && statp->st_mtime <= current_time + 60 * 60)
     354               ? "%b %e %H:%M"
     355               : "%b %e  %Y");
     356  
     357            while (!strftime (buf, bufsize, fmt, when_local))
     358              buf = alloca (bufsize *= 2);
     359  
     360            if (fprintf (stream, "%s ", buf) < 0)
     361              {
     362                output_good = false;
     363                failed_at = 900;
     364              }
     365          }
     366        else
     367          {
     368            /* The time cannot be represented as a local time;
     369               print it as a huge integer number of seconds.  */
     370            int width = 12;
     371  
     372            if (statp->st_mtime < 0)
     373              {
     374                char const *num = human_readable (- (uintmax_t) statp->st_mtime,
     375                                                  hbuf, human_ceiling, 1, 1);
     376                int sign_width = width - strlen (num);
     377                if (fprintf (stream, "%*s%s ",
     378                             sign_width < 0 ? 0 : sign_width, "-", num) < 0)
     379                  {
     380                    output_good = false;
     381                    failed_at = 1000;
     382                  }
     383              }
     384            else
     385              {
     386                if (fprintf (stream, "%*s ", width,
     387                             human_readable ((uintmax_t) statp->st_mtime, hbuf,
     388                                             human_ceiling,
     389                                             1, 1)) < 0)
     390                  {
     391                    output_good = false;
     392                    failed_at = 1100;
     393                  }
     394              }
     395          }
     396      }
     397  
     398    if (output_good)
     399      {
     400        output_good = print_name (name, stream, literal_control_chars);
     401        if (!output_good)
     402          {
     403            failed_at = 1200;
     404          }
     405      }
     406  
     407    if (output_good)
     408      {
     409        if (S_ISLNK (statp->st_mode))
     410          {
     411            char *linkname = areadlinkat (dir_fd, relname);
     412            if (linkname)
     413              {
     414                if (fputs (" -> ", stream) < 0)
     415                  {
     416                    output_good = false;
     417                    failed_at = 1300;
     418                  }
     419                if (output_good)
     420                  {
     421                    output_good = print_name (linkname, stream, literal_control_chars);
     422                    if (!output_good)
     423                      {
     424                        failed_at = 1350;
     425                      }
     426                  }
     427              }
     428            else
     429              {
     430                /* POSIX requires in the case of find that if we issue a
     431                 * diagnostic we should have a nonzero status.  However,
     432                 * this function doesn't have a way of telling the caller to
     433                 * do that.  However, since this function is only used when
     434                 * processing "-ls", we're already using an extension.
     435                 */
     436                error (0, errno, "%s", name);
     437              }
     438            free (linkname);
     439          }
     440        if (output_good)
     441          {
     442            if (EOF == putc ('\n', stream))
     443              {
     444                output_good = false;
     445                if (!output_good)
     446                  {
     447                    failed_at = 1400;
     448                  }
     449              }
     450          }
     451      }
     452    if (!output_good)
     453      {
     454        die (EXIT_FAILURE, errno, _("Failed to write output (at stage %d)"), failed_at);
     455      }
     456  }
     457  
     458  
     459  static bool
     460  print_name_without_quoting (const char *p, FILE *stream)
     461  {
     462    return (fprintf (stream, "%s", p) >= 0);
     463  }
     464  
     465  
     466  static bool
     467  print_name_with_quoting (register const char *p, FILE *stream)
     468  {
     469    register unsigned char c;
     470  
     471    while ((c = *p++) != '\0')
     472      {
     473        int fprintf_result = -1;
     474        switch (c)
     475          {
     476          case '\\':
     477            fprintf_result = fprintf (stream, "\\\\");
     478            break;
     479  
     480          case '\n':
     481            fprintf_result = fprintf (stream, "\\n");
     482            break;
     483  
     484          case '\b':
     485            fprintf_result = fprintf (stream, "\\b");
     486            break;
     487  
     488          case '\r':
     489            fprintf_result = fprintf (stream, "\\r");
     490            break;
     491  
     492          case '\t':
     493            fprintf_result = fprintf (stream, "\\t");
     494            break;
     495  
     496          case '\f':
     497            fprintf_result = fprintf (stream, "\\f");
     498            break;
     499  
     500          case ' ':
     501            fprintf_result = fprintf (stream, "\\ ");
     502            break;
     503  
     504          case '"':
     505            fprintf_result = fprintf (stream, "\\\"");
     506            break;
     507  
     508          default:
     509            if (c > 040 && c < 0177)
     510              {
     511                if (EOF == putc (c, stream))
     512                  return false;
     513                fprintf_result = 1; /* otherwise it's used uninitialized. */
     514              }
     515            else
     516              {
     517                fprintf_result = fprintf (stream, "\\%03o", (unsigned int) c);
     518              }
     519          }
     520        if (fprintf_result < 0)
     521          return false;
     522      }
     523    return true;
     524  }
     525  
     526  static bool print_name (register const char *p, FILE *stream, int literal_control_chars)
     527  {
     528    if (literal_control_chars)
     529      return print_name_without_quoting (p, stream);
     530    else
     531      return print_name_with_quoting (p, stream);
     532  }