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