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