(root)/
findutils-4.9.0/
find/
print.c
       1  /* print.c -- print/printf-related code.
       2     Copyright (C) 1990-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  
      18  /* We always include config.h first. */
      19  #include <config.h>
      20  
      21  /* system headers go here. */
      22  #include <assert.h>
      23  #include <ctype.h>
      24  #include <errno.h>
      25  #include <grp.h>
      26  #include <math.h>
      27  #include <pwd.h>
      28  #include <stdarg.h>
      29  #include <sys/stat.h>
      30  #include <sys/types.h>
      31  
      32  /* gnulib headers. */
      33  #include "areadlink.h"
      34  #include "dirname.h"
      35  #include "error.h"
      36  #include "filemode.h"
      37  #include "human.h"
      38  #include "printquoted.h"
      39  #include "stat-size.h"
      40  #include "stat-time.h"
      41  #include "verify.h"
      42  #include "xalloc.h"
      43  
      44  /* find-specific headers. */
      45  #include "system.h"
      46  #include "defs.h"
      47  #include "die.h"
      48  #include "print.h"
      49  
      50  
      51  #if defined STDC_HEADERS
      52  # define ISDIGIT(c) isdigit ((unsigned char)c)
      53  #else
      54  # define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
      55  #endif
      56  #undef MAX
      57  #define MAX(a, b) ((a) > (b) ? (a) : (b))
      58  
      59  static void checked_fprintf (struct format_val *dest, const char *fmt, ...)
      60    _GL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 3);
      61  
      62  
      63  /* Create a new fprintf segment in *SEGMENT, with type KIND,
      64     from the text in FORMAT, which has length LEN.
      65     Return the address of the `next' pointer of the new segment. */
      66  struct segment **
      67  make_segment (struct segment **segment,
      68                char *format,
      69                int len,
      70                int kind,
      71                char format_char,
      72                char aux_format_char,
      73                struct predicate *pred)
      74  {
      75    enum EvaluationCost mycost = NeedsNothing;
      76    char *fmt;
      77  
      78    assert (format_char != '{');
      79    assert (format_char != '[');
      80    assert (format_char != '(');
      81  
      82    *segment = xmalloc (sizeof (struct segment));
      83  
      84    (*segment)->segkind = kind;
      85    (*segment)->format_char[0] = format_char;
      86    (*segment)->format_char[1] = aux_format_char;
      87    (*segment)->next = NULL;
      88    (*segment)->text_len = len;
      89  
      90    fmt = (*segment)->text = xmalloc (len + sizeof "d");
      91    strncpy (fmt, format, len);
      92    fmt += len;
      93  
      94    if (kind == KIND_PLAIN     /* Plain text string, no % conversion. */
      95        || kind == KIND_STOP)  /* Terminate argument, no newline. */
      96      {
      97        assert (0 == format_char);
      98        assert (0 == aux_format_char);
      99        *fmt = '\0';
     100        if (mycost > pred->p_cost)
     101          pred->p_cost = NeedsNothing;
     102        return &(*segment)->next;
     103      }
     104  
     105    assert (kind == KIND_FORMAT);
     106    switch (format_char)
     107      {
     108      case '%':                   /* literal % */
     109        *fmt++ = '%';
     110        break;
     111  
     112      case 'l':                   /* object of symlink */
     113        pred->need_stat = true;
     114        mycost = NeedsLinkName;
     115        *fmt++ = 's';
     116        break;
     117  
     118      case 'y':                   /* file type */
     119        pred->need_type = true;
     120        mycost = NeedsType;
     121        *fmt++ = 's';
     122        break;
     123  
     124      case 'i':                   /* inode number */
     125        pred->need_inum = true;
     126        mycost = NeedsInodeNumber;
     127        *fmt++ = 's';
     128        break;
     129  
     130      case 'a':                   /* atime in `ctime' format */
     131      case 'A':                   /* atime in user-specified strftime format */
     132      case 'B':                   /* birth time in user-specified strftime format */
     133      case 'c':                   /* ctime in `ctime' format */
     134      case 'C':                   /* ctime in user-specified strftime format */
     135      case 'F':                   /* file system type */
     136      case 'g':                   /* group name */
     137      case 'M':                   /* mode in `ls -l' format (eg., "drwxr-xr-x") */
     138      case 's':                   /* size in bytes */
     139      case 't':                   /* mtime in `ctime' format */
     140      case 'T':                   /* mtime in user-specified strftime format */
     141      case 'u':                   /* user name */
     142        pred->need_stat = true;
     143        mycost = NeedsStatInfo;
     144        *fmt++ = 's';
     145        break;
     146  
     147      case 'S':                   /* sparseness */
     148        pred->need_stat = true;
     149        mycost = NeedsStatInfo;
     150        *fmt++ = 'g';
     151        break;
     152  
     153      case 'Y':                   /* symlink pointed file type */
     154        pred->need_stat = true;
     155        mycost = NeedsType;       /* true for amortised effect */
     156        *fmt++ = 's';
     157        break;
     158  
     159      case 'f':                   /* basename of path */
     160      case 'h':                   /* leading directories part of path */
     161      case 'p':                   /* pathname */
     162      case 'P':                   /* pathname with ARGV element stripped */
     163        *fmt++ = 's';
     164        break;
     165  
     166      case 'Z':                   /* SELinux security context */
     167        mycost = NeedsAccessInfo;
     168        *fmt++ = 's';
     169        break;
     170  
     171      case 'H':                   /* ARGV element file was found under */
     172        *fmt++ = 's';
     173        break;
     174  
     175        /* Numeric items that one might expect to honour
     176         * #, 0, + flags but which do not.
     177         */
     178      case 'G':                   /* GID number */
     179      case 'U':                   /* UID number */
     180      case 'b':                   /* size in 512-byte blocks (NOT birthtime in ctime fmt)*/
     181      case 'D':                   /* Filesystem device on which the file exits */
     182      case 'k':                   /* size in 1K blocks */
     183      case 'n':                   /* number of links */
     184        pred->need_stat = true;
     185        mycost = NeedsStatInfo;
     186        *fmt++ = 's';
     187        break;
     188  
     189        /* Numeric items that DO honour #, 0, + flags.
     190         */
     191      case 'd':                   /* depth in search tree (0 = ARGV element) */
     192        *fmt++ = 'd';
     193        break;
     194  
     195      case 'm':                   /* mode as octal number (perms only) */
     196        *fmt++ = 'o';
     197        pred->need_stat = true;
     198        mycost = NeedsStatInfo;
     199        break;
     200      }
     201    *fmt = '\0';
     202  
     203    if (mycost > pred->p_cost)
     204      pred->p_cost = mycost;
     205    return &(*segment)->next;
     206  }
     207  
     208  static bool
     209  is_octal_char (char ch)
     210  {
     211    return ch >= '0' && ch <= '7';
     212  }
     213  
     214  static char
     215  parse_octal_escape(const char *p, size_t *consumed)
     216  {
     217    register int n, i;
     218    size_t pos = 0;
     219  
     220    for (i = n = 0; i < 3 && is_octal_char(p[pos]); i++, pos++)
     221      {
     222        n = 8 * n + p[pos] - '0';
     223      }
     224    --pos;
     225    *consumed = pos;
     226    return n;
     227  }
     228  
     229  static int
     230  parse_escape_char(const char ch)
     231  {
     232    char value = 0;
     233    switch (ch)
     234      {
     235      case 'a':
     236        value = '\a';
     237        break;
     238      case 'b':
     239        value = '\b';
     240        break;
     241      case 'f':
     242        value = '\f';
     243        break;
     244      case 'n':
     245        value = '\n';
     246        break;
     247      case 'r':
     248        value = '\r';
     249        break;
     250      case 't':
     251        value = '\t';
     252        break;
     253      case 'v':
     254        value = '\v';
     255        break;
     256      case '\\':
     257        value = '\\';
     258        break;
     259      }
     260    return value;
     261  }
     262  
     263  
     264  static size_t
     265  get_format_flags_length(const char *p)
     266  {
     267    size_t n = 0;
     268    /* Scan past flags, width and precision, to verify kind. */
     269    for (; p[++n] && strchr ("-+ #", p[n]);)
     270      {
     271        /* Do nothing. */
     272      }
     273    while (ISDIGIT (p[n]))
     274      n++;
     275    if (p[n] == '.')
     276      for (n++; ISDIGIT (p[n]); n++)
     277        /* Do nothing. */ ;
     278    return n;
     279  }
     280  
     281  static size_t
     282  get_format_specifer_length(char ch)
     283  {
     284    if (strchr ("abcdDfFgGhHiklmMnpPsStuUyYZ%", ch))
     285      {
     286        return 1;
     287      }
     288    else if (strchr ("ABCT", ch))
     289      {
     290        return 2;
     291      }
     292    else
     293      {
     294        return 0;
     295      }
     296  }
     297  
     298  
     299  bool
     300  insert_fprintf (struct format_val *vec,
     301                  const struct parser_table *entry,
     302                  char *format)
     303  {
     304    char *segstart = format;
     305    char *fmt_editpos;       /* Current address in scanning `format'. */
     306    struct segment **segmentp;      /* Address of current segment. */
     307    struct predicate *our_pred;
     308  
     309    our_pred = insert_primary_withpred (entry, pred_fprintf, format);
     310    our_pred->side_effects = our_pred->no_default_print = true;
     311    our_pred->args.printf_vec = *vec;
     312    our_pred->need_type = false;
     313    our_pred->need_stat = false;
     314    our_pred->p_cost    = NeedsNothing;
     315  
     316    segmentp = &our_pred->args.printf_vec.segment;
     317    *segmentp = NULL;
     318  
     319    for (fmt_editpos = segstart; *fmt_editpos; fmt_editpos++)
     320      {
     321        if (fmt_editpos[0] == '\\' && fmt_editpos[1] == 'c')
     322          {
     323            make_segment (segmentp, segstart, fmt_editpos - segstart,
     324                          KIND_STOP, 0, 0,
     325                          our_pred);
     326            if (our_pred->need_stat && (our_pred->p_cost < NeedsStatInfo))
     327              our_pred->p_cost = NeedsStatInfo;
     328            return true;
     329          }
     330        else if (*fmt_editpos == '\\')
     331          {
     332            size_t readpos = 1;
     333            if (!fmt_editpos[readpos])
     334              {
     335                error (0, 0, _("warning: escape `\\' followed by nothing at all"));
     336                --readpos;
     337                /* (*fmt_editpos) is already '\\' and that's a reasonable result. */
     338              }
     339            else if (is_octal_char(fmt_editpos[readpos]))
     340              {
     341                size_t consumed = 0;
     342                *fmt_editpos = parse_octal_escape(fmt_editpos + readpos, &consumed);
     343                readpos += consumed;
     344              }
     345            else
     346              {
     347                const char val = parse_escape_char(fmt_editpos[readpos]);
     348                if (val)
     349                  {
     350                    fmt_editpos[0] = val;
     351                  }
     352                else
     353                  {
     354                    error (0, 0, _("warning: unrecognized escape `\\%c'"),
     355                           fmt_editpos[readpos]);
     356                    fmt_editpos += readpos;
     357                    continue;
     358                  }
     359              }
     360            segmentp = make_segment (segmentp,
     361                                     segstart, fmt_editpos - segstart + 1,
     362                                     KIND_PLAIN, 0, 0,
     363                                     our_pred);
     364            segstart = fmt_editpos + readpos + 1; /* Move past the escape. */
     365            fmt_editpos += readpos;  /* Incremented immediately by `for'. */
     366          }
     367        else if (fmt_editpos[0] == '%')
     368          {
     369            size_t len;
     370            if (fmt_editpos[1] == 0)
     371              {
     372                /* Trailing %.  We don't like those. */
     373                die (EXIT_FAILURE, 0,
     374                     _("error: %s at end of format string"), fmt_editpos);
     375              }
     376  
     377            if (fmt_editpos[1] == '%') /* %% produces just %. */
     378              len = 1;
     379            else
     380              len = get_format_flags_length(fmt_editpos);
     381            fmt_editpos += len;
     382  
     383            len = get_format_specifer_length (fmt_editpos[0]);
     384            if (len && (fmt_editpos[len-1]))
     385              {
     386                const char fmt2 = (len == 2) ? fmt_editpos[1] : 0;
     387                segmentp = make_segment (segmentp, segstart,
     388                                         fmt_editpos - segstart,
     389                                         KIND_FORMAT, fmt_editpos[0], fmt2,
     390                                         our_pred);
     391                fmt_editpos += (len - 1);
     392              }
     393            else
     394              {
     395                if (strchr ("{[(", fmt_editpos[0]))
     396                  {
     397                    die (EXIT_FAILURE, 0,
     398                         _("error: the format directive `%%%c' is reserved for future use"),
     399                         (int)fmt_editpos[0]);
     400                    /*NOTREACHED*/
     401                  }
     402  
     403                if (len == 2 && !fmt_editpos[1])
     404                  {
     405                    error (0, 0,
     406                           _("warning: format directive `%%%c' "
     407                             "should be followed by another character"),
     408                           fmt_editpos[0]);
     409                  }
     410                else
     411                  {
     412                    /* An unrecognized % escape.  Print the char after the %. */
     413                    error (0, 0,
     414                           _("warning: unrecognized format directive `%%%c'"),
     415                           fmt_editpos[0]);
     416                  }
     417                segmentp = make_segment (segmentp,
     418                                         segstart, fmt_editpos + 1 - segstart,
     419                                         KIND_PLAIN, 0, 0,
     420                                         our_pred);
     421              }
     422            segstart = fmt_editpos + 1;
     423          }
     424      }
     425  
     426    if (fmt_editpos > segstart)
     427      make_segment (segmentp, segstart, fmt_editpos - segstart, KIND_PLAIN, 0, 0,
     428                    our_pred);
     429    return true;
     430  }
     431  
     432  static bool
     433  scan_for_digit_differences (const char *p, const char *q,
     434                              size_t *first, size_t *n)
     435  {
     436    bool seen = false;
     437    size_t i;
     438  
     439    for (i=0; p[i] && q[i]; i++)
     440      {
     441        if (p[i] != q[i])
     442          {
     443            if (!isdigit ((unsigned char)p[i]) || !isdigit ((unsigned char)q[i]))
     444              return false;
     445  
     446            if (!seen)
     447              {
     448                *first = i;
     449                *n = 1;
     450                seen = 1;
     451              }
     452            else
     453              {
     454                if (i-*first == *n)
     455                  {
     456                    /* Still in the first sequence of differing digits. */
     457                    ++*n;
     458                  }
     459                else
     460                  {
     461                    /* More than one differing contiguous character sequence. */
     462                    return false;
     463                  }
     464              }
     465          }
     466      }
     467    if (p[i] || q[i])
     468      {
     469        /* strings are different lengths. */
     470        return false;
     471      }
     472    return true;
     473  }
     474  
     475  static char*
     476  do_time_format (const char *fmt, const struct tm *p, const char *ns, size_t ns_size)
     477  {
     478    static char *buf = NULL;
     479    static size_t buf_size;
     480    char *timefmt = NULL;
     481    struct tm altered_time;
     482  
     483  
     484    /* If the format expands to nothing (%p in some locales, for
     485     * example), strftime can return 0.  We actually want to distinguish
     486     * the error case where the buffer is too short, so we just prepend
     487     * an otherwise uninteresting character to prevent the no-output
     488     * case.
     489     */
     490    timefmt = xmalloc (strlen (fmt) + 2u);
     491    timefmt[0] = '_';
     492    memcpy (timefmt + 1, fmt, strlen (fmt) + 1);
     493  
     494    /* altered_time is a similar time, but in which both
     495     * digits of the seconds field are different.
     496     */
     497    altered_time = *p;
     498    if (altered_time.tm_sec >= 11)
     499      altered_time.tm_sec -= 11;
     500    else
     501      altered_time.tm_sec += 11;
     502  
     503    /* If we call strftime() with buf_size=0, the program will coredump
     504     * on Solaris, since it unconditionally writes the terminating null
     505     * character.
     506     */
     507    if (buf == NULL)
     508      {
     509        buf_size = 1u;
     510        buf = xmalloc (buf_size);
     511      }
     512    while (true)
     513      {
     514        /* I'm not sure that Solaris will return 0 when the buffer is too small.
     515         * Therefore we do not check for (buf_used != 0) as the termination
     516         * condition.
     517         */
     518        size_t buf_used = strftime (buf, buf_size, timefmt, p);
     519        if (buf_used              /* Conforming POSIX system */
     520            && (buf_used < buf_size)) /* Solaris workaround */
     521          {
     522            char *altbuf;
     523            size_t i = 0, n = 0;
     524            size_t final_len = (buf_used
     525                                + 1u /* for \0 */
     526                                + ns_size);
     527            buf = xrealloc (buf, final_len);
     528            buf_size = final_len;
     529            altbuf = xmalloc (final_len);
     530            strftime (altbuf, buf_size, timefmt, &altered_time);
     531  
     532            /* Find the seconds digits; they should be the only changed part.
     533             * In theory the result of the two formatting operations could differ in
     534             * more than just one sequence of decimal digits (for example %X might
     535             * in theory return a spelled-out time like "thirty seconds past noon").
     536             * When that happens, we just avoid inserting the nanoseconds field.
     537             */
     538            if (scan_for_digit_differences (buf, altbuf, &i, &n)
     539                && (2==n) && !isdigit ((unsigned char)buf[i+n]))
     540              {
     541                const size_t end_of_seconds = i + n;
     542                const size_t suffix_len = buf_used-(end_of_seconds)+1;
     543  
     544                /* Move the tail (including the \0).  Note that this
     545                 * is a move of an overlapping memory block, so we
     546                 * must use memmove instead of memcpy.  Then insert
     547                 * the nanoseconds (but not its trailing \0).
     548                 */
     549                assert (end_of_seconds + ns_size + suffix_len == final_len);
     550                memmove (buf+end_of_seconds+ns_size,
     551                         buf+end_of_seconds,
     552                         suffix_len);
     553                memcpy (buf+i+n, ns, ns_size);
     554              }
     555            else
     556              {
     557                /* No seconds digits.  No need to insert anything. */
     558              }
     559            /* The first character of buf is the underscore, which we actually
     560             * don't want.
     561             */
     562            free (timefmt);
     563            free (altbuf);
     564            return buf+1;
     565          }
     566        else
     567          {
     568            buf = x2nrealloc (buf, &buf_size, sizeof *buf);
     569          }
     570      }
     571  }
     572  
     573  /* Return a static string formatting the time WHEN according to the
     574   * strftime format character KIND.
     575   *
     576   * This function contains a number of assertions.  These look like
     577   * runtime checks of the results of computations, which would be a
     578   * problem since external events should not be tested for with
     579   * "assert" (instead you should use "if").  However, they are not
     580   * really runtime checks.  The assertions actually exist to verify
     581   * that the various buffers are correctly sized.
     582   */
     583  static char *
     584  format_date (struct timespec ts, int kind)
     585  {
     586    /* In theory, we use an extra 10 characters for 9 digits of
     587     * nanoseconds and 1 for the decimal point.  However, the real
     588     * world is more complex than that.
     589     *
     590     * For example, some systems return junk in the tv_nsec part of
     591     * st_birthtime.  An example of this is the NetBSD-4.0-RELENG kernel
     592     * (at Sat Mar 24 18:46:46 2007) running a NetBSD-3.1-RELEASE
     593     * runtime and examining files on an msdos filesytem.  So for that
     594     * reason we set NS_BUF_LEN to 32, which is simply "long enough" as
     595     * opposed to "exactly the right size".  Note that the behaviour of
     596     * NetBSD appears to be a result of the use of uninitialized data,
     597     * as it's not 100% reproducible (more like 25%).
     598     */
     599    enum {
     600      NS_BUF_LEN = 32,
     601      DATE_LEN_PERCENT_APLUS=21   /* length of result of %A+ (it's longer than %c)*/
     602    };
     603    static char buf[128u+10u + MAX(DATE_LEN_PERCENT_APLUS,
     604                              MAX (LONGEST_HUMAN_READABLE + 2, NS_BUF_LEN+64+200))];
     605    char ns_buf[NS_BUF_LEN]; /* -.9999999990 (- sign can happen!)*/
     606    int  charsprinted, need_ns_suffix;
     607    struct tm *tm;
     608    char fmt[12];
     609  
     610    /* human_readable() assumes we pass a buffer which is at least as
     611     * long as LONGEST_HUMAN_READABLE.  We use an assertion here to
     612     * ensure that no nasty unsigned overflow happened in our calculation
     613     * of the size of buf.  Do the assertion here rather than in the
     614     * code for %@ so that we find the problem quickly if it exists.  If
     615     * you want to submit a patch to move this into the if statement, go
     616     * ahead, I'll apply it.  But include performance timings
     617     * demonstrating that the performance difference is actually
     618     * measurable.
     619     */
     620    verify (sizeof (buf) >= LONGEST_HUMAN_READABLE);
     621  
     622    charsprinted = 0;
     623    need_ns_suffix = 0;
     624  
     625    /* Format the main part of the time. */
     626    if (kind == '+')
     627      {
     628        /* Avoid %F, some Unix versions lack it.  For example:
     629           HP Tru64 UNIX V5.1B (Rev. 2650); Wed Feb 17 22:59:59 CST 2016
     630           Also, some older HP-UX versions expand %F as the full month (like %B).
     631           Reported by Steven M. Schweda <sms@antinode.info> */
     632        strcpy (fmt, "%Y-%m-%d+%T");
     633        need_ns_suffix = 1;
     634      }
     635    else
     636      {
     637        fmt[0] = '%';
     638        fmt[1] = kind;
     639        fmt[2] = '\0';
     640  
     641        /* %a, %c, and %t are handled in ctime_format() */
     642        switch (kind)
     643          {
     644          case 'S':
     645          case 'T':
     646          case 'X':
     647          case '@':
     648            need_ns_suffix = 1;
     649            break;
     650          default:
     651            need_ns_suffix = 0;
     652            break;
     653          }
     654      }
     655  
     656    if (need_ns_suffix)
     657      {
     658        /* Format the nanoseconds part.  Leave a trailing zero to
     659         * discourage people from writing scripts which extract the
     660         * fractional part of the timestamp by using column offsets.
     661         * The reason for discouraging this is that in the future, the
     662         * granularity may not be nanoseconds.
     663         */
     664        charsprinted = snprintf (ns_buf, NS_BUF_LEN, ".%09ld0", (long int)ts.tv_nsec);
     665        assert (charsprinted < NS_BUF_LEN);
     666      }
     667    else
     668      {
     669        charsprinted = 0;
     670        ns_buf[0] = 0;
     671      }
     672  
     673    if (kind != '@')
     674      {
     675        tm = localtime (&ts.tv_sec);
     676        if (tm)
     677          {
     678            char *s = do_time_format (fmt, tm, ns_buf, charsprinted);
     679            if (s)
     680              return s;
     681          }
     682      }
     683  
     684    /* If we get to here, either the format was %@, or we have fallen back to it
     685     * because strftime failed.
     686     */
     687    if (1)
     688      {
     689        uintmax_t w = ts.tv_sec;
     690        size_t used, len, remaining;
     691  
     692        /* XXX: note that we are negating an unsigned type which is the
     693         * widest possible unsigned type.
     694         */
     695        char *p = human_readable (ts.tv_sec < 0 ? -w : w, buf + 1,
     696                                  human_ceiling, 1, 1);
     697        assert (p > buf);
     698        assert (p < (buf + (sizeof buf)));
     699        if (ts.tv_sec < 0)
     700          *--p = '-'; /* XXX: Ugh, relying on internal details of human_readable(). */
     701  
     702        /* Add the nanoseconds part.  Because we cannot enforce a
     703         * particlar implementation of human_readable, we cannot assume
     704         * any particular value for (p-buf).  So we need to be careful
     705         * that there is enough space remaining in the buffer.
     706         */
     707        if (need_ns_suffix)
     708          {
     709            len = strlen (p);
     710            used = (p-buf) + len; /* Offset into buf of current end */
     711            assert (sizeof buf > used); /* Ensure we can perform subtraction safely. */
     712            remaining = sizeof buf - used - 1u; /* allow space for NUL */
     713  
     714            if (strlen (ns_buf) >= remaining)
     715              {
     716                error (0, 0,
     717                       "charsprinted=%ld but remaining=%lu: ns_buf=%s",
     718                       (long)charsprinted, (unsigned long)remaining, ns_buf);
     719              }
     720            assert (strlen (ns_buf) < remaining);
     721            strcat (p, ns_buf);
     722          }
     723        return p;
     724      }
     725  }
     726  
     727  static const char *weekdays[] =
     728    {
     729      "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
     730    };
     731  static const char * months[] =
     732    {
     733      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
     734      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
     735    };
     736  
     737  
     738  static char *
     739  ctime_format (struct timespec ts)
     740  {
     741    const struct tm * ptm;
     742  #define TIME_BUF_LEN 1024
     743    static char resultbuf[TIME_BUF_LEN];
     744    int nout;
     745  
     746    ptm = localtime (&ts.tv_sec);
     747    if (ptm)
     748      {
     749        assert (ptm->tm_wday >=  0);
     750        assert (ptm->tm_wday <   7);
     751        assert (ptm->tm_mon  >=  0);
     752        assert (ptm->tm_mon  <  12);
     753        assert (ptm->tm_hour >=  0);
     754        assert (ptm->tm_hour <  24);
     755        assert (ptm->tm_min  <  60);
     756        assert (ptm->tm_sec  <= 61); /* allows 2 leap seconds. */
     757  
     758        /* wkday mon mday hh:mm:ss.nnnnnnnnn yyyy */
     759        nout = snprintf (resultbuf, TIME_BUF_LEN,
     760                         "%3s %3s %2d %02d:%02d:%02d.%09ld0 %04d",
     761                         weekdays[ptm->tm_wday],
     762                         months[ptm->tm_mon],
     763                         ptm->tm_mday,
     764                         ptm->tm_hour,
     765                         ptm->tm_min,
     766                         ptm->tm_sec,
     767                         (long int)ts.tv_nsec,
     768                         1900 + ptm->tm_year);
     769  
     770        assert (nout < TIME_BUF_LEN);
     771        return resultbuf;
     772      }
     773    else
     774      {
     775        /* The time cannot be represented as a struct tm.
     776           Output it as an integer.  */
     777        return format_date (ts, '@');
     778      }
     779  }
     780  
     781  static double
     782  file_sparseness (const struct stat *p)
     783  {
     784    if (0 == p->st_size)
     785      {
     786        if (0 == ST_NBLOCKS(*p))
     787          return 1.0;
     788        else
     789          return ST_NBLOCKS(*p) < 0 ? -HUGE_VAL : HUGE_VAL;
     790      }
     791    else
     792      {
     793        double blklen = ST_NBLOCKSIZE * (double)ST_NBLOCKS(*p);
     794        return blklen / p->st_size;
     795      }
     796  }
     797  
     798  
     799  static void
     800  checked_fprintf (struct format_val *dest, const char *fmt, ...)
     801  {
     802    int rv;
     803    va_list ap;
     804  
     805    va_start (ap, fmt);
     806    rv = vfprintf (dest->stream, fmt, ap);
     807    va_end (ap);
     808    if (rv < 0)
     809      nonfatal_nontarget_file_error (errno, dest->filename);
     810  }
     811  
     812  static void
     813  checked_print_quoted (struct format_val *dest,
     814                        const char *format, const char *s)
     815  {
     816    int rv = print_quoted (dest->stream, dest->quote_opts, dest->dest_is_tty,
     817                           format, s);
     818    if (rv < 0)
     819      nonfatal_nontarget_file_error (errno, dest->filename);
     820  }
     821  
     822  
     823  static void
     824  checked_fwrite (void *p, size_t siz, size_t nmemb, struct format_val *dest)
     825  {
     826    const size_t items_written = fwrite (p, siz, nmemb, dest->stream);
     827    if (items_written < nmemb)
     828      nonfatal_nontarget_file_error (errno, dest->filename);
     829  }
     830  
     831  static void
     832  checked_fflush (struct format_val *dest)
     833  {
     834    if (0 != fflush (dest->stream))
     835      {
     836        nonfatal_nontarget_file_error (errno, dest->filename);
     837      }
     838  }
     839  
     840  static const char*
     841  mode_to_filetype (mode_t m)
     842  {
     843  #define HANDLE_TYPE(t,letter) if (m==t) { return letter; }
     844  #ifdef S_IFREG
     845    HANDLE_TYPE(S_IFREG,  "f");   /* regular file */
     846  #endif
     847  #ifdef S_IFDIR
     848    HANDLE_TYPE(S_IFDIR,  "d");   /* directory */
     849  #endif
     850  #ifdef S_IFLNK
     851    HANDLE_TYPE(S_IFLNK,  "l");   /* symbolic link */
     852  #endif
     853  #ifdef S_IFSOCK
     854    HANDLE_TYPE(S_IFSOCK, "s");   /* Unix domain socket */
     855  #endif
     856  #ifdef S_IFBLK
     857    HANDLE_TYPE(S_IFBLK,  "b");   /* block device */
     858  #endif
     859  #ifdef S_IFCHR
     860    HANDLE_TYPE(S_IFCHR,  "c");   /* character device */
     861  #endif
     862  #ifdef S_IFIFO
     863    HANDLE_TYPE(S_IFIFO,  "p");   /* FIFO */
     864  #endif
     865  #ifdef S_IFDOOR
     866    HANDLE_TYPE(S_IFDOOR, "D");   /* Door (e.g. on Solaris) */
     867  #endif
     868    return "U";                   /* Unknown */
     869  }
     870  
     871  
     872  
     873  static void
     874  do_fprintf (struct format_val *dest,
     875              struct segment *segment,
     876              const char *pathname,
     877              const struct stat *stat_buf)
     878  {
     879    char hbuf[LONGEST_HUMAN_READABLE + 1];
     880    const char *cp;
     881  
     882    switch (segment->segkind)
     883      {
     884      case KIND_PLAIN:    /* Plain text string (no % conversion). */
     885        /* trusted */
     886        checked_fwrite(segment->text, 1, segment->text_len, dest);
     887        break;
     888  
     889      case KIND_STOP:             /* Terminate argument and flush output. */
     890        /* trusted */
     891        checked_fwrite (segment->text, 1, segment->text_len, dest);
     892        checked_fflush (dest);
     893        break;
     894  
     895      case KIND_FORMAT:
     896        switch (segment->format_char[0])
     897          {
     898          case 'a':               /* atime in `ctime' format. */
     899            /* UNTRUSTED, probably unexploitable */
     900            checked_fprintf (dest, segment->text, ctime_format (get_stat_atime (stat_buf)));
     901            break;
     902          case 'b':               /* size in 512-byte blocks */
     903            /* UNTRUSTED, probably unexploitable */
     904            checked_fprintf (dest, segment->text,
     905                             human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf),
     906                                             hbuf, human_ceiling,
     907                                             ST_NBLOCKSIZE, 512));
     908            break;
     909          case 'c':               /* ctime in `ctime' format */
     910            /* UNTRUSTED, probably unexploitable */
     911            checked_fprintf (dest, segment->text, ctime_format (get_stat_ctime (stat_buf)));
     912            break;
     913          case 'd':               /* depth in search tree */
     914            /* UNTRUSTED, probably unexploitable */
     915            checked_fprintf (dest, segment->text, state.curdepth);
     916            break;
     917          case 'D':               /* Device on which file exists (stat.st_dev) */
     918            /* trusted */
     919            checked_fprintf (dest, segment->text,
     920                             human_readable ((uintmax_t) stat_buf->st_dev, hbuf,
     921                                             human_ceiling, 1, 1));
     922            break;
     923          case 'f':               /* base name of path */
     924            /* sanitised */
     925            {
     926              char *base = base_name (pathname);
     927              checked_print_quoted (dest, segment->text, base);
     928              free (base);
     929            }
     930            break;
     931          case 'F':               /* file system type */
     932            /* trusted */
     933            checked_print_quoted (dest, segment->text, filesystem_type (stat_buf, pathname));
     934            break;
     935          case 'g':               /* group name */
     936            /* trusted */
     937            /* (well, the actual group is selected by the user but
     938             * its name was selected by the system administrator)
     939             */
     940            {
     941              struct group *g;
     942  
     943              g = getgrgid (stat_buf->st_gid);
     944              if (g)
     945                {
     946                  segment->text[segment->text_len] = 's';
     947                  checked_fprintf (dest, segment->text, g->gr_name);
     948                  break;
     949                }
     950            }
     951            FALLTHROUGH; /*...sometimes, so 'G' case.*/
     952  
     953          case 'G':               /* GID number */
     954            /* UNTRUSTED, probably unexploitable */
     955            checked_fprintf (dest, segment->text,
     956                             human_readable ((uintmax_t) stat_buf->st_gid, hbuf,
     957                                             human_ceiling, 1, 1));
     958            break;
     959          case 'h':               /* leading directories part of path */
     960            /* sanitised */
     961            {
     962              char *pname = xstrdup (pathname);
     963  
     964              /* Remove trailing slashes - unless it's the root '/' directory.  */
     965              char *s = pname + strlen (pname) -1;
     966              for ( ; pname <= s; s--)
     967                if (*s != '/')
     968                  break;
     969              if (pname < s && *(s+1) == '/')
     970                *(s+1) = '\0';
     971  
     972              s = strrchr (pname, '/');
     973              if (s == NULL)     /* No leading directories. */
     974                {
     975                  /* If there is no slash in the pathname, we still
     976                   * print the string because it contains characters
     977                   * other than just '%s'.  The %h expands to ".".
     978                   */
     979                  checked_print_quoted (dest, segment->text, ".");
     980                }
     981              else
     982                {
     983                  *s = '\0';
     984                  checked_print_quoted (dest, segment->text, pname);
     985                }
     986              free (pname);
     987            }
     988            break;
     989  
     990          case 'H':               /* ARGV element file was found under */
     991            /* trusted */
     992            {
     993              char *s = xmalloc (state.starting_path_length+1);
     994              memcpy (s, pathname, state.starting_path_length);
     995              s[state.starting_path_length] = 0;
     996              checked_fprintf (dest, segment->text, s);
     997              free (s);
     998            }
     999            break;
    1000  
    1001          case 'i':               /* inode number */
    1002            /* UNTRUSTED, but not exploitable I think */
    1003            /* POSIX does not guarantee that ino_t is unsigned or even
    1004             * integral (except as an XSI extension), but we'll work on
    1005             * fixing that if we ever get a report of a system where
    1006             * ino_t is indeed a signed integral type or a non-integral
    1007             * arithmetic type. */
    1008            checked_fprintf (dest, segment->text,
    1009                             human_readable ((uintmax_t) stat_buf->st_ino, hbuf,
    1010                                             human_ceiling,
    1011                                             1, 1));
    1012            break;
    1013          case 'k':               /* size in 1K blocks */
    1014            /* UNTRUSTED, but not exploitable I think */
    1015            checked_fprintf (dest, segment->text,
    1016                             human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf),
    1017                                             hbuf, human_ceiling,
    1018                                             ST_NBLOCKSIZE, 1024));
    1019            break;
    1020          case 'l':               /* object of symlink */
    1021            /* sanitised */
    1022  #ifdef S_ISLNK
    1023            {
    1024              char *linkname = NULL;
    1025  
    1026              if (S_ISLNK (stat_buf->st_mode))
    1027                {
    1028                  linkname = areadlinkat (state.cwd_dir_fd, state.rel_pathname);
    1029                  if (linkname == NULL)
    1030                    {
    1031                      nonfatal_target_file_error (errno, pathname);
    1032                      state.exit_status = EXIT_FAILURE;
    1033                    }
    1034                }
    1035              if (linkname)
    1036                {
    1037                  checked_print_quoted (dest, segment->text, linkname);
    1038                }
    1039              else
    1040                {
    1041                  /* We still need to honour the field width etc., so this is
    1042                   * not a no-op.
    1043                   */
    1044                  checked_print_quoted (dest, segment->text, "");
    1045                }
    1046              free (linkname);
    1047            }
    1048  #endif                          /* S_ISLNK */
    1049            break;
    1050  
    1051          case 'M':               /* mode as 10 chars (eg., "-rwxr-x--x" */
    1052            /* UNTRUSTED, probably unexploitable */
    1053            {
    1054              char modestring[16] ;
    1055              filemodestring (stat_buf, modestring);
    1056              modestring[10] = '\0';
    1057              checked_fprintf (dest, segment->text, modestring);
    1058            }
    1059            break;
    1060  
    1061          case 'm':               /* mode as octal number (perms only) */
    1062            /* UNTRUSTED, probably unexploitable */
    1063            {
    1064              /* Output the mode portably using the traditional numbers,
    1065                 even if the host unwisely uses some other numbering
    1066                 scheme.  But help the compiler in the common case where
    1067                 the host uses the traditional numbering scheme.  */
    1068              mode_t m = stat_buf->st_mode;
    1069              bool traditional_numbering_scheme =
    1070                (S_ISUID == 04000 && S_ISGID == 02000 && S_ISVTX == 01000
    1071                 && S_IRUSR == 00400 && S_IWUSR == 00200 && S_IXUSR == 00100
    1072                 && S_IRGRP == 00040 && S_IWGRP == 00020 && S_IXGRP == 00010
    1073                 && S_IROTH == 00004 && S_IWOTH == 00002 && S_IXOTH == 00001);
    1074              checked_fprintf (dest, segment->text,
    1075                       (traditional_numbering_scheme
    1076                        ? m & MODE_ALL
    1077                        : ((m & S_ISUID ? 04000 : 0)
    1078                           | (m & S_ISGID ? 02000 : 0)
    1079                           | (m & S_ISVTX ? 01000 : 0)
    1080                           | (m & S_IRUSR ? 00400 : 0)
    1081                           | (m & S_IWUSR ? 00200 : 0)
    1082                           | (m & S_IXUSR ? 00100 : 0)
    1083                           | (m & S_IRGRP ? 00040 : 0)
    1084                           | (m & S_IWGRP ? 00020 : 0)
    1085                           | (m & S_IXGRP ? 00010 : 0)
    1086                           | (m & S_IROTH ? 00004 : 0)
    1087                           | (m & S_IWOTH ? 00002 : 0)
    1088                           | (m & S_IXOTH ? 00001 : 0))));
    1089            }
    1090            break;
    1091  
    1092          case 'n':               /* number of links */
    1093            /* UNTRUSTED, probably unexploitable */
    1094            checked_fprintf (dest, segment->text,
    1095                     human_readable ((uintmax_t) stat_buf->st_nlink,
    1096                                     hbuf,
    1097                                     human_ceiling,
    1098                                     1, 1));
    1099            break;
    1100  
    1101          case 'p':               /* pathname */
    1102            /* sanitised */
    1103            checked_print_quoted (dest, segment->text, pathname);
    1104            break;
    1105  
    1106          case 'P':               /* pathname with ARGV element stripped */
    1107            /* sanitised */
    1108            if (state.curdepth > 0)
    1109              {
    1110                cp = pathname + state.starting_path_length;
    1111                if (*cp == '/')
    1112                  /* Move past the slash between the ARGV element
    1113                     and the rest of the pathname.  But if the ARGV element
    1114                     ends in a slash, we didn't add another, so we've
    1115                     already skipped past it.  */
    1116                  cp++;
    1117              }
    1118            else
    1119              {
    1120                cp = "";
    1121              }
    1122            checked_print_quoted (dest, segment->text, cp);
    1123            break;
    1124  
    1125          case 's':               /* size in bytes */
    1126            /* UNTRUSTED, probably unexploitable */
    1127            checked_fprintf (dest, segment->text,
    1128                     human_readable ((uintmax_t) stat_buf->st_size,
    1129                                     hbuf, human_ceiling, 1, 1));
    1130            break;
    1131  
    1132          case 'S':               /* sparseness */
    1133            /* UNTRUSTED, probably unexploitable */
    1134            checked_fprintf (dest, segment->text, file_sparseness (stat_buf));
    1135            break;
    1136  
    1137          case 't':               /* mtime in `ctime' format */
    1138            /* UNTRUSTED, probably unexploitable */
    1139            checked_fprintf (dest, segment->text,
    1140                             ctime_format (get_stat_mtime (stat_buf)));
    1141            break;
    1142  
    1143          case 'u':               /* user name */
    1144            /* trusted */
    1145            /* (well, the actual user is selected by the user on systems
    1146             * where chown is not restricted, but the user name was
    1147             * selected by the system administrator)
    1148             */
    1149            {
    1150              struct passwd *p;
    1151  
    1152              p = getpwuid (stat_buf->st_uid);
    1153              if (p)
    1154                {
    1155                  segment->text[segment->text_len] = 's';
    1156                  checked_fprintf (dest, segment->text, p->pw_name);
    1157                  break;
    1158                }
    1159            }
    1160            FALLTHROUGH; /* .. to case U */
    1161  
    1162          case 'U':               /* UID number */
    1163            /* UNTRUSTED, probably unexploitable */
    1164            checked_fprintf (dest, segment->text,
    1165                             human_readable ((uintmax_t) stat_buf->st_uid, hbuf,
    1166                                             human_ceiling, 1, 1));
    1167            break;
    1168  
    1169            /* %Y: type of file system entry like `ls -l`:
    1170             *     (d,-,l,s,p,b,c,n) n=nonexistent (symlink)
    1171             */
    1172          case 'Y':               /* in case of symlink */
    1173            /* trusted */
    1174            {
    1175  #ifdef S_ISLNK
    1176              if (S_ISLNK (stat_buf->st_mode))
    1177                {
    1178                  struct stat sbuf;
    1179                  /* %Y needs to stat the symlink target regardless of
    1180                   * whether we would normally follow symbolic links or not.
    1181                   * (Actually we do not even come here when following_links()
    1182                   *  other than the ENOENT case.)
    1183                   */
    1184                  if (fstatat (state.cwd_dir_fd, state.rel_pathname, &sbuf, 0) != 0)
    1185                    {
    1186                      if ( (errno == ENOENT) || (errno == ENOTDIR) )
    1187                        {
    1188                          checked_fprintf (dest, segment->text, "N");
    1189                          break;
    1190                        }
    1191                      else if ( errno == ELOOP )
    1192                        {
    1193                          checked_fprintf (dest, segment->text, "L");
    1194                          break;
    1195                        }
    1196                      else
    1197                        {
    1198                          checked_fprintf (dest, segment->text, "?");
    1199                          error (0, errno, "%s",
    1200                                 safely_quote_err_filename (0, pathname));
    1201                          /* exit_status = EXIT_FAILURE;
    1202                             return ; */
    1203                          break;
    1204                        }
    1205                    }
    1206                  checked_fprintf (dest, segment->text,
    1207                                   mode_to_filetype (sbuf.st_mode & S_IFMT));
    1208                }
    1209              else
    1210  #endif /* S_ISLNK */
    1211                {
    1212                  checked_fprintf (dest, segment->text,
    1213                                   mode_to_filetype (stat_buf->st_mode & S_IFMT));
    1214                }
    1215            }
    1216            break;
    1217  
    1218          case 'y':
    1219            /* trusted */
    1220            {
    1221              checked_fprintf (dest, segment->text,
    1222                               mode_to_filetype (stat_buf->st_mode & S_IFMT));
    1223            }
    1224            break;
    1225  
    1226          case 'Z':               /* SELinux security context */
    1227            {
    1228              char *scontext;
    1229              int rv = (*options.x_getfilecon) (state.cwd_dir_fd, state.rel_pathname,
    1230                                                &scontext);
    1231              if (rv < 0)
    1232                {
    1233                  /* If getfilecon fails, there will in the general case
    1234                     still be some text to print.   We just make %Z expand
    1235                     to an empty string. */
    1236                  checked_fprintf (dest, segment->text, "");
    1237  
    1238                  error (0, errno, _("getfilecon failed: %s"),
    1239                      safely_quote_err_filename (0, pathname));
    1240                  state.exit_status = EXIT_FAILURE;
    1241                }
    1242              else
    1243                {
    1244                  checked_fprintf (dest, segment->text, scontext);
    1245                  freecon (scontext);
    1246                }
    1247            }
    1248            break;
    1249  
    1250          case '%':
    1251            checked_fwrite (segment->text, 1, segment->text_len, dest);
    1252            break;
    1253  
    1254          case 0:
    1255  	  /* Trailing single %.  This should have been rejected by
    1256  	     insert_fprintf.  We use %s here in the error message
    1257  	     simply to ensure that the error message matches the one
    1258  	     in insert_fprintf, easing the translation burden.
    1259  	   */
    1260  	  die (EXIT_FAILURE, 0, _("error: %s at end of format string"), "%");
    1261  	  /*NOTREACHED*/
    1262  	  break;
    1263          }
    1264        /* end of KIND_FORMAT case */
    1265        break;
    1266      }
    1267  }
    1268  
    1269  bool
    1270  pred_fprintf (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
    1271  {
    1272    struct format_val *dest = &pred_ptr->args.printf_vec;
    1273    struct segment *segment;
    1274  
    1275    for (segment = dest->segment; segment; segment = segment->next)
    1276      {
    1277        if ( (KIND_FORMAT == segment->segkind) && segment->format_char[1]) /* Component of date. */
    1278          {
    1279            struct timespec ts;
    1280            int valid = 0;
    1281  
    1282            switch (segment->format_char[0])
    1283              {
    1284              case 'A':
    1285                ts = get_stat_atime (stat_buf);
    1286                valid = 1;
    1287                break;
    1288              case 'B':
    1289                ts = get_stat_birthtime (stat_buf);
    1290                if ('@' == segment->format_char[1])
    1291                  valid = 1;
    1292                else
    1293                  valid = (ts.tv_nsec >= 0);
    1294                break;
    1295              case 'C':
    1296                ts = get_stat_ctime (stat_buf);
    1297                valid = 1;
    1298                break;
    1299              case 'T':
    1300                ts = get_stat_mtime (stat_buf);
    1301                valid = 1;
    1302                break;
    1303              default:
    1304                assert (0);
    1305                abort ();
    1306              }
    1307            /* We trust the output of format_date not to contain
    1308             * nasty characters, though the value of the date
    1309             * is itself untrusted data.
    1310             */
    1311            if (valid)
    1312              {
    1313                /* trusted */
    1314                checked_fprintf (dest, segment->text,
    1315                                 format_date (ts, segment->format_char[1]));
    1316              }
    1317            else
    1318              {
    1319                /* The specified timestamp is not available, output
    1320                 * nothing for the timestamp, but use the rest (so that
    1321                 * for example find foo -printf '[%Bs] %p\n' can print
    1322                 * "[] foo").
    1323                 */
    1324                /* trusted */
    1325                checked_fprintf (dest, segment->text, "");
    1326              }
    1327          }
    1328        else
    1329          {
    1330            /* Print a segment which is not a date. */
    1331            do_fprintf (dest, segment, pathname, stat_buf);
    1332          }
    1333      }
    1334    return true;
    1335  }