Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F151310675
D51776.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
D51776.id.diff
View Options
diff --git a/usr.bin/find/find.1 b/usr.bin/find/find.1
--- a/usr.bin/find/find.1
+++ b/usr.bin/find/find.1
@@ -1144,9 +1144,18 @@
.It n
Number of hard links.
.It y
-Unimplemented -- Type of the file
+A single character representing the type of the file.
.It Y
-Unimplemented -- Type of the file with loop detection
+A single character representing the type of the file.
+If the file is a symbolic link, show information for the target of the
+link instead, or
+.Sq L
+if the link loops,
+.Sq N
+if the target does not exist, or
+.Sq ?
+if any other error occurs while attempting to determine the type of
+the target.
.It a
Access time of the file.
.It A
diff --git a/usr.bin/find/printf.c b/usr.bin/find/printf.c
--- a/usr.bin/find/printf.c
+++ b/usr.bin/find/printf.c
@@ -5,15 +5,18 @@
*/
#include <sys/types.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+
#include <err.h>
+#include <errno.h>
#include <fts.h>
#include <grp.h>
#include <pwd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <time.h>
+#include <unistd.h>
#include "find.h"
@@ -32,7 +35,7 @@
return (c >= 'a' && c <= 'v' && esc[c - 'a'] != c);
}
-static const char *
+static char *
escape(const char *str, bool *flush, bool *warned)
{
char c;
@@ -147,7 +150,6 @@
gmtime_r(&t, &tm);
fmt[1] = mod;
- printf("fmt is '%s'\n", fmt);
if (strftime(buffer, sizeof(buffer), fmt, &tm) == 0)
errx(1, "Format bad or data too long for buffer"); /* Can't really happen ??? */
fputs(buffer, fp);
@@ -156,36 +158,36 @@
void
do_printf(PLAN *plan, FTSENT *entry, FILE *fout)
{
- const char *fmt, *path, *pend, *all;
- char c;
- FILE *fp;
+ char buf[4096];
+ struct stat sb;
+ struct stat *sp;
+ const char *path, *pend;
+ char *all, *fmt;
+ ssize_t ret;
+ int c;
bool flush, warned;
- struct stat *sb;
- char *tmp;
- size_t tmplen;
- fp = open_memstream(&tmp, &tmplen);
warned = (plan->flags & F_HAS_WARNED) != 0;
all = fmt = escape(plan->c_data, &flush, &warned);
if (warned)
plan->flags |= F_HAS_WARNED;
- sb = entry->fts_statp;
for (c = *fmt++; c; c = *fmt++) {
+ sp = entry->fts_statp;
if (c != '%') {
- putc(c, fp);
+ putc(c, fout);
continue;
}
c = *fmt++;
/* Style(9) deviation: case order same as gnu find info doc */
switch (c) {
case '%':
- putc(c, fp);
+ putc(c, fout);
break;
case 'p': /* Path to file */
- fputs(entry->fts_path, fp);
+ fputs(entry->fts_path, fout);
break;
case 'f': /* filename w/o dirs */
- fputs(entry->fts_name, fp);
+ fputs(entry->fts_name, fout);
break;
case 'h':
/*
@@ -195,98 +197,139 @@
path = entry->fts_path;
pend = strrchr(path, '/');
if (pend == NULL)
- putc('.', fp);
- else {
- char *t = malloc(pend - path + 1);
- memcpy(t, path, pend - path);
- t[pend - path] = '\0';
- fputs(t, fp);
- free(t);
- }
+ putc('.', fout);
+ else
+ fwrite(path, pend - path, 1, fout);
break;
case 'P': /* file with command line arg rm'd -- HOW? fts_parent? */
errx(1, "%%%c is unimplemented", c);
case 'H': /* Command line arg -- HOW? */
errx(1, "%%%c is unimplemented", c);
case 'g': /* gid human readable */
- fputs(group_from_gid(sb->st_gid, 0), fp);
+ fputs(group_from_gid(sp->st_gid, 0), fout);
break;
case 'G': /* gid numeric */
- fprintf(fp, "%d", sb->st_gid);
+ fprintf(fout, "%d", sp->st_gid);
break;
case 'u': /* uid human readable */
- fputs(user_from_uid(sb->st_uid, 0), fp);
+ fputs(user_from_uid(sp->st_uid, 0), fout);
break;
case 'U': /* uid numeric */
- fprintf(fp, "%d", sb->st_uid);
+ fprintf(fout, "%d", sp->st_uid);
break;
case 'm': /* mode in octal */
- fprintf(fp, "%o", sb->st_mode & 07777);
+ fprintf(fout, "%o", sp->st_mode & 07777);
break;
- case 'M': { /* Mode in ls-standard form */
- char mode[12];
- strmode(sb->st_mode, mode);
- fputs(mode, fp);
+ case 'M': /* Mode in ls-standard form */
+ strmode(sp->st_mode, buf);
+ fwrite(buf, 10, 1, fout);
break;
- }
case 'k': /* kbytes used by file */
- fprintf(fp, "%jd", (intmax_t)sb->st_blocks / 2);
+ fprintf(fout, "%jd", (intmax_t)sp->st_blocks / 2);
break;
case 'b': /* blocks used by file */
- fprintf(fp, "%jd", (intmax_t)sb->st_blocks);
+ fprintf(fout, "%jd", (intmax_t)sp->st_blocks);
break;
case 's': /* size in bytes of file */
- fprintf(fp, "%ju", (uintmax_t)sb->st_size);
+ fprintf(fout, "%ju", (uintmax_t)sp->st_size);
break;
case 'S': /* sparseness of file */
- fprintf(fp, "%3.1f",
- (float)sb->st_blocks * 512 / (float)sb->st_size);
+ fprintf(fout, "%3.1f",
+ (float)sp->st_blocks * 512 / (float)sp->st_size);
break;
case 'd': /* Depth in tree */
- fprintf(fp, "%ld", entry->fts_level);
+ fprintf(fout, "%ld", entry->fts_level);
break;
case 'D': /* device number */
- fprintf(fp, "%ju", (uintmax_t)sb->st_dev);
+ fprintf(fout, "%ju", (uintmax_t)sp->st_dev);
break;
case 'F': /* Filesystem type */
errx(1, "%%%c is unimplemented", c);
case 'l': /* object of symbolic link */
- fprintf(fp, "%s", entry->fts_accpath);
+ ret = readlink(entry->fts_accpath, buf, sizeof(buf));
+ if (ret > 0)
+ fwrite(buf, ret, 1, fout);
break;
case 'i': /* inode # */
- fprintf(fp, "%ju", (uintmax_t)sb->st_ino);
+ fprintf(fout, "%ju", (uintmax_t)sp->st_ino);
break;
case 'n': /* number of hard links */
- fprintf(fp, "%ju", (uintmax_t)sb->st_nlink);
+ fprintf(fout, "%ju", (uintmax_t)sp->st_nlink);
break;
- case 'y': /* -type of file, incl 'l' */
- errx(1, "%%%c is unimplemented", c);
case 'Y': /* -type of file, following 'l' types L loop ? error */
- errx(1, "%%%c is unimplemented", c);
+ if (S_ISLNK(sp->st_mode)) {
+ if (stat(entry->fts_accpath, &sb) != 0) {
+ switch (errno) {
+ case ELOOP:
+ putc('L', fout);
+ break;
+ case ENOENT:
+ putc('N', fout);
+ break;
+ default:
+ putc('?', fout);
+ break;
+ }
+ break;
+ }
+ sp = &sb;
+ }
+ /* FALLTHROUGH */
+ case 'y': /* -type of file, incl 'l' */
+ switch (sp->st_mode & S_IFMT) {
+ case S_IFIFO:
+ putc('p', fout);
+ break;
+ case S_IFCHR:
+ putc('c', fout);
+ break;
+ case S_IFDIR:
+ putc('d', fout);
+ break;
+ case S_IFBLK:
+ putc('b', fout);
+ break;
+ case S_IFREG:
+ putc('f', fout);
+ break;
+ case S_IFLNK:
+ putc('l', fout);
+ break;
+ case S_IFSOCK:
+ putc('s', fout);
+ break;
+ case S_IFWHT:
+ putc('w', fout);
+ break;
+ default:
+ putc('U', fout);
+ break;
+ }
+ break;
case 'a': /* access time ctime */
- fp_ctime(fp, sb->st_atime);
+ fp_ctime(fout, sp->st_atime);
break;
case 'A': /* access time with next char strftime format */
- fp_strftime(fp, sb->st_atime, *fmt++);
+ fp_strftime(fout, sp->st_atime, *fmt++);
break;
case 'B': /* birth time with next char strftime format */
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
- if (sb->st_birthtime != 0)
- fp_strftime(fp, sb->st_birthtime, *fmt);
+ if (sp->st_birthtime != 0)
+ fp_strftime(fout, sp->st_birthtime, *fmt);
#endif
fmt++;
break; /* blank on systems that don't support it */
case 'c': /* status change time ctime */
- fp_ctime(fp, sb->st_ctime);
+ fp_ctime(fout, sp->st_ctime);
break;
case 'C': /* status change time with next char strftime format */
- fp_strftime(fp, sb->st_ctime, *fmt++);
+ fp_strftime(fout, sp->st_ctime, *fmt++);
break;
case 't': /* modification change time ctime */
- fp_ctime(fp, sb->st_mtime);
+ fp_ctime(fout, sp->st_mtime);
break;
case 'T': /* modification time with next char strftime format */
- fp_strftime(fp, sb->st_mtime, *fmt++);
+ fp_strftime(fout, sp->st_mtime, *fmt++);
break;
case 'Z': /* empty string for compat SELinux context string */
break;
@@ -299,9 +342,7 @@
errx(1, "Unknown format %c '%s'", c, all);
}
}
- fputs(tmp, fout);
if (flush)
fflush(fout);
- free(__DECONST(char *, fmt));
- free(tmp);
+ free(all);
}
diff --git a/usr.bin/find/tests/find_test.sh b/usr.bin/find/tests/find_test.sh
--- a/usr.bin/find/tests/find_test.sh
+++ b/usr.bin/find/tests/find_test.sh
@@ -1,5 +1,8 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright 2017, Conrad Meyer <cem@FreeBSD.org>.
+# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
@@ -64,8 +67,116 @@
atf_check -s exit:0 -o "inline:test/link2\n" find test -samefile test/link2
}
+atf_test_case find_printf
+find_printf_head()
+{
+ atf_set "descr" "Test the -printf primary"
+}
+find_printf_body()
+{
+ mkdir dir
+ chmod 0755 dir
+ jot -b hello 1024 >dir/file
+ chmod 0644 dir/file
+ ln -s file dir/link
+ chmod -h 0444 dir/link
+ local db=$(stat -f %b dir)
+ local fb=$(stat -f %b dir/file)
+ local lb=$(stat -f %b dir/link)
+
+ # paths
+ atf_check -o inline:"dir\ndir/file\ndir/link\n" \
+ find -s dir -printf '%p\n'
+ atf_check -o inline:"dir\nfile\nlink\n" \
+ find -s dir -printf '%f\n'
+ atf_check -o inline:".\ndir\ndir\n" \
+ find -s dir -printf '%h\n'
+ atf_check -s exit:1 -e match:"unimplemented" -o ignore \
+ find -s dir -printf '%P\n'
+ atf_check -s exit:1 -e match:"unimplemented" -o ignore \
+ find -s dir -printf '%H\n'
+
+ # group
+ atf_check -o inline:"$(stat -f %Sg dir dir/file dir/link)\n" \
+ find -s dir -printf '%g\n'
+ atf_check -o inline:"$(stat -f %g dir dir/file dir/link)\n" \
+ find -s dir -printf '%G\n'
+
+ # owner
+ atf_check -o inline:"$(stat -f %Su dir dir/file dir/link)\n" \
+ find -s dir -printf '%u\n'
+ atf_check -o inline:"$(stat -f %u dir dir/file dir/link)\n" \
+ find -s dir -printf '%U\n'
+
+ # mode
+ atf_check -o inline:"$(stat -f %Lp dir dir/file dir/link)\n" \
+ find -s dir -printf '%m\n'
+ atf_check -o inline:"$(stat -f %Sp dir dir/file dir/link)\n" \
+ find -s dir -printf '%M\n'
+
+ # size
+ atf_check -o inline:"$((db/2))\n$((fb/2))\n$((lb/2))\n" \
+ find -s dir -printf '%k\n'
+ atf_check -o inline:"$db\n$fb\n$lb\n" \
+ find -s dir -printf '%b\n'
+ atf_check -o inline:"$(stat -f %z dir dir/file dir/link)\n" \
+ find -s dir -printf '%s\n'
+ # XXX test %S properly
+ atf_check -o ignore \
+ find -s dir -printf '%S\n'
+ atf_check -o inline:"0\n1\n1\n" \
+ find -s dir -printf '%d\n'
+
+ # device
+ atf_check -o inline:"$(stat -f %d dir dir/file dir/link)\n" \
+ find -s dir -printf '%D\n'
+ atf_check -s exit:1 -e match:"unimplemented" -o ignore \
+ find -s dir -printf '%F\n'
+
+ # link target
+ atf_check -o inline:"\n\nfile\n" \
+ find -s dir -printf '%l\n'
+
+ # inode
+ atf_check -o inline:"$(stat -f %i dir dir/file dir/link)\n" \
+ find -s dir -printf '%i\n'
+
+ # nlinks
+ atf_check -o inline:"$(stat -f %l dir dir/file dir/link)\n" \
+ find -s dir -printf '%n\n'
+
+ # type
+ atf_check -o inline:"d\nf\nl\n" \
+ find -s dir -printf '%y\n'
+ atf_check -o inline:"d\nf\nf\n" \
+ find -s dir -printf '%Y\n'
+
+ # access time
+ atf_check -o inline:"$(stat -f %Sa -t '%a %b %e %T %Y' dir dir/file dir/link)\n" \
+ find -s dir -printf '%a\n'
+ atf_check -o inline:"$(stat -f %Sa -t '%e' dir dir/file dir/link)\n" \
+ find -s dir -printf '%Ae\n'
+
+ # birth time
+ atf_check -o inline:"$(stat -f %SB -t '%e' dir dir/file dir/link)\n" \
+ find -s dir -printf '%Be\n'
+
+ # inode change time
+ atf_check -o inline:"$(stat -f %Sc -t '%a %b %e %T %Y' dir dir/file dir/link)\n" \
+ find -s dir -printf '%c\n'
+ atf_check -o inline:"$(stat -f %Sc -t '%e' dir dir/file dir/link)\n" \
+ find -s dir -printf '%Ce\n'
+
+ # modification time
+ atf_check -o inline:"$(stat -f %Sm -t '%a %b %e %T %Y' dir dir/file dir/link)\n" \
+ find -s dir -printf '%t\n'
+ atf_check -o inline:"$(stat -f %Sm -t '%e' dir dir/file dir/link)\n" \
+ find -s dir -printf '%Te\n'
+}
+
atf_init_test_cases()
{
atf_add_test_case find_newer_link
atf_add_test_case find_samefile_link
+ atf_add_test_case find_printf
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Apr 8, 12:24 PM (8 h, 32 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31098508
Default Alt Text
D51776.id.diff (11 KB)
Attached To
Mode
D51776: find: Fix -printf
Attached
Detach File
Event Timeline
Log In to Comment