Don't call do_lstat() unless SUPPORT_LINKS is defined.
[rsync/rsync.git] / tls.c
1 /* -*- c-file-style: "linux" -*-
2  *
3  * Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License version
7  * 2 as published by the Free Software Foundation.
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, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /**
20  * @file tls.c
21  *
22  * Trivial @c ls for comparing two directories after running an rsync.
23  *
24  * The problem with using the system's own ls is that some features
25  * have little quirks that make directories look different when for
26  * our purposes they're the same -- for example, the BSD braindamage
27  * about setting the mode on symlinks based on your current umask.
28  *
29  * All the filenames must be given on the command line -- tls does not
30  * even read directories, let alone recurse.  The typical usage is
31  * "find|sort|xargs tls".
32  *
33  * The format is not exactly the same as any particular Unix ls(1).
34  *
35  * A key requirement for this program is that the output be "very
36  * reproducible."  So we mask away information that can accidentally
37  * change.
38  **/
39
40
41 #include "rsync.h"
42
43 #define PROGRAM "tls"
44
45 /* These are to make syscall.o shut up. */
46 int dry_run = 0;
47 int read_only = 1;
48 int list_only = 0;
49 int preserve_perms = 0;
50
51
52 static void failed(char const *what, char const *where)
53 {
54         fprintf(stderr, PROGRAM ": %s %s: %s\n",
55                 what, where, strerror(errno));
56         exit(1);
57 }
58
59
60
61 static void list_file(const char *fname)
62 {
63         STRUCT_STAT buf;
64         char permbuf[PERMSTRING_SIZE];
65         struct tm *mt;
66         char datebuf[50];
67         char linkbuf[4096];
68         int ret;
69
70 #if SUPPORT_LINKS
71         ret = do_lstat(fname, &buf);
72 #else
73         ret = do_stat(fname, &buf);
74 #endif
75         if (ret < 0)
76                 failed("stat", fname);
77
78         /* The size of anything but a regular file is probably not
79          * worth thinking about. */
80         if (!S_ISREG(buf.st_mode))
81                 buf.st_size = 0;
82
83         /* On some BSD platforms the mode bits of a symlink are
84          * undefined.  Also it tends not to be possible to reset a
85          * symlink's mtime, so we have to ignore it too. */
86         if (S_ISLNK(buf.st_mode)) {
87                 int len;
88                 buf.st_mode &= ~0777;
89                 buf.st_mtime = (time_t)0;
90                 buf.st_uid = buf.st_gid = 0;
91                 strcpy(linkbuf, " -> ");
92                 /* const-cast required for silly UNICOS headers */
93                 len = readlink((char *) fname, linkbuf+4, sizeof(linkbuf) - 4);
94                 if (len == -1)
95                         failed("readlink", fname);
96                 else
97                         /* it's not nul-terminated */
98                         linkbuf[4+len] = 0;
99         } else {
100                 linkbuf[0] = 0;
101         }
102
103         permstring(permbuf, buf.st_mode);
104
105         if (buf.st_mtime) {
106                 mt = gmtime(&buf.st_mtime);
107
108                 sprintf(datebuf, "%04d-%02d-%02d %02d:%02d:%02d",
109                         mt->tm_year + 1900,
110                         mt->tm_mon + 1,
111                         mt->tm_mday,
112                         mt->tm_hour,
113                         mt->tm_min,
114                         mt->tm_sec);
115         } else {
116                 strcpy(datebuf, "                   ");
117         }
118
119         /* TODO: Perhaps escape special characters in fname? */
120
121         printf("%s ", permbuf);
122         if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) {
123                 printf("%5ld,%6ld",
124                     (long)major(buf.st_rdev),
125                     (long)minor(buf.st_rdev));
126         } else /* NB: use double for size since it might not fit in a long. */
127                 printf("%12.0f", (double)buf.st_size);
128         printf(" %6ld.%-6ld %6ld %s %s%s\n",
129                (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
130                datebuf, fname, linkbuf);
131 }
132
133
134 int
135 main(int argc, char *argv[])
136 {
137         if (argc < 2) {
138                 fprintf(stderr, "usage: " PROGRAM " DIR ...\n"
139                         "Trivial file listing program for portably checking rsync\n");
140                 return 1;
141         }
142
143         for (argv++; *argv; argv++) {
144                 list_file(*argv);
145         }
146
147         return 0;
148 }