? autom4te.cache
? config.guess
? gnulib
? stamp-h1
? find/find.c.anotated
? find/find.c.log
? find/x
? lib/.deps
? m4/Makefile
? po/.pot
? po/stamp-po
? po/tmp
Index: NEWS
===================================================================
RCS file: /cvsroot/findutils/findutils/NEWS,v
retrieving revision 1.74
diff -u -p -r1.74 NEWS
--- NEWS	30 May 2005 08:25:13 -0000	1.74
+++ NEWS	7 Jun 2005 08:07:28 -0000
@@ -7,6 +7,12 @@ is incompatible with POSIX in obscure ca
 Use the new syntax "find ... -perm /MODE" instead.  Old usages will
 still continue to work, so long as they don't conflict with POSIX.
 
+If the output is going to a terminal, the -print, -fprint, -printf and
+-fprintf actions now quote "unusual" characters to prevent unwanted
+effects on the terminal.  See "Unusual Characters in File Names" for
+further details.  There is no change to the behaviour when the output
+is not going to a terminal.
+
 ** Functional Changes to locate
 
 The locate command will now read each locate database at most once.
Index: doc/find.texi
===================================================================
RCS file: /cvsroot/findutils/findutils/doc/find.texi,v
retrieving revision 1.80
diff -u -p -r1.80 find.texi
--- doc/find.texi	30 May 2005 08:25:13 -0000	1.80
+++ doc/find.texi	7 Jun 2005 08:07:30 -0000
@@ -1759,6 +1759,7 @@ with the @samp{--eof} option. 
 @menu
 * Unsafe File Name Handling::
 * Safe File Name Handling::
+* Unusual Characters in File Names::
 * Limiting Command Size::
 * Interspersing File Names::
 @end menu
@@ -1861,6 +1862,70 @@ can do this in just one step:
 xargs --null --arg-file=<(find / -name xyzzy -print0) munge
 @end example
 
+@node Unusual Characters in File Names
+@subsubsection Unusual Characters in File Names
+As discussed above, you often need to be careful about how the names
+of files are handled by @code{find} and other programs.  If the output
+of @code{find} is not going to another program but instead is being
+shown on a terminal, this can still be a problem.  For example, some
+character sequences can reprogram the function keys on some terminals.
+@xref{Security Considerations}, for a discussion of other security
+problems relating to @code{find}.
+
+Unusual characters are handled differently by various
+actions, as described below.
+
+@table @samp
+@item -print0
+@itemx -fprint0
+Always print the exact filename, unchanged, even if the output is
+going to a terminal.
+@item -ok
+@itemx -okdir
+Always print the exact filename, unchanged.  This will probably change
+in a future release.
+@item -ls
+@itemx -fls
+Unusual characters are always escaped.  White space, backslash, and
+double quote characters are printed using C-style escaping (for
+example @samp{\f}, @samp{\"}).  Other unusual characters are printed
+using an octal escape.  Printable characters (for @samp{-ls} and
+@samp{-fls} these are the characters between octal 040 and 0177) are
+printed as-is.
+@item -printf
+@itemx -fprintf
+If the output is not going to a terminal, it is printed as-is.
+Otherwise, the result depends on which directive is in use:
+
+@table @asis
+@item %D, %F, %H, %Y, %y 
+These expand to values which are not under control of files' ownwers,
+and so are printed as-is.
+@item  %a, %b, %c, %d, %g, %G, %i, %k, %m, %M, %n, %s, %t, %u, %U
+These have values which are under the control of files' ownwers but which
+cannot be used to send arbitrary data to the terminal, and so these
+are printed as-is.  
+@item %f, %h, %l, %p, %P 
+The output of these directives is quoted if the output is going to a
+terminal.
+
+This quoting is performed in the same way as for GNU @code{ls}.   
+This is not the same quoting mechanism as the one used for  @samp{-ls} and
+@samp{fls}.   If you are able to decide what format to use for the output 
+of @code{find} then it is normally better to use @samp{\0} as a terminator
+than to use newline, as file names can contain white space and newline
+characters.
+@end table 
+@item -print
+@itemx -fprint
+Quoting is handled in the same way as for the @samp{%p} directive
+of @samp{-printf} and @samp{-fprintf}.  If you are using @code{find} in
+a script or in a situation where the matched files might have
+arbitrary names, you should consider using @samp{-print0} instead of
+@samp{-print}.
+@end table
+
+
 @node Limiting Command Size
 @subsubsection Limiting Command Size
 
@@ -2041,6 +2106,8 @@ find /usr/local -type f -perm /a=x \
   \( -execdir unstripped '@{@}' \; -fprint ubins -o -fprint sbins \)
 @end example
 
+
+
 @node Common Tasks, Databases, Actions, Top
 @chapter Common Tasks
 
Index: find/defs.h
===================================================================
RCS file: /cvsroot/findutils/findutils/find/defs.h,v
retrieving revision 1.36
diff -u -p -r1.36 defs.h
--- find/defs.h	12 Feb 2005 21:11:04 -0000	1.36
+++ find/defs.h	7 Jun 2005 08:07:31 -0000
@@ -264,6 +264,8 @@ struct format_val
 {
   struct segment *segment;	/* Linked list of segments. */
   FILE *stream;			/* Output stream to print on. */
+  boolean dest_is_tty;		/* True if the destination is a terminal. */
+  struct quoting_options *quote_opts;
 };
 
 struct predicate
@@ -313,8 +315,8 @@ struct predicate
     struct perm_val perm;	/* perm */
     struct dir_id   fileid;	/* samefile */
     mode_t type;		/* type */
-    FILE *stream;		/* fprint fprint0 */
-    struct format_val printf_vec; /* printf fprintf */
+    FILE *stream;		/* ls fls fprint0 */
+    struct format_val printf_vec; /* printf fprintf fprint  */
   } args;
 
   /* The next predicate in the user input sequence,
Index: find/find.1
===================================================================
RCS file: /cvsroot/findutils/findutils/find/find.1,v
retrieving revision 1.49
diff -u -p -r1.49 find.1
--- find/find.1	23 Feb 2005 00:27:28 -0000	1.49
+++ find/find.1	7 Jun 2005 08:07:31 -0000
@@ -498,7 +498,12 @@ appropriately-named file in a directory 
 
 .IP "\-fls \fIfile\fR"
 True; like \-ls but write to \fIfile\fR like \-fprint.
-The output file is always created, even if the predicate is never matched.
+The output file is always created, even if the predicate is never
+matched.  
+See the
+.B UNUSUAL FILENAMES
+section for information about how unusual characters in filenames are handled.
+
 .IP "\-fprint \fIfile\fR"
 True; print the full file name into file \fIfile\fR.  If \fIfile\fR
 does not exist when \fBfind\fR is run, it is created; if it does
@@ -506,12 +511,21 @@ exist, it is truncated.  The file names 
 ``/dev/stderr'' are handled specially; they refer to the standard
 output and standard error output, respectively.
 The output file is always created, even if the predicate is never matched.
+See the
+.B UNUSUAL FILENAMES
+section for information about how unusual characters in filenames are handled.
 .IP "\-fprint0 \fIfile\fR"
 True; like \-print0 but write to \fIfile\fR like \-fprint.
 The output file is always created, even if the predicate is never matched.
+See the
+.B UNUSUAL FILENAMES
+section for information about how unusual characters in filenames are handled.
 .IP "\-fprintf \fIfile\fR \fIformat\fR"
 True; like \-printf but write to \fIfile\fR like \-fprint.
 The output file is always created, even if the predicate is never matched.
+See the
+.B UNUSUAL FILENAMES
+section for information about how unusual characters in filenames are handled.
 .IP "\-ok \fIcommand\fR ;"
 Like \-exec but ask the user first (on the standard input); if the
 response does not start with `y' or `Y', do not run the command, and
@@ -523,6 +537,9 @@ newline.   If you are piping the output 
 into another program and there is the faintest possibility that the files 
 which you are searching for might contain a newline, then you should 
 seriously consider using the `\-print0' option instead of `\-print'.
+See the
+.B UNUSUAL FILENAMES
+section for information about how unusual characters in filenames are handled.
 .IP "\-okdir \fIcommand\fR ;"
 Like \-execdir but ask the user first (on the standard input); if the
 response does not start with `y' or `Y', do not run the command, and
@@ -735,6 +752,10 @@ and 
 .BR n .
 The `\-' format flag is supported and changes the alignment of a field
 from right-justified (which is the default) to left-justified.
+.PP
+See the
+.B UNUSUAL FILENAMES
+section for information about how unusual characters in filenames are handled.
 
 
 .RE
@@ -761,7 +782,59 @@ an error has already occurred.  
 True; list current file in `ls \-dils' format on standard output.
 The block counts are of 1K blocks, unless the environment variable
 POSIXLY_CORRECT is set, in which case 512-byte blocks are used.
+See the
+.B UNUSUAL FILENAMES
+section for information about how unusual characters in filenames are handled.
+
 
+.SS UNUSUAL FILENAMES
+Many of the actions of 
+.B find
+result in the printing of data which is under the control of other
+users.  This includes file names, sizes, modification times and so
+forth.  File names are a potential problem since they can contain any
+character except '\\0' and '/'.  Unusual characters in file names can
+do unexpected and often undesirable things to your terminal (for
+example, changing the settings of your function keys on some
+terminals).  Unusual characters are handled differently by various
+actions, as described below.
+.IP "\-print0, \-fprint0\"
+Always print the exact filename, unchanged, even if the output is
+going to a terminal.
+.IP "\-ls, \-fls"
+Unusual characters are always escaped.  White space, backslash, and double
+quote characters are printed using C-style escaping (for 
+example '\\f', '\\"').   Other unusual characters are printed using an octal
+escape.   Printable characters (for \-ls and -fls these are the
+characters between octal 040 and 0177) are printed as-is.
+.IP "\-printf, \-fprintf"
+If the output is not going to a terminal, it is printed as-is.
+Otherwise, the result depends on which directive is in use.  The
+directives %D, %F, %g, %G, %H, %Y, and %y expand to values which are
+not under control of files' ownwers, and so are printed as-is.  The
+directives %a, %b, %c, %d, %i, %k, %m, %M, %n, %s, %t, %u and %U have
+values which are under the control of files' ownwers but which cannot
+be used to send arbitrary data to the terminal, and so these are
+printed as-is.  The directives %f, %h, %l, %p and %P are quoted.  This
+quoting is performed in the same way as for GNU
+.BR ls .   
+This is not the same quoting mechanism as the one used for  \-ls and
+\-fls.   If you are able to decide what format to use for the output 
+of 
+.B find
+then it is normally better to use '\\0' as a terminator
+than to use newline, as file names can contain white space and newline
+characters.
+.IP "\-print, \-fprint"
+Quoting is handled in the same way as for \-printf and \-fprintf.
+If you are using 
+.B find
+in a script or in a situation where the matched files might have
+arbitrary names, you should consider using \-print0 instead of
+\-print.
+.P
+The \-ok and \-okdir actions print the current filename as-is.  This
+may change in a future release.
 .SS OPERATORS
 .P
 Listed in order of decreasing precedence:
Index: find/parser.c
===================================================================
RCS file: /cvsroot/findutils/findutils/find/parser.c,v
retrieving revision 1.65
diff -u -p -r1.65 parser.c
--- find/parser.c	25 May 2005 19:57:43 -0000	1.65
+++ find/parser.c	7 Jun 2005 08:07:31 -0000
@@ -23,10 +23,12 @@
 #include <pwd.h>
 #include <grp.h>
 #include <fnmatch.h>
-#include "../gnulib/lib/modechange.h"
+#include "modechange.h"
 #include "modetype.h"
-#include "../gnulib/lib/xstrtol.h"
-#include "../gnulib/lib/xalloc.h"
+#include "xstrtol.h"
+#include "xalloc.h"
+#include "quote.h"
+#include "quotearg.h"
 #include "buildcmd.h"
 #include "nextelem.h"
 
@@ -36,6 +38,12 @@
 #include <sys/file.h>
 #endif
 
+#ifdef HAVE_UNISTD_H
+/* We need <unistd.h> for isatty(). */
+#include <unistd.h>
+#endif
+
+
 #if ENABLE_NLS
 # include <libintl.h>
 # define _(Text) gettext (Text)
@@ -150,6 +158,7 @@ static boolean insert_time PARAMS((char 
 static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type));
 static boolean insert_num PARAMS((char *argv[], int *arg_ptr, PFB pred));
 static FILE *open_output_file PARAMS((char *path));
+static boolean stream_is_tty(FILE *fp);
 
 #ifdef DEBUG
 char *find_pred_name PARAMS((PFB pred_func));
@@ -643,9 +652,12 @@ parse_fprint (char **argv, int *arg_ptr)
   struct predicate *our_pred;
 
   if ((argv == NULL) || (argv[*arg_ptr] == NULL))
-    return (false);
+    return false;
   our_pred = insert_primary (pred_fprint);
-  our_pred->args.stream = open_output_file (argv[*arg_ptr]);
+  our_pred->args.printf_vec.segment = NULL;
+  our_pred->args.printf_vec.stream = open_output_file (argv[*arg_ptr]);
+  our_pred->args.printf_vec.dest_is_tty = stream_is_tty(our_pred->args.printf_vec.stream);
+  our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
   our_pred->side_effects = true;
   our_pred->no_default_print = true;
   our_pred->need_stat = our_pred->need_type = false;
@@ -1308,6 +1320,11 @@ parse_print (char **argv, int *arg_ptr)
   our_pred->side_effects = true;
   our_pred->no_default_print = true;
   our_pred->need_stat = our_pred->need_type = false;
+  our_pred->args.printf_vec.segment = NULL;
+  our_pred->args.printf_vec.stream = stdout;
+  our_pred->args.printf_vec.dest_is_tty = stream_is_tty(stdout);
+  our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
+  
   return (true);
 }
 
@@ -1727,6 +1744,26 @@ insert_type (char **argv, int *arg_ptr, 
   return (true);
 }
 
+
+/* Return true if the file accessed via FP is a terminal.
+ */
+static boolean 
+stream_is_tty(FILE *fp)
+{
+  int fd = fileno(fp);
+  if (-1 == fd)
+    {
+      return false; /* not a valid stream */
+    }
+  else
+    {
+      return isatty(fd) ? true : false;
+    }
+  
+}
+
+
+
 /* If true, we've determined that the current fprintf predicate
    uses stat information. */
 static boolean fprintf_stat_needed;
@@ -1747,6 +1784,8 @@ insert_fprintf (FILE *fp, boolean (*func
   our_pred->side_effects = true;
   our_pred->no_default_print = true;
   our_pred->args.printf_vec.stream = fp;
+  our_pred->args.printf_vec.dest_is_tty = stream_is_tty(fp);
+  our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
   segmentp = &our_pred->args.printf_vec.segment;
   *segmentp = NULL;
 
Index: find/pred.c
===================================================================
RCS file: /cvsroot/findutils/findutils/find/pred.c,v
retrieving revision 1.59
diff -u -p -r1.59 pred.c
--- find/pred.c	16 May 2005 21:14:46 -0000	1.59
+++ find/pred.c	7 Jun 2005 08:07:31 -0000
@@ -33,6 +33,8 @@
 #include "modetype.h"
 #include "filemode.h"
 #include "wait.h"
+#include "quote.h"
+#include "quotearg.h"
 #include "buildcmd.h"
 
 #if ENABLE_NLS
@@ -52,6 +54,14 @@
 #define SIGCHLD SIGCLD
 #endif
 
+
+/* Get mbstate_t, mbrtowc(), mbsinit(), wcwidth().  */
+#if HAVE_WCHAR_H
+# include <wchar.h>
+#endif
+
+
+
 #if HAVE_DIRENT_H
 # include <dirent.h>
 # define NAMLEN(dirent) strlen((dirent)->d_name)
@@ -140,6 +150,43 @@ extern int yesno ();
 # define ST_NBLOCKSIZE 512
 #endif
 
+
+/* 
+   This comment, IN_CTYPE_DOMAIN and ISPRINT were borrowed from 
+   coreutils at Sun Jun  5 21:17:40 2005 UTC.
+
+   Jim Meyering writes:
+
+   "... Some ctype macros are valid only for character codes that
+   isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
+   using /bin/cc or gcc but without giving an ansi option).  So, all
+   ctype uses should be through macros like ISPRINT...  If
+   STDC_HEADERS is defined, then autoconf has verified that the ctype
+   macros don't need to be guarded with references to isascii. ...
+   Defining isascii to 1 should let any compiler worth its salt
+   eliminate the && through constant folding."
+
+   Bruno Haible adds:
+
+   "... Furthermore, isupper(c) etc. have an undefined result if c is
+   outside the range -1 <= c <= 255. One is tempted to write isupper(c)
+   with c being of type `char', but this is wrong if c is an 8-bit
+   character >= 128 which gets sign-extended to a negative value.
+   The macro ISUPPER protects against this as well."  */
+
+#if STDC_HEADERS || (!defined (isascii) && !HAVE_ISASCII)
+# define IN_CTYPE_DOMAIN(c) 1
+#else
+# define IN_CTYPE_DOMAIN(c) isascii(c)
+#endif
+
+
+/* ISPRINT is defined in <sys/euc.h> on at least Solaris2.6 systems.  */
+#undef ISPRINT
+#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
+
+
+
 #undef MAX
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 
@@ -147,6 +194,7 @@ static boolean insert_lname PARAMS((char
 
 static char *format_date PARAMS((time_t when, int kind));
 static char *ctime_format PARAMS((time_t when));
+static void print_quoted (FILE *fp, const struct quoting_options *qopts, boolean dest_is_tty, const char *format, const char *s);
 
 #ifdef	DEBUG
 struct pred_assoc
@@ -531,9 +579,12 @@ pred_fprint (char *pathname, struct stat
   (void) &pathname;
   (void) &stat_buf;
   
-  fputs (pathname, pred_ptr->args.stream);
-  putc ('\n', pred_ptr->args.stream);
-  return (true);
+  print_quoted(pred_ptr->args.printf_vec.stream,
+	       pred_ptr->args.printf_vec.quote_opts,
+	       pred_ptr->args.printf_vec.dest_is_tty,
+	       "%s\n",
+	       pathname);
+  return true;
 }
 
 boolean
@@ -547,10 +598,198 @@ pred_fprint0 (char *pathname, struct sta
   return (true);
 }
 
+
+/* Convert a possibly-signed character to an unsigned character.  This is
+ * a bit safer than casting to unsigned char, since it catches some type
+ * errors that the cast doesn't.  
+ *
+ * This code taken from coreutils' system.h header at 
+ * Sun Jun  5 21:05:21 2005 UTC.
+ */
+static inline unsigned char to_uchar (char ch)
+{
+  return ch;
+}
+
+
+/* Scan BUF, replacing any dangerous-looking characters with question
+ * marks.  This code is taken from the ls.c file in coreutils as at 
+ * Sun Jun  5 20:51:54 2005 UTC.
+ *
+ * This function may shrink the buffer.   Either way, the new length
+ * is returned.
+ */
+static size_t
+qmark_chars(char *buf, size_t len)
+{
+#if HAVE_MBRTOWC
+  if (MB_CUR_MAX > 1)
+    {
+      char const *p = buf;
+      char const *plimit = buf + len;
+      char *q = buf;
+
+      while (p < plimit)
+	switch (*p)
+	  {
+	  case ' ': case '!': case '"': case '#': case '%':
+	  case '&': case '\'': case '(': case ')': case '*':
+	  case '+': case ',': case '-': case '.': case '/':
+	  case '0': case '1': case '2': case '3': case '4':
+	  case '5': case '6': case '7': case '8': case '9':
+	  case ':': case ';': case '<': case '=': case '>':
+	  case '?':
+	  case 'A': case 'B': case 'C': case 'D': case 'E':
+	  case 'F': case 'G': case 'H': case 'I': case 'J':
+	  case 'K': case 'L': case 'M': case 'N': case 'O':
+	  case 'P': case 'Q': case 'R': case 'S': case 'T':
+	  case 'U': case 'V': case 'W': case 'X': case 'Y':
+	  case 'Z':
+	  case '[': case '\\': case ']': case '^': case '_':
+	  case 'a': case 'b': case 'c': case 'd': case 'e':
+	  case 'f': case 'g': case 'h': case 'i': case 'j':
+	  case 'k': case 'l': case 'm': case 'n': case 'o':
+	  case 'p': case 'q': case 'r': case 's': case 't':
+	  case 'u': case 'v': case 'w': case 'x': case 'y':
+	  case 'z': case '{': case '|': case '}': case '~':
+	    /* These characters are printable ASCII characters.  */
+	    *q++ = *p++;
+	    break;
+	  default:
+	    /* If we have a multibyte sequence, copy it until we
+	       reach its end, replacing each non-printable multibyte
+	       character with a single question mark.  */
+	    {
+	      mbstate_t mbstate;
+	      memset (&mbstate, 0, sizeof mbstate);
+	      do
+		{
+		  wchar_t wc;
+		  size_t bytes;
+		  int w;
+
+		  bytes = mbrtowc (&wc, p, plimit - p, &mbstate);
+
+		  if (bytes == (size_t) -1)
+		    {
+		      /* An invalid multibyte sequence was
+			 encountered.  Skip one input byte, and
+			 put a question mark.  */
+		      p++;
+		      *q++ = '?';
+		      break;
+		    }
+
+		  if (bytes == (size_t) -2)
+		    {
+		      /* An incomplete multibyte character
+			 at the end.  Replace it entirely with
+			 a question mark.  */
+		      p = plimit;
+		      *q++ = '?';
+		      break;
+		    }
+
+		  if (bytes == 0)
+		    /* A null wide character was encountered.  */
+		    bytes = 1;
+
+		  w = wcwidth (wc);
+		  if (w >= 0)
+		    {
+		      /* A printable multibyte character.
+			 Keep it.  */
+		      for (; bytes > 0; --bytes)
+			*q++ = *p++;
+		    }
+		  else
+		    {
+		      /* An unprintable multibyte character.
+			 Replace it entirely with a question
+			 mark.  */
+		      p += bytes;
+		      *q++ = '?';
+		    }
+		}
+	      while (! mbsinit (&mbstate));
+	    }
+	    break;
+	  }
+
+      /* The buffer may have shrunk.  */
+      len = q - buf;
+      return len;
+    }
+#endif
+  {
+    char *p = buf;
+    char const *plimit = buf + len;
+    
+    while (p < plimit)
+      {
+	if (! ISPRINT (to_uchar (*p)))
+	  *p = '?';
+	p++;
+      }
+  }
+}
+
+
+
+/*
+ * Print S according to the format FORMAT, but if the destination is a tty, 
+ * convert any potentially-dangerous characters.  The logic in this function 
+ * was taken from ls.c in coreutils (at Sun Jun  5 20:42:51 2005 UTC).
+ */
+static void
+print_quoted (FILE *fp,
+	      const struct quoting_options *qopts,
+	      boolean dest_is_tty,
+	      const char *format,
+	      const char *s)
+{
+  if (dest_is_tty)
+    {
+      char smallbuf[BUFSIZ];
+      size_t len = quotearg_buffer (smallbuf, sizeof smallbuf, s, -1, qopts);
+      char *buf;
+      if (len < sizeof smallbuf)
+	buf = smallbuf;
+      else
+	{
+	  /* The original coreutils code uses alloca(), but I don't
+	   * want to take on the anguish of introducing alloca() to
+	   * 'find'.
+	   */
+	  buf = xmalloc (len + 1);
+	  quotearg_buffer (buf, len + 1, s, -1, qopts);
+	}
+
+      /* Replace any remaining funny characters with '?'. */
+      len = qmark_chars(buf, len);
+      
+      fprintf(fp, format, buf);	/* Print the quoted version */
+      if (buf != smallbuf)
+	{
+	  free(buf);
+	  buf = NULL;
+	}
+    }
+  else
+    {
+      /* no need to quote things. */
+      fprintf(fp, format, s);
+    }
+  
+}
+
+
 boolean
 pred_fprintf (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
 {
   FILE *fp = pred_ptr->args.printf_vec.stream;
+  const struct quoting_options *qopts = pred_ptr->args.printf_vec.quote_opts;
+  boolean ttyflag = pred_ptr->args.printf_vec.dest_is_tty;
   struct segment *segment;
   char *cp;
   char hbuf[LONGEST_HUMAN_READABLE + 1];
@@ -576,6 +815,11 @@ pred_fprintf (char *pathname, struct sta
 	    default:
 	      abort ();
 	    }
+	  /* We trust the output of format_date not to contain 
+	   * nasty characters, though the value of the date
+	   * is itself untrusted data.
+	   */
+	  /* trusted */
 	  fprintf (fp, segment->text,
 		   format_date (t, (segment->kind >> 8) & 0xff));
 	  continue;
@@ -584,39 +828,52 @@ pred_fprintf (char *pathname, struct sta
       switch (segment->kind)
 	{
 	case KIND_PLAIN:	/* Plain text string (no % conversion). */
+	  /* trusted */
 	  fwrite (segment->text, 1, segment->text_len, fp);
 	  break;
 	case KIND_STOP:		/* Terminate argument and flush output. */
+	  /* trusted */
 	  fwrite (segment->text, 1, segment->text_len, fp);
 	  fflush (fp);
 	  return (true);
 	case 'a':		/* atime in `ctime' format. */
+	  /* UNTRUSTED, probably unexploitable */
 	  fprintf (fp, segment->text, ctime_format (stat_buf->st_atime));
 	  break;
 	case 'b':		/* size in 512-byte blocks */
+	  /* UNTRUSTED, probably unexploitable */
 	  fprintf (fp, segment->text,
 		   human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf),
 				   hbuf, human_ceiling,
 				   ST_NBLOCKSIZE, 512));
 	  break;
 	case 'c':		/* ctime in `ctime' format */
+	  /* UNTRUSTED, probably unexploitable */
 	  fprintf (fp, segment->text, ctime_format (stat_buf->st_ctime));
 	  break;
 	case 'd':		/* depth in search tree */
+	  /* UNTRUSTED, probably unexploitable */
 	  fprintf (fp, segment->text, state.curdepth);
 	  break;
 	case 'D':		/* Device on which file exists (stat.st_dev) */
+	  /* trusted */
 	  fprintf (fp, segment->text, 
 		   human_readable ((uintmax_t) stat_buf->st_dev, hbuf,
 				   human_ceiling, 1, 1));
 	  break;
 	case 'f':		/* base name of path */
-	  fprintf (fp, segment->text, base_name (pathname));
+	  /* sanitised */
+	  print_quoted (fp, qopts, ttyflag, segment->text, base_name (pathname));
 	  break;
 	case 'F':		/* filesystem type */
-	  fprintf (fp, segment->text, filesystem_type (stat_buf));
+	  /* trusted */
+	  print_quoted (fp, qopts, ttyflag, segment->text, filesystem_type (stat_buf));
 	  break;
 	case 'g':		/* group name */
+	  /* trusted */
+	  /* (well, the actual group is selected by the user but
+	   * its name was selected by the system administrator)
+	   */
 	  {
 	    struct group *g;
 
@@ -630,11 +887,13 @@ pred_fprintf (char *pathname, struct sta
 	    /* else fallthru */
 	  }
 	case 'G':		/* GID number */
+	  /* UNTRUSTED, probably unexploitable */
 	  fprintf (fp, segment->text,
 		   human_readable ((uintmax_t) stat_buf->st_gid, hbuf,
 				   human_ceiling, 1, 1));
 	  break;
 	case 'h':		/* leading directories part of path */
+	  /* sanitised */
 	  {
 	    char cc;
 	    
@@ -645,18 +904,19 @@ pred_fprintf (char *pathname, struct sta
 		 * print the string because it contains characters
 		 * other than just '%s'.  The %h expands to ".".
 		 */
-		fprintf (fp, segment->text, ".");
+		print_quoted (fp, qopts, ttyflag, segment->text, ".");
 	      }
 	    else
 	      {
 		cc = *cp;
 		*cp = '\0';
-		fprintf (fp, segment->text, pathname);
+		print_quoted (fp, qopts, ttyflag, segment->text, pathname);
 		*cp = cc;
 	      }
 	    break;
 	  }
 	case 'H':		/* ARGV element file was found under */
+	  /* trusted */
 	  {
 	    char cc = pathname[state.path_length];
 
@@ -666,18 +926,21 @@ pred_fprintf (char *pathname, struct sta
 	    break;
 	  }
 	case 'i':		/* inode number */
+	  /* UNTRUSTED, but not exploitable I think */
 	  fprintf (fp, segment->text,
-		   human_readable ((uintmax_t) stat_buf->st_ino, hbuf,
-				   human_ceiling,
-				   1, 1));
+			human_readable ((uintmax_t) stat_buf->st_ino, hbuf,
+					human_ceiling,
+					1, 1));
 	  break;
 	case 'k':		/* size in 1K blocks */
+	  /* UNTRUSTED, but not exploitable I think */
 	  fprintf (fp, segment->text,
 		   human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf),
 				   hbuf, human_ceiling,
 				   ST_NBLOCKSIZE, 1024)); 
 	  break;
 	case 'l':		/* object of symlink */
+	  /* sanitised */
 #ifdef S_ISLNK
 	  {
 	    char *linkname = 0;
@@ -690,16 +953,17 @@ pred_fprintf (char *pathname, struct sta
 	      }
 	    if (linkname)
 	      {
-		fprintf (fp, segment->text, linkname);
+		print_quoted (fp, qopts, ttyflag, segment->text, linkname);
 		free (linkname);
 	      }
 	    else
-	      fprintf (fp, segment->text, "");
+	      print_quoted (fp, qopts, ttyflag, segment->text, "");
 	  }
 #endif				/* S_ISLNK */
 	  break;
 	  
 	case 'M':		/* mode as 10 chars (eg., "-rwxr-x--x" */
+	  /* UNTRUSTED, probably unexploitable */
 	  {
 	    char modestring[16] ;
 	    mode_string (stat_buf->st_mode, modestring);
@@ -709,6 +973,7 @@ pred_fprintf (char *pathname, struct sta
 	  break;
 	  
 	case 'm':		/* mode as octal number (perms only) */
+	  /* UNTRUSTED, probably unexploitable */
 	  {
 	    /* Output the mode portably using the traditional numbers,
 	       even if the host unwisely uses some other numbering
@@ -739,6 +1004,7 @@ pred_fprintf (char *pathname, struct sta
 	  break;
 	  
 	case 'n':		/* number of links */
+	  /* UNTRUSTED, probably unexploitable */
 	  fprintf (fp, segment->text,
 		   human_readable ((uintmax_t) stat_buf->st_nlink,
 				   hbuf,
@@ -746,9 +1012,11 @@ pred_fprintf (char *pathname, struct sta
 				   1, 1));
 	  break;
 	case 'p':		/* pathname */
-	  fprintf (fp, segment->text, pathname);
+	  /* sanitised */
+	  print_quoted (fp, qopts, ttyflag, segment->text, pathname);
 	  break;
 	case 'P':		/* pathname with ARGV element stripped */
+	  /* sanitised */
 	  if (state.curdepth > 0)
 	    {
 	      cp = pathname + state.path_length;
@@ -761,17 +1029,24 @@ pred_fprintf (char *pathname, struct sta
 	    }
 	  else
 	    cp = "";
-	  fprintf (fp, segment->text, cp);
+	  print_quoted (fp, qopts, ttyflag, segment->text, cp);
 	  break;
 	case 's':		/* size in bytes */
+	  /* UNTRUSTED, probably unexploitable */
 	  fprintf (fp, segment->text,
 		   human_readable ((uintmax_t) stat_buf->st_size,
 				   hbuf, human_ceiling, 1, 1));
 	  break;
 	case 't':		/* mtime in `ctime' format */
+	  /* UNTRUSTED, probably unexploitable */
 	  fprintf (fp, segment->text, ctime_format (stat_buf->st_mtime));
 	  break;
 	case 'u':		/* user name */
+	  /* trusted */
+	  /* (well, the actual user is selected by the user on systems
+	   * where chown is not restricted, but the user name was
+	   * selected by the system administrator)
+	   */
 	  {
 	    struct passwd *p;
 
@@ -786,6 +1061,7 @@ pred_fprintf (char *pathname, struct sta
 	  }
 	  
 	case 'U':		/* UID number */
+	  /* UNTRUSTED, probably unexploitable */
 	  fprintf (fp, segment->text,
 		   human_readable ((uintmax_t) stat_buf->st_uid, hbuf,
 				   human_ceiling, 1, 1));
@@ -793,6 +1069,7 @@ pred_fprintf (char *pathname, struct sta
 
 	/* type of filesystem entry like `ls -l`: (d,-,l,s,p,b,c,n) n=nonexistent(symlink) */
 	case 'Y':		/* in case of symlink */
+	  /* trusted */
 	  {
 #ifdef S_ISLNK
 	  if (S_ISLNK (stat_buf->st_mode))
@@ -822,6 +1099,7 @@ pred_fprintf (char *pathname, struct sta
 	  }
 	  /* FALLTHROUGH */
 	case 'y':
+	  /* trusted */
 	  {
 	    mode_t m = stat_buf->st_mode & S_IFMT;
 
@@ -1096,6 +1374,7 @@ is_ok(const char *program, const char *a
      The exact format is not specified.
      This standard does not have requirements for locales other than POSIX
   */
+  /* XXX: printing UNTRUSTED data here. */
   fprintf (stderr, _("< %s ... %s > ? "), program, arg);
   fflush (stderr);
   return yesno();
@@ -1184,7 +1463,11 @@ pred_print (char *pathname, struct stat 
 {
   (void) stat_buf;
   (void) pred_ptr;
-  puts (pathname);
+  /* puts (pathname); */
+  print_quoted(pred_ptr->args.printf_vec.stream,
+	       pred_ptr->args.printf_vec.quote_opts,
+	       pred_ptr->args.printf_vec.dest_is_tty,
+	       "%s\n", pathname);
   return true;
 }
 
