Fixed failing hunks.
[rsync/rsync-patches.git] / atimes.diff
1 To use this patch, run these commands for a successful build:
2
3     patch -p1 <patches/atimes.diff
4     ./prepare-source
5     ./configure                      (optional if already run)
6     make
7
8
9 --- old/compat.c
10 +++ new/compat.c
11 @@ -62,6 +62,8 @@ void setup_protocol(int f_out,int f_in)
12                 preserve_uid = ++file_extra_cnt;
13         if (preserve_gid)
14                 preserve_gid = ++file_extra_cnt;
15 +       if (preserve_atimes)
16 +               preserve_atimes = ++file_extra_cnt;
17         if (preserve_acls && !am_sender)
18                 preserve_acls = ++file_extra_cnt;
19  
20 --- old/flist.c
21 +++ new/flist.c
22 @@ -48,6 +48,7 @@ extern int preserve_devices;
23  extern int preserve_specials;
24  extern int preserve_uid;
25  extern int preserve_gid;
26 +extern int preserve_atimes;
27  extern int relative_paths;
28  extern int implied_dirs;
29  extern int file_extra_cnt;
30 @@ -143,6 +144,7 @@ void show_flist_stats(void)
31  static void list_file_entry(struct file_struct *f)
32  {
33         char permbuf[PERMSTRING_SIZE];
34 +       time_t atime = preserve_atimes ? F_ATIME(f) : 0;
35         double len;
36  
37         if (!F_IS_ACTIVE(f)) {
38 @@ -157,14 +159,16 @@ static void list_file_entry(struct file_
39  
40  #ifdef SUPPORT_LINKS
41         if (preserve_links && S_ISLNK(f->mode)) {
42 -               rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
43 +               rprintf(FINFO, "%s %11.0f %s %s %s -> %s\n",
44                         permbuf, len, timestring(f->modtime),
45 +                       preserve_atimes ? timestring(atime) : "",
46                         f_name(f, NULL), F_SYMLINK(f));
47         } else
48  #endif
49         {
50 -               rprintf(FINFO, "%s %11.0f %s %s\n",
51 +               rprintf(FINFO, "%s %11.0f %s %s %s\n",
52                         permbuf, len, timestring(f->modtime),
53 +                       preserve_atimes ? timestring(atime) : "",
54                         f_name(f, NULL));
55         }
56  }
57 @@ -348,6 +352,7 @@ int push_flist_dir(const char *dir, int 
58  static void send_file_entry(int f, struct file_struct *file, int ndx)
59  {
60         static time_t modtime;
61 +       static time_t atime;
62         static mode_t mode;
63         static int64 dev;
64         static dev_t rdev;
65 @@ -415,6 +420,13 @@ static void send_file_entry(int f, struc
66                 flags |= XMIT_SAME_TIME;
67         else
68                 modtime = file->modtime;
69 +       if (preserve_atimes && !S_ISDIR(mode)) {
70 +               time_t file_atime = F_ATIME(file);
71 +               if (file_atime == atime)
72 +                       flags |= XMIT_SAME_ATIME;
73 +               else
74 +                       atime = file_atime;
75 +       }
76  
77  #ifdef SUPPORT_HARD_LINKS
78         if (tmp_dev != 0) {
79 @@ -482,6 +494,8 @@ static void send_file_entry(int f, struc
80                 write_int(f, modtime);
81         if (!(flags & XMIT_SAME_MODE))
82                 write_int(f, to_wire_mode(mode));
83 +       if (preserve_atimes && !S_ISDIR(mode) && !(flags & XMIT_SAME_ATIME))
84 +               write_int(f, atime);
85         if (preserve_uid && !(flags & XMIT_SAME_UID)) {
86                 write_abbrevint30(f, uid);
87                 if (flags & XMIT_USER_NAME_FOLLOWS) {
88 @@ -558,7 +572,7 @@ static void send_file_entry(int f, struc
89  static struct file_struct *recv_file_entry(struct file_list *flist,
90                                            int flags, int f)
91  {
92 -       static time_t modtime;
93 +       static time_t modtime, atime;
94         static mode_t mode;
95         static int64 dev;
96         static dev_t rdev;
97 @@ -655,6 +669,8 @@ static struct file_struct *recv_file_ent
98                 modtime = (time_t)read_int(f);
99         if (!(flags & XMIT_SAME_MODE))
100                 mode = from_wire_mode(read_int(f));
101 +       if (preserve_atimes && !S_ISDIR(mode) && !(flags & XMIT_SAME_ATIME))
102 +               atime = (time_t)read_int(f);
103  
104         if (chmod_modes && !S_ISLNK(mode))
105                 mode = tweak_mode(mode, chmod_modes);
106 @@ -769,6 +785,8 @@ static struct file_struct *recv_file_ent
107                 F_OWNER(file) = uid;
108         if (preserve_gid)
109                 F_GROUP(file) = gid;
110 +       if (preserve_atimes)
111 +               F_ATIME(file) = atime;
112  
113         if (basename != thisname) {
114                 file->dirname = lastdir;
115 @@ -1074,6 +1092,8 @@ struct file_struct *make_file(const char
116                 F_OWNER(file) = st.st_uid;
117         if (preserve_gid)
118                 F_GROUP(file) = st.st_gid;
119 +       if (preserve_atimes)
120 +               F_ATIME(file) = st.st_atime;
121  
122         if (basename != thisname)
123                 file->dirname = lastdir;
124 --- old/generator.c
125 +++ new/generator.c
126 @@ -44,6 +44,7 @@ extern int preserve_perms;
127  extern int preserve_uid;
128  extern int preserve_gid;
129  extern int preserve_times;
130 +extern int preserve_atimes;
131  extern int omit_dir_times;
132  extern int delete_mode;
133  extern int delete_before;
134 @@ -552,6 +553,9 @@ void itemize(const char *fname, struct f
135                   && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
136                  || (keep_time && cmp_time(file->modtime, sxp->st.st_mtime) != 0))
137                         iflags |= ITEM_REPORT_TIME;
138 +               if (preserve_atimes && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
139 +                && cmp_time(F_ATIME(file), st->st_atime) != 0)
140 +                       iflags |= ITEM_REPORT_ATIME;
141                 if (!BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS))
142                         iflags |= ITEM_REPORT_PERMS;
143                 if (preserve_uid && am_root && F_UID(file) != sxp->st.st_uid)
144 @@ -852,6 +856,8 @@ static int try_dests_reg(struct file_str
145                 if (link_dest) {
146                         if (!hard_link_one(file, fname, cmpbuf, 1))
147                                 goto try_a_copy;
148 +                       if (preserve_atimes)
149 +                               set_file_attrs(fname, file, stp, 0);
150                         if (preserve_hard_links && F_IS_HLINKED(file))
151                                 finish_hard_link(file, fname, &sxp->st, itemizing, code, j);
152                         if (itemizing && (verbose > 1 || stdout_format_has_i > 1)) {
153 --- old/log.c
154 +++ new/log.c
155 @@ -36,6 +36,7 @@ extern int msg_fd_out;
156  extern int allow_8bit_chars;
157  extern int protocol_version;
158  extern int preserve_times;
159 +extern int preserve_atimes;
160  extern int preserve_uid;
161  extern int preserve_gid;
162  extern int stdout_format_has_i;
163 @@ -624,7 +625,8 @@ static void log_formatted(enum logcode c
164                         c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
165                         c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
166                         c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
167 -                       c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
168 +                       c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.'
169 +                            : S_ISLNK(file->mode) ? 'U' : 'u';
170                         c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
171                         c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
172                         c[11] = '\0';
173 --- old/options.c
174 +++ new/options.c
175 @@ -55,6 +55,7 @@ int preserve_uid = 0;
176  int preserve_gid = 0;
177  int preserve_times = 0;
178  int omit_dir_times = 0;
179 +int preserve_atimes = 0;
180  int update_only = 0;
181  int cvs_exclude = 0;
182  int dry_run = 0;
183 @@ -315,8 +316,9 @@ void usage(enum logcode F)
184    rprintf(F,"     --devices               preserve device files (super-user only)\n");
185    rprintf(F,"     --specials              preserve special files\n");
186    rprintf(F," -D                          same as --devices --specials\n");
187 -  rprintf(F," -t, --times                 preserve times\n");
188 -  rprintf(F," -O, --omit-dir-times        omit directories when preserving times\n");
189 +  rprintf(F," -t, --times                 preserve modify times\n");
190 +  rprintf(F," -O, --omit-dir-times        omit directories when preserving modify times\n");
191 +  rprintf(F," -U, --atimes                preserve access (use) times\n");
192    rprintf(F,"     --super                 receiver attempts super-user activities\n");
193    rprintf(F," -S, --sparse                handle sparse files efficiently\n");
194    rprintf(F," -n, --dry-run               show what would have been transferred\n");
195 @@ -436,6 +438,9 @@ static struct poptOption long_options[] 
196    {"times",           't', POPT_ARG_VAL,    &preserve_times, 1, 0, 0 },
197    {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
198    {"no-t",             0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
199 +  {"atimes",          'U', POPT_ARG_VAL,    &preserve_atimes, 1, 0, 0 },
200 +  {"no-atimes",        0,  POPT_ARG_VAL,    &preserve_atimes, 0, 0, 0 },
201 +  {"no-U",             0,  POPT_ARG_VAL,    &preserve_atimes, 0, 0, 0 },
202    {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 2, 0, 0 },
203    {"modify-window",    0,  POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
204    {"super",            0,  POPT_ARG_VAL,    &am_root, 2, 0, 0 },
205 @@ -1577,6 +1582,8 @@ void server_options(char **args,int *arg
206                 argstr[x++] = 'D';
207         if (preserve_times)
208                 argstr[x++] = 't';
209 +       if (preserve_atimes)
210 +               argstr[x++] = 'U';
211         if (preserve_perms)
212                 argstr[x++] = 'p';
213         else if (preserve_executability && am_sender)
214 --- old/rsync.c
215 +++ new/rsync.c
216 @@ -34,6 +34,7 @@ extern int dry_run;
217  extern int preserve_acls;
218  extern int preserve_perms;
219  extern int preserve_executability;
220 +extern int preserve_atimes;
221  extern int preserve_times;
222  extern int omit_dir_times;
223  extern int am_root;
224 @@ -234,6 +235,7 @@ int set_file_attrs(char *fname, struct f
225         int updated = 0;
226         statx sx2;
227         int change_uid, change_gid;
228 +       time_t atime, mtime;
229         mode_t new_mode = file->mode;
230  
231         if (!sxp) {
232 @@ -261,18 +263,36 @@ int set_file_attrs(char *fname, struct f
233                 get_acl(fname, sxp);
234  #endif
235  
236 +       /* This code must be the first update in the function due to
237 +        * how it uses the "updated" variable. */
238         if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && omit_dir_times))
239                 flags |= ATTRS_SKIP_MTIME;
240 +       if (!preserve_atimes || S_ISDIR(sxp->st.st_mode))
241 +               flags |= ATTRS_SKIP_ATIME;
242         if (!(flags & ATTRS_SKIP_MTIME)
243             && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
244 -               int ret = set_modtime(fname, file->modtime, sxp->st.st_mode);
245 +               mtime = file->modtime;
246 +               updated = 1;
247 +       } else
248 +               mtime = sxp->st.st_mtime;
249 +       if (!(flags & ATTRS_SKIP_ATIME)) {
250 +               time_t file_atime = F_ATIME(file);
251 +               if (cmp_time(sxp->st.st_atime, file_atime) != 0) {
252 +                       atime = file_atime;
253 +                       updated = 1;
254 +               } else
255 +                       atime = sxp->st.st_atime;
256 +       } else
257 +               atime = sxp->st.st_atime;
258 +       if (updated) {
259 +               int ret = set_times(fname, mtime, atime, sxp->st.st_mode);
260                 if (ret < 0) {
261                         rsyserr(FERROR, errno, "failed to set times on %s",
262                                 full_fname(fname));
263                         goto cleanup;
264                 }
265 -               if (ret == 0) /* ret == 1 if symlink could not be set */
266 -                       updated = 1;
267 +               if (ret > 0) /* ret == 1 if symlink could not be set */
268 +                       updated = 0;
269         }
270  
271         change_uid = am_root && preserve_uid && sxp->st.st_uid != F_UID(file);
272 --- old/rsync.h
273 +++ new/rsync.h
274 @@ -56,6 +56,7 @@
275  #define XMIT_RDEV_MINOR_IS_SMALL (1<<11)
276  #define XMIT_USER_NAME_FOLLOWS (1<<12) /* protocols >= 30 */
277  #define XMIT_GROUP_NAME_FOLLOWS (1<<13) /* protocols >= 30 */
278 +#define XMIT_SAME_ATIME (1<<14)                /* protocols >= 30 */
279  
280  /* These flags are used in the live flist data. */
281  
282 @@ -136,6 +137,7 @@
283  
284  #define ATTRS_REPORT           (1<<0)
285  #define ATTRS_SKIP_MTIME       (1<<1)
286 +#define ATTRS_SKIP_ATIME       (1<<2)
287  
288  #define FULL_FLUSH     1
289  #define NORMAL_FLUSH   0
290 @@ -571,6 +573,7 @@ extern int file_extra_cnt;
291  extern int preserve_uid;
292  extern int preserve_gid;
293  extern int preserve_acls;
294 +extern int preserve_atimes;
295  
296  #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
297  #define EXTRA_LEN (sizeof (union file_extras))
298 @@ -603,6 +606,7 @@ extern int preserve_acls;
299  /* When the associated option is on, all entries will have these present: */
300  #define F_OWNER(f) REQ_EXTRA(f, preserve_uid)->unum
301  #define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum
302 +#define F_ATIME(f) REQ_EXTRA(f, preserve_atimes)->unum
303  #define F_ACL(f) REQ_EXTRA(f, preserve_acls)->unum
304  
305  /* These items are per-entry optional and mutally exclusive: */
306 --- old/rsync.yo
307 +++ new/rsync.yo
308 @@ -329,8 +329,9 @@ to the detailed description below for a 
309       --devices               preserve device files (super-user only)
310       --specials              preserve special files
311   -D                          same as --devices --specials
312 - -t, --times                 preserve times
313 - -O, --omit-dir-times        omit directories when preserving times
314 + -t, --times                 preserve modify times
315 + -O, --omit-dir-times        omit directories when preserving mod-times
316 + -U, --atimes                preserve access (use) times
317       --super                 receiver attempts super-user activities
318   -S, --sparse                handle sparse files efficiently
319   -n, --dry-run               show what would have been transferred
320 @@ -896,6 +897,12 @@ it is preserving modification times (see
321  the directories on the receiving side, it is a good idea to use bf(-O).
322  This option is inferred if you use bf(--backup) without bf(--backup-dir).
323  
324 +dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the
325 +destination files to the same value as the source files.  Note that the
326 +reading of the source file may update the atime of the source files, so
327 +repeated rsync runs with --atimes may be needed if you want to force the
328 +access-time values to be 100% identical on the two systems.
329 +
330  dit(bf(--super)) This tells the receiving side to attempt super-user
331  activities even if the receiving rsync wasn't run by the super-user.  These
332  activities include: preserving users via the bf(--owner) option, preserving
333 @@ -1482,7 +1489,7 @@ quote(itemization(
334    by the file transfer.
335    it() A bf(t) means the modification time is different and is being updated
336    to the sender's value (requires bf(--times)).  An alternate value of bf(T)
337 -  means that the time will be set to the transfer time, which happens
338 +  means that the modify time will be set to the transfer time, which happens
339    anytime a symlink is transferred, or when a file or device is transferred
340    without bf(--times).
341    it() A bf(p) means the permissions are different and are being updated to
342 @@ -1491,8 +1498,10 @@ quote(itemization(
343    sender's value (requires bf(--owner) and super-user privileges).
344    it() A bf(g) means the group is different and is being updated to the
345    sender's value (requires bf(--group) and the authority to set the group).
346 -  it() The bf(u) slot is reserved for reporting update (access) time changes
347 -  (a feature that is not yet released).
348 +  it() A bf(u) means the access (use) time is different and is being updated to
349 +  the sender's value (requires bf(--atimes)).  An alternate value of bf(U)
350 +  means that the access time will be set to the transfer time, which happens
351 +  when a symlink or directory is updated.
352    it() The bf(a) means that the ACL information changed.
353    it() The bf(x) slot is reserved for reporting extended attribute changes
354    (a feature that is not yet released).
355 --- old/sender.c
356 +++ new/sender.c
357 @@ -41,6 +41,7 @@ extern int do_progress;
358  extern int inplace;
359  extern int batch_fd;
360  extern int write_batch;
361 +extern unsigned int file_struct_len;
362  extern struct stats stats;
363  extern struct file_list *cur_flist, *first_flist;
364  
365 --- old/testsuite/atimes.test
366 +++ new/testsuite/atimes.test
367 @@ -0,0 +1,19 @@
368 +#! /bin/sh
369 +
370 +# Test rsync copying atimes
371 +
372 +. "$suitedir/rsync.fns"
373 +
374 +set -x
375 +
376 +mkdir "$fromdir"
377 +
378 +touch "$fromdir/foo"
379 +touch -a -t 200102031717.42 "$fromdir/foo"
380 +
381 +TLS_ARGS=--atime
382 +
383 +checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
384 +
385 +# The script would have aborted on error, so getting here means we've won.
386 +exit 0
387 --- old/testsuite/rsync.fns
388 +++ new/testsuite/rsync.fns
389 @@ -66,7 +66,7 @@ printmsg() {
390  }
391  
392  rsync_ls_lR() {
393 -    find "$@" -print | sort | sed 's/ /\\ /g' | xargs "$TOOLDIR/tls"
394 +    find "$@" -print | sort | sed 's/ /\\ /g' | xargs "$TOOLDIR/tls" $TLS_ARGS
395  }
396  
397  check_perms() {
398 @@ -184,6 +184,10 @@ checkit() {
399      # We can just write everything to stdout/stderr, because the
400      # wrapper hides it unless there is a problem.
401  
402 +    if test x$TLS_ARGS = x--atime; then
403 +       ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
404 +    fi
405 +
406      echo "Running: \"$1\""  
407      eval "$1" 
408      status=$?
409 @@ -191,10 +195,13 @@ checkit() {
410         failed="YES";
411      fi
412  
413 +    if test x$TLS_ARGS != x--atime; then
414 +       ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
415 +    fi
416 +
417      echo "-------------"
418      echo "check how the directory listings compare with diff:"
419      echo ""
420 -    ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
421      ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
422      diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed=YES
423  
424 --- old/tls.c
425 +++ new/tls.c
426 @@ -34,6 +34,7 @@
427   * change. */
428  
429  #include "rsync.h"
430 +#include "popt.h"
431  
432  #define PROGRAM "tls"
433  
434 @@ -43,6 +44,8 @@ int read_only = 1;
435  int list_only = 0;
436  int preserve_perms = 0;
437  
438 +static int display_atime = 0;
439
440  static void failed(char const *what, char const *where)
441  {
442         fprintf(stderr, PROGRAM ": %s %s: %s\n",
443 @@ -50,12 +53,29 @@ static void failed(char const *what, cha
444         exit(1);
445  }
446  
447 +static void storetime(char *dest, time_t t, size_t destsize)
448 +{
449 +       if (t) {
450 +               struct tm *mt = gmtime(&t);
451 +
452 +               snprintf(dest, destsize,
453 +                       "%04d-%02d-%02d %02d:%02d:%02d ",
454 +                       (int)mt->tm_year + 1900,
455 +                       (int)mt->tm_mon + 1,
456 +                       (int)mt->tm_mday,
457 +                       (int)mt->tm_hour,
458 +                       (int)mt->tm_min,
459 +                       (int)mt->tm_sec);
460 +       } else
461 +               strlcpy(dest, "                    ", destsize);
462 +}
463 +
464  static void list_file(const char *fname)
465  {
466         STRUCT_STAT buf;
467         char permbuf[PERMSTRING_SIZE];
468 -       struct tm *mt;
469 -       char datebuf[50];
470 +       char mtimebuf[50];
471 +       char atimebuf[50];
472         char linkbuf[4096];
473  
474         if (do_lstat(fname, &buf) < 0)
475 @@ -88,19 +108,8 @@ static void list_file(const char *fname)
476  
477         permstring(permbuf, buf.st_mode);
478  
479 -       if (buf.st_mtime) {
480 -               mt = gmtime(&buf.st_mtime);
481 -
482 -               snprintf(datebuf, sizeof datebuf,
483 -                       "%04d-%02d-%02d %02d:%02d:%02d",
484 -                       (int)mt->tm_year + 1900,
485 -                       (int)mt->tm_mon + 1,
486 -                       (int)mt->tm_mday,
487 -                       (int)mt->tm_hour,
488 -                       (int)mt->tm_min,
489 -                       (int)mt->tm_sec);
490 -       } else
491 -               strlcpy(datebuf, "                   ", sizeof datebuf);
492 +       storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
493 +       storetime(atimebuf, buf.st_atime, sizeof atimebuf);
494  
495         /* TODO: Perhaps escape special characters in fname? */
496  
497 @@ -111,23 +120,55 @@ static void list_file(const char *fname)
498                     (long)minor(buf.st_rdev));
499         } else /* NB: use double for size since it might not fit in a long. */
500                 printf("%12.0f", (double)buf.st_size);
501 -       printf(" %6ld.%-6ld %6ld %s %s%s\n",
502 +       printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
503                (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
504 -              datebuf, fname, linkbuf);
505 +              mtimebuf, display_atime && !S_ISDIR(buf.st_mode) ? atimebuf : "",
506 +              fname, linkbuf);
507 +}
508 +
509 +static struct poptOption long_options[] = {
510 +  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
511 +  {"atime",           'u', POPT_ARG_NONE,   &display_atime, 0,   0, 0},
512 +  {"help",            'h', POPT_ARG_NONE,   0,              'h', 0, 0},
513 +  {0,0,0,0,0,0,0}
514 +};
515 +
516 +static void tls_usage(int ret)
517 +{
518 +       fprintf(stderr, "usage: " PROGRAM " [--atime | -u] FILE ...\n"
519 +           "Trivial file listing program for portably checking rsync\n");
520 +       exit(ret);
521  }
522  
523  int
524  main(int argc, char *argv[])
525  {
526 -       if (argc < 2) {
527 -               fprintf(stderr, "usage: " PROGRAM " FILE ...\n"
528 -                       "Trivial file listing program for portably checking rsync\n");
529 -               return 1;
530 -       }
531 +       poptContext pc;
532 +       const char **extra_args;
533 +       int opt;
534  
535 -       for (argv++; *argv; argv++) {
536 -               list_file(*argv);
537 +       pc = poptGetContext(PROGRAM, argc, (const char **)argv,
538 +                           long_options, 0);
539 +       while ((opt = poptGetNextOpt(pc)) != -1) {
540 +               switch (opt) {
541 +               case 'h':
542 +                       tls_usage(0);
543 +               default:
544 +                       fprintf(stderr,
545 +                               "%s: %s\n",
546 +                               poptBadOption(pc, POPT_BADOPTION_NOALIAS),
547 +                               poptStrerror(opt));
548 +                       tls_usage(1);
549 +               }
550         }
551  
552 +       extra_args = poptGetArgs(pc);
553 +       if (*extra_args == NULL)
554 +               tls_usage(1);
555 +
556 +       for (; *extra_args; extra_args++)
557 +               list_file(*extra_args);
558 +       poptFreeContext(pc);
559 +
560         return 0;
561  }
562 --- old/util.c
563 +++ new/util.c
564 @@ -120,7 +120,7 @@ NORETURN void overflow_exit(const char *
565         exit_cleanup(RERR_MALLOC);
566  }
567  
568 -int set_modtime(const char *fname, time_t modtime, mode_t mode)
569 +int set_times(const char *fname, time_t modtime, time_t atime, mode_t mode)
570  {
571  #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
572         if (S_ISLNK(mode))
573 @@ -128,9 +128,13 @@ int set_modtime(const char *fname, time_
574  #endif
575  
576         if (verbose > 2) {
577 -               rprintf(FINFO, "set modtime of %s to (%ld) %s",
578 +               char mtimebuf[200];
579 +
580 +               strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
581 +               rprintf(FINFO,
582 +                       "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
583                         fname, (long)modtime,
584 -                       asctime(localtime(&modtime)));
585 +                       mtimebuf, (long)atime, timestring(atime));
586         }
587  
588         if (dry_run)
589 @@ -139,7 +143,7 @@ int set_modtime(const char *fname, time_
590         {
591  #ifdef HAVE_UTIMES
592                 struct timeval t[2];
593 -               t[0].tv_sec = time(NULL);
594 +               t[0].tv_sec = atime;
595                 t[0].tv_usec = 0;
596                 t[1].tv_sec = modtime;
597                 t[1].tv_usec = 0;
598 @@ -152,12 +156,12 @@ int set_modtime(const char *fname, time_
599                 return utimes(fname, t);
600  #elif defined HAVE_UTIMBUF
601                 struct utimbuf tbuf;
602 -               tbuf.actime = time(NULL);
603 +               tbuf.actime = atime;
604                 tbuf.modtime = modtime;
605                 return utime(fname,&tbuf);
606  #elif defined HAVE_UTIME
607                 time_t t[2];
608 -               t[0] = time(NULL);
609 +               t[0] = atime;
610                 t[1] = modtime;
611                 return utime(fname,t);
612  #else