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/flist.c
10 +++ new/flist.c
11 @@ -46,6 +46,7 @@ extern int preserve_devices;
12  extern int preserve_specials;
13  extern int preserve_uid;
14  extern int preserve_gid;
15 +extern int preserve_atimes;
16  extern int relative_paths;
17  extern int implied_dirs;
18  extern int ignore_perishable;
19 @@ -127,6 +128,7 @@ void show_flist_stats(void)
20  static void list_file_entry(struct file_struct *f)
21  {
22         char permbuf[PERMSTRING_SIZE];
23 +       time_t atime = preserve_atimes ? IVAL(ATIME_PTR(f), 0) : 0;
24  
25         if (!f->basename) {
26                 /* this can happen if duplicate names were removed */
27 @@ -137,16 +139,18 @@ static void list_file_entry(struct file_
28  
29  #ifdef SUPPORT_LINKS
30         if (preserve_links && S_ISLNK(f->mode)) {
31 -               rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
32 +               rprintf(FINFO, "%s %11.0f %s %s %s -> %s\n",
33                         permbuf,
34                         (double)f->length, timestring(f->modtime),
35 +                       preserve_atimes ? timestring(atime) : "",
36                         f_name(f, NULL), f->u.link);
37         } else
38  #endif
39         {
40 -               rprintf(FINFO, "%s %11.0f %s %s\n",
41 +               rprintf(FINFO, "%s %11.0f %s %s %s\n",
42                         permbuf,
43                         (double)f->length, timestring(f->modtime),
44 +                       preserve_atimes ? timestring(atime) : "",
45                         f_name(f, NULL));
46         }
47  }
48 @@ -304,6 +308,7 @@ static void send_file_entry(struct file_
49  {
50         unsigned short flags;
51         static time_t modtime;
52 +       static time_t atime;
53         static mode_t mode;
54         static int64 dev;
55         static dev_t rdev;
56 @@ -319,7 +324,7 @@ static void send_file_entry(struct file_
57  
58         if (!file) {
59                 write_byte(f, 0);
60 -               modtime = 0, mode = 0;
61 +               modtime = 0, atime = 0, mode = 0;
62                 dev = 0, rdev = MAKEDEV(0, 0);
63                 rdev_major = 0;
64                 uid = 0, gid = 0;
65 @@ -365,6 +370,13 @@ static void send_file_entry(struct file_
66                 flags |= XMIT_SAME_TIME;
67         else
68                 modtime = file->modtime;
69 +       if (preserve_atimes && !S_ISDIR(mode)) {
70 +               time_t file_atime = IVAL(ATIME_PTR(file), 0);
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 (file->link_u.idev) {
79 @@ -418,6 +430,8 @@ static void send_file_entry(struct file_
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                 if (!numeric_ids)
87                         add_uid(uid);
88 @@ -484,6 +498,7 @@ static struct file_struct *receive_file_
89                                               unsigned short flags, int f)
90  {
91         static time_t modtime;
92 +       static time_t atime;
93         static mode_t mode;
94         static int64 dev;
95         static dev_t rdev;
96 @@ -497,12 +512,13 @@ static struct file_struct *receive_file_
97         char thisname[MAXPATHLEN];
98         unsigned int l1 = 0, l2 = 0;
99         int alloc_len, basename_len, dirname_len, linkname_len, sum_len;
100 +       int atime_len;
101         OFF_T file_length;
102         char *basename, *dirname, *bp;
103         struct file_struct *file;
104  
105         if (!flist) {
106 -               modtime = 0, mode = 0;
107 +               modtime = 0, atime = 0, mode = 0;
108                 dev = 0, rdev = MAKEDEV(0, 0);
109                 rdev_major = 0;
110                 uid = 0, gid = 0;
111 @@ -558,6 +574,8 @@ static struct file_struct *receive_file_
112                 modtime = (time_t)read_int(f);
113         if (!(flags & XMIT_SAME_MODE))
114                 mode = from_wire_mode(read_int(f));
115 +       if (preserve_atimes && !S_ISDIR(mode) && !(flags & XMIT_SAME_ATIME))
116 +               atime = (time_t)read_int(f);
117  
118         if (chmod_modes && !S_ISLNK(mode))
119                 mode = tweak_mode(mode, chmod_modes);
120 @@ -600,19 +618,23 @@ static struct file_struct *receive_file_
121  
122         sum_len = always_checksum && S_ISREG(mode) ? MD4_SUM_LENGTH : 0;
123  
124 +       atime_len = preserve_atimes ? 4 : 0;
125 +
126         alloc_len = file_struct_len + dirname_len + basename_len
127 -                 + linkname_len + sum_len;
128 +                 + atime_len + linkname_len + sum_len;
129         bp = pool_alloc(flist->file_pool, alloc_len, "receive_file_entry");
130  
131         file = (struct file_struct *)bp;
132         memset(bp, 0, file_struct_len);
133 -       bp += file_struct_len;
134 +       bp += file_struct_len + atime_len;
135  
136         file->modtime = modtime;
137         file->length = file_length;
138         file->mode = mode;
139         file->uid = uid;
140         file->gid = gid;
141 +       if (preserve_atimes)
142 +               SIVAL(ATIME_PTR(file), 0, atime);
143  
144         if (dirname_len) {
145                 file->dirname = lastdir = bp;
146 @@ -731,6 +753,7 @@ struct file_struct *make_file(char *fnam
147         char thisname[MAXPATHLEN];
148         char linkname[MAXPATHLEN];
149         int alloc_len, basename_len, dirname_len, linkname_len, sum_len;
150 +       int atime_len;
151         char *basename, *dirname, *bp;
152  
153         if (!flist || !flist->count)    /* Ignore lastdir when invalid. */
154 @@ -854,8 +877,10 @@ struct file_struct *make_file(char *fnam
155         sum_len = always_checksum && am_sender && S_ISREG(st.st_mode)
156                 ? MD4_SUM_LENGTH : 0;
157  
158 +       atime_len = preserve_atimes ? 4 : 0;
159 +
160         alloc_len = file_struct_len + dirname_len + basename_len
161 -                 + linkname_len + sum_len;
162 +                 + atime_len + linkname_len + sum_len;
163         if (flist)
164                 bp = pool_alloc(flist->file_pool, alloc_len, "make_file");
165         else {
166 @@ -865,7 +890,7 @@ struct file_struct *make_file(char *fnam
167  
168         file = (struct file_struct *)bp;
169         memset(bp, 0, file_struct_len);
170 -       bp += file_struct_len;
171 +       bp += file_struct_len + atime_len;
172  
173         file->flags = flags;
174         file->modtime = st.st_mtime;
175 @@ -873,6 +898,8 @@ struct file_struct *make_file(char *fnam
176         file->mode = st.st_mode;
177         file->uid = st.st_uid;
178         file->gid = st.st_gid;
179 +       if (preserve_atimes)
180 +               SIVAL(ATIME_PTR(file), 0, st.st_atime);
181  
182  #ifdef SUPPORT_HARD_LINKS
183         if (flist && flist->hlink_pool) {
184 @@ -1594,8 +1621,9 @@ static void clean_flist(struct file_list
185                         }
186                         /* Make sure we don't lose track of a user-specified
187                          * top directory. */
188 -                       flist->files[keep]->flags |= flist->files[drop]->flags
189 -                                                  & (FLAG_TOP_DIR|FLAG_DEL_HERE);
190 +                       flist->files[keep]->flags
191 +                           |= flist->files[drop]->flags
192 +                            & (FLAG_TOP_DIR|FLAG_DEL_HERE);
193  
194                         clear_file(flist->files[drop], flist);
195  
196 --- old/generator.c
197 +++ new/generator.c
198 @@ -43,6 +43,7 @@ extern int preserve_perms;
199  extern int preserve_uid;
200  extern int preserve_gid;
201  extern int preserve_times;
202 +extern int preserve_atimes;
203  extern int omit_dir_times;
204  extern int delete_mode;
205  extern int delete_before;
206 @@ -90,6 +91,7 @@ extern dev_t filesystem_dev;
207  extern char *backup_dir;
208  extern char *backup_suffix;
209  extern int backup_suffix_len;
210 +extern unsigned int file_struct_len;
211  extern struct file_list *the_file_list;
212  extern struct filter_list_struct server_filter_list;
213  
214 @@ -409,6 +411,9 @@ void itemize(struct file_struct *file, i
215                   && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
216                  || (keep_time && cmp_time(file->modtime, st->st_mtime) != 0))
217                         iflags |= ITEM_REPORT_TIME;
218 +               if (preserve_atimes && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
219 +                && cmp_time(IVAL(ATIME_PTR(file), 0), st->st_atime) != 0)
220 +                       iflags |= ITEM_REPORT_ATIME;
221                 if ((file->mode & CHMOD_BITS) != (st->st_mode & CHMOD_BITS))
222                         iflags |= ITEM_REPORT_PERMS;
223                 if (preserve_uid && am_root && file->uid != st->st_uid)
224 @@ -720,6 +725,8 @@ static int try_dests_reg(struct file_str
225                         if (hard_link_one(file, ndx, fname, 0, stp,
226                                           cmpbuf, 1, i, code) < 0)
227                                 goto try_a_copy;
228 +                       if (preserve_atimes)
229 +                               set_file_attrs(fname, file, stp, 0);
230                         if (preserve_hard_links && file->link_u.links) {
231                                 if (dry_run)
232                                         file->link_u.links->link_dest_used = j + 1;
233 --- old/log.c
234 +++ new/log.c
235 @@ -37,6 +37,7 @@ extern int msg_fd_out;
236  extern int allow_8bit_chars;
237  extern int protocol_version;
238  extern int preserve_times;
239 +extern int preserve_atimes;
240  extern int stdout_format_has_i;
241  extern int stdout_format_has_o_or_i;
242  extern int logfile_format_has_i;
243 @@ -611,7 +612,8 @@ static void log_formatted(enum logcode c
244                         c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
245                         c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
246                         c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
247 -                       c[8] = '.';
248 +                       c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.'
249 +                            : S_ISLNK(file->mode) ? 'U' : 'u';
250                         c[9] = '\0';
251  
252                         if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
253 --- old/options.c
254 +++ new/options.c
255 @@ -55,6 +55,7 @@ int preserve_uid = 0;
256  int preserve_gid = 0;
257  int preserve_times = 0;
258  int omit_dir_times = 0;
259 +int preserve_atimes = 0;
260  int update_only = 0;
261  int cvs_exclude = 0;
262  int dry_run = 0;
263 @@ -311,8 +312,9 @@ void usage(enum logcode F)
264    rprintf(F,"     --devices               preserve device files (super-user only)\n");
265    rprintf(F,"     --specials              preserve special files\n");
266    rprintf(F," -D                          same as --devices --specials\n");
267 -  rprintf(F," -t, --times                 preserve times\n");
268 -  rprintf(F," -O, --omit-dir-times        omit directories when preserving times\n");
269 +  rprintf(F," -t, --times                 preserve modify times\n");
270 +  rprintf(F," -O, --omit-dir-times        omit directories when preserving modify times\n");
271 +  rprintf(F," -U, --atimes                preserve access (use) times\n");
272    rprintf(F,"     --super                 receiver attempts super-user activities\n");
273    rprintf(F," -S, --sparse                handle sparse files efficiently\n");
274    rprintf(F," -n, --dry-run               show what would have been transferred\n");
275 @@ -428,6 +430,9 @@ static struct poptOption long_options[] 
276    {"times",           't', POPT_ARG_VAL,    &preserve_times, 1, 0, 0 },
277    {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
278    {"no-t",             0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
279 +  {"atimes",          'U', POPT_ARG_VAL,    &preserve_atimes, 1, 0, 0 },
280 +  {"no-atimes",        0,  POPT_ARG_VAL,    &preserve_atimes, 0, 0, 0 },
281 +  {"no-k",             0,  POPT_ARG_VAL,    &preserve_atimes, 0, 0, 0 },
282    {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 2, 0, 0 },
283    {"modify-window",    0,  POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
284    {"super",            0,  POPT_ARG_VAL,    &am_root, 2, 0, 0 },
285 @@ -1538,6 +1543,8 @@ void server_options(char **args,int *arg
286                 argstr[x++] = 'D';
287         if (preserve_times)
288                 argstr[x++] = 't';
289 +       if (preserve_atimes)
290 +               argstr[x++] = 'U';
291         if (preserve_perms)
292                 argstr[x++] = 'p';
293         else if (preserve_executability && am_sender)
294 --- old/rsync.c
295 +++ new/rsync.c
296 @@ -34,6 +34,7 @@ extern int verbose;
297  extern int dry_run;
298  extern int preserve_perms;
299  extern int preserve_executability;
300 +extern int preserve_atimes;
301  extern int preserve_times;
302  extern int omit_dir_times;
303  extern int am_root;
304 @@ -49,6 +50,7 @@ extern int keep_dirlinks;
305  extern int make_backups;
306  extern mode_t orig_umask;
307  extern struct stats stats;
308 +extern unsigned int file_struct_len;
309  extern struct chmod_mode_struct *daemon_chmod_modes;
310  
311  #if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
312 @@ -129,6 +131,7 @@ int set_file_attrs(char *fname, struct f
313         int updated = 0;
314         STRUCT_STAT st2;
315         int change_uid, change_gid;
316 +       time_t atime, mtime;
317         mode_t new_mode = file->mode;
318  
319         if (!st) {
320 @@ -148,18 +151,36 @@ int set_file_attrs(char *fname, struct f
321                 }
322         }
323  
324 +       /* This code must be the first update in the function due to
325 +        * how it uses the "updated" variable. */
326         if (!preserve_times || (S_ISDIR(st->st_mode) && omit_dir_times))
327                 flags |= ATTRS_SKIP_MTIME;
328 +       if (!preserve_atimes || S_ISDIR(st->st_mode))
329 +               flags |= ATTRS_SKIP_ATIME;
330         if (!(flags & ATTRS_SKIP_MTIME)
331             && cmp_time(st->st_mtime, file->modtime) != 0) {
332 -               int ret = set_modtime(fname, file->modtime, st->st_mode);
333 +               mtime = file->modtime;
334 +               updated = 1;
335 +       } else
336 +               mtime = st->st_mtime;
337 +       if (!(flags & ATTRS_SKIP_ATIME)) {
338 +               time_t file_atime = IVAL(ATIME_PTR(file), 0);
339 +               if (cmp_time(st->st_atime, file_atime) != 0) {
340 +                       atime = file_atime;
341 +                       updated = 1;
342 +               } else
343 +                       atime = st->st_atime;
344 +       } else
345 +               atime = st->st_atime;
346 +       if (updated) {
347 +               int ret = set_times(fname, mtime, atime, st->st_mode);
348                 if (ret < 0) {
349                         rsyserr(FERROR, errno, "failed to set times on %s",
350                                 full_fname(fname));
351                         return 0;
352                 }
353 -               if (ret == 0) /* ret == 1 if symlink could not be set */
354 -                       updated = 1;
355 +               if (ret > 0) /* ret == 1 if symlink could not be set */
356 +                       updated = 0;
357         }
358  
359         change_uid = am_root && preserve_uid && st->st_uid != file->uid;
360 --- old/rsync.h
361 +++ new/rsync.h
362 @@ -54,6 +54,7 @@
363  #define XMIT_HAS_IDEV_DATA (1<<9)
364  #define XMIT_SAME_DEV (1<<10)
365  #define XMIT_RDEV_MINOR_IS_SMALL (1<<11)
366 +#define XMIT_SAME_ATIME (1<<12)
367  
368  /* These flags are used in the live flist data. */
369  
370 @@ -120,6 +121,7 @@
371  
372  #define ATTRS_REPORT           (1<<0)
373  #define ATTRS_SKIP_MTIME       (1<<1)
374 +#define ATTRS_SKIP_ATIME       (1<<2)
375  
376  #define FULL_FLUSH     1
377  #define NORMAL_FLUSH   0
378 @@ -534,6 +536,8 @@ struct file_struct {
379         uchar flags;    /* this item MUST remain last */
380  };
381  
382 +#define ATIME_PTR(f) (((uchar*)(f))+file_struct_len)
383 +
384  /*
385   * Start the flist array at FLIST_START entries and grow it
386   * by doubling until FLIST_LINEAR then grow by FLIST_LINEAR
387 --- old/rsync.yo
388 +++ new/rsync.yo
389 @@ -328,8 +328,9 @@ to the detailed description below for a 
390       --devices               preserve device files (super-user only)
391       --specials              preserve special files
392   -D                          same as --devices --specials
393 - -t, --times                 preserve times
394 - -O, --omit-dir-times        omit directories when preserving times
395 + -t, --times                 preserve modify times
396 + -O, --omit-dir-times        omit directories when preserving mod-times
397 + -U, --atimes                preserve access (use) times
398       --super                 receiver attempts super-user activities
399   -S, --sparse                handle sparse files efficiently
400   -n, --dry-run               show what would have been transferred
401 @@ -869,6 +870,12 @@ it is preserving modification times (see
402  the directories on the receiving side, it is a good idea to use bf(-O).
403  This option is inferred if you use bf(--backup) without bf(--backup-dir).
404  
405 +dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the
406 +destination files to the same value as the source files.  Note that the
407 +reading of the source file may update the atime of the source files, so
408 +repeated rsync runs with --atimes may be needed if you want to force the
409 +access-time values to be 100% identical on the two systems.
410 +
411  dit(bf(--super)) This tells the receiving side to attempt super-user
412  activities even if the receiving rsync wasn't run by the super-user.  These
413  activities include: preserving users via the bf(--owner) option, preserving
414 @@ -1396,7 +1403,7 @@ with older versions of rsync, but that a
415  verbose messages).
416  
417  The "%i" escape has a cryptic output that is 9 letters long.  The general
418 -format is like the string bf(YXcstpogz), where bf(Y) is replaced by the
419 +format is like the string bf(YXcstpogu), where bf(Y) is replaced by the
420  type of update being done, bf(X) is replaced by the file-type, and the
421  other letters represent attributes that may be output if they are being
422  modified.
423 @@ -1436,7 +1443,7 @@ quote(itemization(
424    by the file transfer.
425    it() A bf(t) means the modification time is different and is being updated
426    to the sender's value (requires bf(--times)).  An alternate value of bf(T)
427 -  means that the time will be set to the transfer time, which happens
428 +  means that the modify time will be set to the transfer time, which happens
429    anytime a symlink is transferred, or when a file or device is transferred
430    without bf(--times).
431    it() A bf(p) means the permissions are different and are being updated to
432 @@ -1445,7 +1452,10 @@ quote(itemization(
433    sender's value (requires bf(--owner) and super-user privileges).
434    it() A bf(g) means the group is different and is being updated to the
435    sender's value (requires bf(--group) and the authority to set the group).
436 -  it() The bf(z) slot is reserved for future use.
437 +  it() A bf(u) means the access (use) time is different and is being updated to
438 +  the sender's value (requires bf(--atimes)).  An alternate value of bf(U)
439 +  means that the access time will be set to the transfer time, which happens
440 +  when a symlink or directory is updated.
441  ))
442  
443  One other output is possible:  when deleting files, the "%i" will output
444 --- old/sender.c
445 +++ new/sender.c
446 @@ -41,6 +41,7 @@ extern int do_progress;
447  extern int inplace;
448  extern int batch_fd;
449  extern int write_batch;
450 +extern unsigned int file_struct_len;
451  extern struct stats stats;
452  extern struct file_list *the_file_list;
453  extern char *stdout_format;
454 --- old/testsuite/atimes.test
455 +++ new/testsuite/atimes.test
456 @@ -0,0 +1,19 @@
457 +#! /bin/sh
458 +
459 +# Test rsync copying atimes
460 +
461 +. "$suitedir/rsync.fns"
462 +
463 +set -x
464 +
465 +mkdir "$fromdir"
466 +
467 +touch "$fromdir/foo"
468 +touch -a -t 200102031717.42 "$fromdir/foo"
469 +
470 +TLS_ARGS=--atime
471 +
472 +checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
473 +
474 +# The script would have aborted on error, so getting here means we've won.
475 +exit 0
476 --- old/testsuite/rsync.fns
477 +++ new/testsuite/rsync.fns
478 @@ -66,7 +66,7 @@ printmsg() {
479  }
480  
481  rsync_ls_lR() {
482 -    find "$@" -print | sort | sed 's/ /\\ /g' | xargs "$TOOLDIR/tls"
483 +    find "$@" -print | sort | sed 's/ /\\ /g' | xargs "$TOOLDIR/tls" $TLS_ARGS
484  }
485  
486  check_perms() {
487 @@ -184,6 +184,10 @@ checkit() {
488      # We can just write everything to stdout/stderr, because the
489      # wrapper hides it unless there is a problem.
490  
491 +    if test x$TLS_ARGS = x--atime; then
492 +       ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
493 +    fi
494 +
495      echo "Running: \"$1\""  
496      eval "$1" 
497      status=$?
498 @@ -191,10 +195,13 @@ checkit() {
499         failed="YES";
500      fi
501  
502 +    if test x$TLS_ARGS != x--atime; then
503 +       ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
504 +    fi
505 +
506      echo "-------------"
507      echo "check how the directory listings compare with diff:"
508      echo ""
509 -    ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
510      ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
511      diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed=YES
512  
513 --- old/tls.c
514 +++ new/tls.c
515 @@ -34,6 +34,7 @@
516   * change. */
517  
518  #include "rsync.h"
519 +#include "popt.h"
520  
521  #define PROGRAM "tls"
522  
523 @@ -43,6 +44,8 @@ int read_only = 1;
524  int list_only = 0;
525  int preserve_perms = 0;
526  
527 +static int display_atime = 0;
528
529  static void failed(char const *what, char const *where)
530  {
531         fprintf(stderr, PROGRAM ": %s %s: %s\n",
532 @@ -50,12 +53,29 @@ static void failed(char const *what, cha
533         exit(1);
534  }
535  
536 +static void storetime(char *dest, time_t t, size_t destsize)
537 +{
538 +       if (t) {
539 +               struct tm *mt = gmtime(&t);
540 +
541 +               snprintf(dest, destsize,
542 +                       "%04d-%02d-%02d %02d:%02d:%02d ",
543 +                       (int)mt->tm_year + 1900,
544 +                       (int)mt->tm_mon + 1,
545 +                       (int)mt->tm_mday,
546 +                       (int)mt->tm_hour,
547 +                       (int)mt->tm_min,
548 +                       (int)mt->tm_sec);
549 +       } else
550 +               strlcpy(dest, "                    ", destsize);
551 +}
552 +
553  static void list_file(const char *fname)
554  {
555         STRUCT_STAT buf;
556         char permbuf[PERMSTRING_SIZE];
557 -       struct tm *mt;
558 -       char datebuf[50];
559 +       char mtimebuf[50];
560 +       char atimebuf[50];
561         char linkbuf[4096];
562  
563         if (do_lstat(fname, &buf) < 0)
564 @@ -88,19 +108,8 @@ static void list_file(const char *fname)
565  
566         permstring(permbuf, buf.st_mode);
567  
568 -       if (buf.st_mtime) {
569 -               mt = gmtime(&buf.st_mtime);
570 -
571 -               snprintf(datebuf, sizeof datebuf,
572 -                       "%04d-%02d-%02d %02d:%02d:%02d",
573 -                       (int)mt->tm_year + 1900,
574 -                       (int)mt->tm_mon + 1,
575 -                       (int)mt->tm_mday,
576 -                       (int)mt->tm_hour,
577 -                       (int)mt->tm_min,
578 -                       (int)mt->tm_sec);
579 -       } else
580 -               strlcpy(datebuf, "                   ", sizeof datebuf);
581 +       storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
582 +       storetime(atimebuf, buf.st_atime, sizeof atimebuf);
583  
584         /* TODO: Perhaps escape special characters in fname? */
585  
586 @@ -111,23 +120,55 @@ static void list_file(const char *fname)
587                     (long)minor(buf.st_rdev));
588         } else /* NB: use double for size since it might not fit in a long. */
589                 printf("%12.0f", (double)buf.st_size);
590 -       printf(" %6ld.%-6ld %6ld %s %s%s\n",
591 +       printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
592                (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
593 -              datebuf, fname, linkbuf);
594 +              mtimebuf, display_atime && !S_ISDIR(buf.st_mode) ? atimebuf : "",
595 +              fname, linkbuf);
596 +}
597 +
598 +static struct poptOption long_options[] = {
599 +  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
600 +  {"atime",           'u', POPT_ARG_NONE,   &display_atime, 0,   0, 0},
601 +  {"help",            'h', POPT_ARG_NONE,   0,              'h', 0, 0},
602 +  {0,0,0,0,0,0,0}
603 +};
604 +
605 +static void tls_usage(int ret)
606 +{
607 +       fprintf(stderr, "usage: " PROGRAM " [--atime | -u] DIR ...\n"
608 +           "Trivial file listing program for portably checking rsync\n");
609 +       exit(ret);
610  }
611  
612  int
613  main(int argc, char *argv[])
614  {
615 -       if (argc < 2) {
616 -               fprintf(stderr, "usage: " PROGRAM " DIR ...\n"
617 -                       "Trivial file listing program for portably checking rsync\n");
618 -               return 1;
619 -       }
620 +       poptContext pc;
621 +       const char **extra_args;
622 +       int opt;
623  
624 -       for (argv++; *argv; argv++) {
625 -               list_file(*argv);
626 +       pc = poptGetContext(PROGRAM, argc, (const char **)argv,
627 +                           long_options, 0);
628 +       while ((opt = poptGetNextOpt(pc)) != -1) {
629 +               switch (opt) {
630 +               case 'h':
631 +                       tls_usage(0);
632 +               default:
633 +                       fprintf(stderr,
634 +                               "%s: %s\n",
635 +                               poptBadOption(pc, POPT_BADOPTION_NOALIAS),
636 +                               poptStrerror(opt));
637 +                       tls_usage(1);
638 +               }
639         }
640  
641 +       extra_args = poptGetArgs(pc);
642 +       if (*extra_args == NULL)
643 +               tls_usage(1);
644 +
645 +       for (; *extra_args; extra_args++)
646 +               list_file(*extra_args);
647 +       poptFreeContext(pc);
648 +
649         return 0;
650  }
651 --- old/util.c
652 +++ new/util.c
653 @@ -121,7 +121,7 @@ NORETURN void overflow_exit(const char *
654         exit_cleanup(RERR_MALLOC);
655  }
656  
657 -int set_modtime(const char *fname, time_t modtime, mode_t mode)
658 +int set_times(const char *fname, time_t modtime, time_t atime, mode_t mode)
659  {
660  #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
661         if (S_ISLNK(mode))
662 @@ -129,9 +129,13 @@ int set_modtime(const char *fname, time_
663  #endif
664  
665         if (verbose > 2) {
666 -               rprintf(FINFO, "set modtime of %s to (%ld) %s",
667 +               char mtimebuf[200];
668 +
669 +               strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
670 +               rprintf(FINFO,
671 +                       "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
672                         fname, (long)modtime,
673 -                       asctime(localtime(&modtime)));
674 +                       mtimebuf, (long)atime, timestring(atime));
675         }
676  
677         if (dry_run)
678 @@ -140,7 +144,7 @@ int set_modtime(const char *fname, time_
679         {
680  #ifdef HAVE_UTIMES
681                 struct timeval t[2];
682 -               t[0].tv_sec = time(NULL);
683 +               t[0].tv_sec = atime;
684                 t[0].tv_usec = 0;
685                 t[1].tv_sec = modtime;
686                 t[1].tv_usec = 0;
687 @@ -153,12 +157,12 @@ int set_modtime(const char *fname, time_
688                 return utimes(fname, t);
689  #elif defined HAVE_UTIMBUF
690                 struct utimbuf tbuf;
691 -               tbuf.actime = time(NULL);
692 +               tbuf.actime = atime;
693                 tbuf.modtime = modtime;
694                 return utime(fname,&tbuf);
695  #elif defined HAVE_UTIME
696                 time_t t[2];
697 -               t[0] = time(NULL);
698 +               t[0] = atime;
699                 t[1] = modtime;
700                 return utime(fname,t);
701  #else