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                int check_dst;
 144
 145                //memset(&tm_time, 0, sizeof(tm_time));
 146                /* Better than memset: makes "HH:MM" dates meaningful */
 147                time(&t);
 148                localtime_r(&t, &tm_time);
 149                check_dst = parse_datestr(date_str, &tm_time);
 150
 151                /* Correct any day of week and day of year etc. fields */
 152                if (check_dst)
 153                        tm_time.tm_isdst = -1;  /* recheck dst unless date is UTC */
 154                t = validate_tm_time(date_str, &tm_time);
 155
 156                timebuf[1].tv_sec = timebuf[0].tv_sec = t;
 157                timebuf[1].tv_nsec = timebuf[0].tv_nsec = 0;
 158        }
 159        /* If both -a and -m specified, both times should be set.
 160         * IOW: set OMIT only if one, not both, of them is given!
 161         */
 162        if ((opts & (OPT_a|OPT_m)) == OPT_a)
 163                timebuf[1].tv_nsec = UTIME_OMIT;
 164        if ((opts & (OPT_a|OPT_m)) == OPT_m)
 165                timebuf[0].tv_nsec = UTIME_OMIT;
 166#endif
 167
 168        argv += optind;
 169        do {
 170                int result = utimensat(AT_FDCWD, *argv, timebuf,
 171                                (opts & OPT_h) ? AT_SYMLINK_NOFOLLOW : 0);
 172                if (result != 0) {
 173                        if (errno == ENOENT) { /* no such file? */
 174                                if (opts & OPT_c) {
 175                                        /* Creation is disabled, so ignore */
 176                                        continue;
 177                                }
 178                                /* Try to create the file */
 179                                fd = open(*argv, O_RDWR | O_CREAT, 0666);
 180                                if (fd >= 0) {
 181                                        if (opts & (OPT_r|OPT_d|OPT_t))
 182                                                futimens(fd, timebuf);
 183                                        xclose(fd);
 184                                        continue;
 185                                }
 186                        }
 187                        status = EXIT_FAILURE;
 188                        bb_simple_perror_msg(*argv);
 189                }
 190        } while (*++argv);
 191
 192        return status;
 193}
 194