1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1989-2011 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * Glenn Fowler
23  * AT&T Research
24  *
25  * touch -- touch file times
26  */
27 
28 static const char usage[] =
29 "[-?\n@(#)$Id: touch (AT&T Research) 2004-12-12 $\n]"
30 USAGE_LICENSE
31 "[+NAME?touch - change file access, modification and status change "
32     "times]"
33 "[+DESCRIPTION?\btouch\b changes the modification time, access time or "
34     "both of each \afile\a. The modification time is the \ast_mtime\a member "
35     "of the \bstat\b(2) information and the access time is the \ast_atime\a "
36     "member. On most systems the file status change time is always set to "
37     "the current time when either the access or modification times are "
38     "changed.]"
39 "[+?If neither the \b--reference\b nor the \b--time\b options are "
40     "specified then the time used will be the \adate\a operand or the "
41     "current time if \adate\a is omitted.]"
42 "[+?If the \adate\a operand consists of 4, 6, 8, 10 or 12 digits "
43     "followed by an optional \b.\b and two digits then it is interpreted as: "
44     "\aHHMM.SS\a, \addHHMM.SS\a, \ammddHHMM.SS\a, \ammddHHMMyy.SS\a or "
45     "\ayymmddHHMM.SS\a, or \ammddHHMMccyy.SS\a or \accyymmddHHMM.SS\a. "
46     "Conflicting standards and practice allow a leading or trailing 2 or 4 "
47     "digit year for the 10 and 12 digit forms; the X/Open leading form is "
48     "used to disambiguate (\bdate\b(1) uses the trailing form.) Avoid the 10 "
49     "digit form to avoid confusion. The digit fields are:]"
50     "{"
51         "[+cc?Century - 1, 19-20.]"
52         "[+yy?Year in century, 00-99.]"
53         "[+mm?Month, 01-12.]"
54         "[+dd?Day of month, 01-31.]"
55         "[+HH?Hour, 00-23.]"
56         "[+MM?Minute, 00-59.]"
57         "[+SS?Seconds, 00-60.]"
58     "}"
59 "[a:access|atime|use?Change the access time. Do not change the "
60     "modification time unless \b--modify\b is also specified.]"
61 "[c!:create?Create the \afile\a if it does not exist, but write no "
62     "diagnostic.]"
63 "[f:force?Ignored by this implementation.]"
64 "[m:modify|mtime|modification?Change the modify time. Do not change the "
65     "access time unless \b--access\b is also specified.]"
66 "[r:reference?Use the corresponding time of \afile\a instead of the "
67     "current time.]:[file]"
68 "[s|n:change|ctime|neither?Change only the file status change time "
69     "\ast_ctime\a. Most systems summarily set \ast_ctime\a to the current "
70     "time.]"
71 "[t|d:time|date?Use the specified \adate-time\a instead of the current "
72     "date-time. Most common formats are accepted. See \btmdate\b(3) for "
73     "details. If \adate-time\a consists of 4, 6, 8, 10 or 12 digits followed "
74     "by an optional \b.\b and 2 digits and another optional \b.\b and 1 or "
75     "more digits then it is interpreted as the \adate\a operand above, "
76     "except that the leading 2 or 4 digit year form is used to disambiguate. "
77     "Avoid the 10 digit form to avoid confusion. If \b--reference\b is "
78     "specified or if \afile\a already exists then \atime\a may also be one "
79     "of:]:[date-time]"
80     "{"
81         "[+access|atime|use?The access time of the reference file.]"
82         "[+change|ctime?The change time of the reference file.]"
83         "[+modify|mtime|modification?The modify time of the reference "
84             "file.]"
85     "}"
86 "[v:verbose?Write a diagnostic for each nonexistent \afile\a.]"
87     "\n\n"
88 "[ date ]"
89     "file ...\n\n"
90 "[+CAVEATS?Some systems or file system types may limit the range of "
91     "times that can be set. These limitations may not show up until a "
92     "subsequent \bstat\b(2) call (yes, the time can be set but not checked!) "
93     "Upper limits of <0xffffffff and <0x7fffffff have been observed.]"
94 "[+SEE ALSO?\bdate\b(1), \bnmake\b(1), \butime\b(2), \btm\b(3)]"
95 ;
96 
97 #include <ast.h>
98 #include <ls.h>
99 #include <tmx.h>
100 #include <error.h>
101 
102 #define ATIME		01
103 #define CTIME		02
104 #define MTIME		04
105 
106 int
main(int argc,register char ** argv)107 main(int argc, register char** argv)
108 {
109 	register char*	reference = 0;
110 	int		create = 1;
111 	int		set = 0;
112 	int		use = 0;
113 	int		verbose = 0;
114 
115 	register char*	file;
116 	char*		e;
117 	int		n;
118 	struct stat	st;
119 	Time_t		t;
120 	Tv_t*		ap;
121 	Tv_t*		cp;
122 	Tv_t*		mp;
123 	Tv_t*		up;
124 	Tv_t		av;
125 	Tv_t		cv;
126 	Tv_t		mv;
127 	Tv_t		uv;
128 
129 	NoP(argc);
130 	error_info.id = "touch";
131 	up = 0;
132 	for (;;)
133 	{
134 		switch (optget(argv, usage))
135 		{
136 		case 'a':
137 			set |= ATIME;
138 			continue;
139 		case 'c':
140 			create = 0;
141 			continue;
142 		case 'd':
143 		case 't':
144 			if (streq(opt_info.arg, "access") || streq(opt_info.arg, "atime") || streq(opt_info.arg, "use"))
145 				use = ATIME;
146 			else if (streq(opt_info.arg, "change") || streq(opt_info.arg, "ctime"))
147 				use = CTIME;
148 			else if (streq(opt_info.arg, "modify") || streq(opt_info.arg, "mtime") || streq(opt_info.arg, "modification"))
149 				use = MTIME;
150 			else
151 			{
152 				reference = 0;
153 				t = tmxdate(opt_info.arg, &e, TMX_NOW);
154 				if (*e)
155 					error(3, "%s: invalid date specification", e);
156 				up = &uv;
157 				tmx2tv(t, up);
158 			}
159 			continue;
160 		case 'f':
161 			continue;
162 		case 'm':
163 			set |= MTIME;
164 			continue;
165 		case 'r':
166 			reference = opt_info.arg;
167 			continue;
168 		case 's':
169 			set |= CTIME;
170 			continue;
171 		case 'v':
172 			verbose = 1;
173 			continue;
174 		case ':':
175 			error(2, "%s", opt_info.arg);
176 			break;
177 		case '?':
178 			error(ERROR_USAGE|4, "%s", opt_info.arg);
179 			break;
180 		}
181 		break;
182 	}
183 	argv += opt_info.index;
184 	if (error_info.errors || !*argv)
185 		error(ERROR_USAGE|4, "%s", optusage(NiL));
186 	if (reference)
187 	{
188 		if (stat(reference, &st))
189 			error(ERROR_SYSTEM|3, "%s: not found", reference);
190 		if (use)
191 		{
192 			up = &uv;
193 			switch (use)
194 			{
195 			case ATIME:
196 				tvgetatime(up, &st);
197 				break;
198 			case CTIME:
199 				tvgetctime(up, &st);
200 				break;
201 			case MTIME:
202 				tvgetmtime(up, &st);
203 				break;
204 			}
205 		}
206 	}
207 	else if (!use && !up)
208 	{
209 		for (file = *argv; *file >= '0' && *file <= '9'; file++);
210 		if (((n = (file - *argv)) == 4 || n == 6 || n == 8 || n == 10 || n == 12) && (!*file || *file == '.' && *(file + 1) >= '0' && *(file + 1) <= '9' && *(file + 2) >= '0' && *(file + 2) <= '9' && !*(file + 3)))
211 		{
212 			t = tmxdate(file = *argv++, &e, TMX_NOW);
213 			if (*e)
214 				error(3, "%s: invalid date specification", file);
215 			up = &uv;
216 			tmx2tv(t, up);
217 		}
218 	}
219 	if (!set)
220 		set = MTIME;
221 	if (set & ATIME)
222 	{
223 		if (use || !reference)
224 			ap = up;
225 		else
226 		{
227 			ap = &av;
228 			tvgetatime(ap, &st);
229 		}
230 	}
231 	else
232 		ap = TV_TOUCH_RETAIN;
233 	if (set & CTIME)
234 	{
235 		if (use || !reference)
236 			cp = up;
237 		else
238 		{
239 			cp = &cv;
240 			tvgetctime(cp, &st);
241 		}
242 	}
243 	else
244 		cp = TV_TOUCH_RETAIN;
245 	if (set & MTIME)
246 	{
247 		if (use || !reference)
248 			mp = up;
249 		else
250 		{
251 			mp = &mv;
252 			tvgetmtime(mp, &st);
253 		}
254 	}
255 	else
256 		mp = TV_TOUCH_RETAIN;
257 	if (reference)
258 		use = 0;
259 	else if (use)
260 		up = &uv;
261 	while (file = *argv++)
262 	{
263 		if (use)
264 		{
265 			if (stat(file, &st))
266 			{
267 				error(2, "%s: not found", file);
268 				continue;
269 			}
270 			switch (use)
271 			{
272 			case ATIME:
273 				tvgetatime(up, &st);
274 				break;
275 			case CTIME:
276 				tvgetctime(up, &st);
277 				break;
278 			case MTIME:
279 				tvgetmtime(up, &st);
280 				break;
281 			}
282 			if (set & ATIME)
283 				ap = up;
284 			if (set & CTIME)
285 				cp = up;
286 			if (set & MTIME)
287 				mp = up;
288 		}
289 		if (tvtouch(file, ap, mp, cp, create))
290 			if (errno != ENOENT)
291 				error(ERROR_SYSTEM|2, "%s: cannot touch", file);
292 			else if (verbose)
293 				error(1, "%s: not found", file);
294 	}
295 	return error_info.errors != 0;
296 }
297