busybox/coreutils/touch.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini touch implementation for busybox
   4 *
   5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 */
   9/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
  10 *
  11 * Previous version called open() and then utime().  While this will be
  12 * be necessary to implement -r and -t, it currently only makes things bigger.
  13 * Also, exiting on a failure was a bug.  All args should be processed.
  14 */
  15//config:config TOUCH
  16//config:       bool "touch (5.9 kb)"
  17//config:       default y
  18//config:       help
  19//config:       touch is used to create or change the access and/or
  20//config:       modification timestamp of specified files.
  21//config:
  22//config:config FEATURE_TOUCH_SUSV3
  23//config:       bool "Add support for SUSV3 features (-a -d -m -t -r)"
  24//config:       default y
  25//config:       depends on TOUCH
  26//config:       help
  27//config:       Enable touch to use a reference file or a given date/time argument.
  28
  29//applet:IF_TOUCH(APPLET_NOFORK(touch, touch, BB_DIR_BIN, BB_SUID_DROP, touch))
  30
  31//kbuild:lib-$(CONFIG_TOUCH) += touch.o
  32
  33//usage:#define touch_trivial_usage
  34//usage:       "[-ch" IF_FEATURE_TOUCH_SUSV3("am") "]"
  35//usage:       IF_FEATURE_TOUCH_SUSV3(" [-d DATE] [-t DATE] [-r FILE]")
  36//usage:       " FILE..."
  37//usage:#define touch_full_usage "\n\n"
  38//usage:       "Update mtime of FILEs\n"
  39//usage:     "\n        -c      Don't create files"
  40//usage:     "\n        -h      Don't follow links"
  41//usage:        IF_FEATURE_TOUCH_SUSV3(
  42//usage:     "\n        -a      Change only atime"
  43//usage:     "\n        -m      Change only mtime"
  44//usage:     "\n        -d DT   Date/time to use"
  45//usage:     "\n        -t DT   Date/time to use"
  46//usage:     "\n        -r FILE Use FILE's date/time"
  47//usage:        )
  48//usage:
  49//usage:#define touch_example_usage
  50//usage:       "$ ls -l /tmp/foo\n"
  51//usage:       "/bin/ls: /tmp/foo: No such file or directory\n"
  52//usage:       "$ touch /tmp/foo\n"
  53//usage:       "$ ls -l /tmp/foo\n"
  54//usage:       "-rw-rw-r--    1 andersen andersen        0 Apr 15 01:11 /tmp/foo\n"
  55
  56/* coreutils implements:
  57 * -a   change only the access time
  58 * -c, --no-create
  59 *      do not create any files
  60 * -d, --date=STRING
  61 *      parse STRING and use it instead of current time
  62 * -f   (ignored, BSD compat)
  63 * -m   change only the modification time
  64 * -h, --no-dereference
  65 * -r, --reference=FILE
  66 *      use this file's times instead of current time
  67 * -t STAMP
  68 *      use [[CC]YY]MMDDhhmm[.ss] instead of current time
  69 * --time=WORD
  70 *      change the specified time: WORD is access, atime, or use
  71 */
  72
  73#include "libbb.h"
  74
  75int touch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  76int touch_main(int argc UNUSED_PARAM, char **argv)
  77{
  78        int fd;
  79        int opts;
  80        smalluint status = EXIT_SUCCESS;
  81#if ENABLE_FEATURE_TOUCH_SUSV3
  82        char *reference_file;
  83        char *date_str;
  84        /* timebuf[0] is atime, timebuf[1] is mtime */
  85        struct timespec timebuf[2];
  86#else
  87# define reference_file NULL
  88# define date_str       NULL
  89# define timebuf        ((struct timespec*)NULL)
  90#endif
  91
  92        enum {
  93                OPT_c = (1 << 0),
  94                OPT_h = (1 << 1),
  95                OPT_r = (1 << 2) * ENABLE_FEATURE_TOUCH_SUSV3,
  96                OPT_d = (1 << 3) * ENABLE_FEATURE_TOUCH_SUSV3,
  97                OPT_t = (1 << 4) * ENABLE_FEATURE_TOUCH_SUSV3,
  98                OPT_a = (1 << 5) * ENABLE_FEATURE_TOUCH_SUSV3,
  99                OPT_m = (1 << 6) * ENABLE_FEATURE_TOUCH_SUSV3,
 100        };
 101#if ENABLE_LONG_OPTS
 102        static const char touch_longopts[] ALIGN1 =
 103                /* name, has_arg, val */
 104                "no-create\0"        No_argument       "c"
 105                "no-dereference\0"   No_argument       "h"
 106                IF_FEATURE_TOUCH_SUSV3("reference\0"        Required_argument "r")
 107                IF_FEATURE_TOUCH_SUSV3("date\0"             Required_argument "d")
 108        ;
 109#endif
 110        /* -d and -t both set time. In coreutils,
 111         * accepted data format differs a bit between -d and -t.
 112         * We accept the same formats for both
 113         */
 114        opts = getopt32long(argv, "^"
 115                "ch"
 116                IF_FEATURE_TOUCH_SUSV3("r:d:t:am")
 117                /*ignored:*/ "f" IF_NOT_FEATURE_TOUCH_SUSV3("am")
 118                "\0" /* opt_complementary: */
 119                /* at least one arg: */ "-1"
 120                /* coreutils forbids -r and -t at once: */ IF_FEATURE_TOUCH_SUSV3(":r--t:t--r")
 121                /* but allows these combinations: "r--d:d--r:t--d:d--t" */
 122                , touch_longopts
 123#if ENABLE_FEATURE_TOUCH_SUSV3
 124                , &reference_file
 125                , &date_str
 126                , &date_str
 127#endif
 128        );
 129
 130#if ENABLE_FEATURE_TOUCH_SUSV3
 131        timebuf[0].tv_nsec = timebuf[1].tv_nsec = UTIME_NOW;
 132        if (opts & OPT_r) {
 133                struct stat stbuf;
 134                xstat(reference_file, &stbuf);
 135                timebuf[0].tv_sec = stbuf.st_atime;
 136                timebuf[1].tv_sec = stbuf.st_mtime;
 137                timebuf[0].tv_nsec = stbuf.st_atim.tv_nsec;
 138                timebuf[1].tv_nsec = stbuf.st_mtim.tv_nsec;
 139        }
 140        if (opts & (OPT_d|OPT_t)) {
 141                struct tm tm_time;
 142                time_t t;
 143
 144                //memset(&tm_time, 0, sizeof(tm_time));
 145                /* Better than memset: makes "HH:MM" dates meaningful */
 146                time(&t);
 147                localtime_r(&t, &tm_time);
 148                parse_datestr(date_str, &tm_time);
 149
 150                /* Correct any day of week and day of year etc. fields */
 151                tm_time.tm_isdst = -1;  /* Be sure to recheck dst */
 152                t = validate_tm_time(date_str, &tm_time);
 153
 154                timebuf[1].tv_sec = timebuf[0].tv_sec = t;
 155                timebuf[1].tv_nsec = timebuf[0].tv_nsec = 0;
 156        }
 157        /* If both -a and -m specified, both times should be set.
 158         * IOW: set OMIT only if one, not both, of them is given!
 159         */
 160        if ((opts & (OPT_a|OPT_m)) == OPT_a)
 161                timebuf[1].tv_nsec = UTIME_OMIT;
 162        if ((opts & (OPT_a|OPT_m)) == OPT_m)
 163                timebuf[0].tv_nsec = UTIME_OMIT;
 164#endif
 165
 166        argv += optind;
 167        do {
 168                int result = utimensat(AT_FDCWD, *argv, timebuf,
 169                                (opts & OPT_h) ? AT_SYMLINK_NOFOLLOW : 0);
 170                if (result != 0) {
 171                        if (errno == ENOENT) { /* no such file? */
 172                                if (opts & OPT_c) {
 173                                        /* Creation is disabled, so ignore */
 174                                        continue;
 175                                }
 176                                /* Try to create the file */
 177                                fd = open(*argv, O_RDWR | O_CREAT, 0666);
 178                                if (fd >= 0) {
 179                                        if (opts & (OPT_r|OPT_d|OPT_t))
 180                                                futimens(fd, timebuf);
 181                                        xclose(fd);
 182                                        continue;
 183                                }
 184                        }
 185                        status = EXIT_FAILURE;
 186                        bb_simple_perror_msg(*argv);
 187                }
 188        } while (*++argv);
 189
 190        return status;
 191}
 192