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
75 int touch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
touch_main(int argc UNUSED_PARAM,char ** argv)76 int 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