tools/llvm: Do not build with symbols
[minix3.git] / minix / commands / cleantmp / cleantmp.c
bloba6018fde6899b89ff48fa84cff4369890c1a7162
1 /* cleantmp 1.6 - clean out a tmp dir. Author: Kees J. Bot
2 * 11 Apr 1991
3 */
4 #define nil 0
5 #include <sys/types.h>
6 #include <stdio.h>
7 #include <stddef.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <sys/stat.h>
11 #include <string.h>
12 #include <time.h>
13 #include <dirent.h>
14 #include <errno.h>
16 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
17 #define arraylimit(a) ((a) + arraysize(a))
19 #ifndef S_ISLNK
20 /* There were no symlinks in medieval times. */
21 #define lstat stat
22 #endif
24 #ifndef DEBUG
25 #ifndef NDEBUG
26 #define NDEBUG
27 #endif
28 #endif
29 #include <assert.h>
31 #define SEC_DAY (24 * 3600L) /* A full day in seconds */
32 #define DOTDAYS 14 /* Don't remove tmp/.* in at least 14 days. */
34 void report(const char *label)
36 fprintf(stderr, "cleantmp: %s: %s\n", label, strerror(errno));
39 void fatal(const char *label)
41 report(label);
42 exit(1);
45 void *alloc(size_t s)
47 void *mem;
49 if ((mem= (void *) malloc(s)) == nil) fatal("");
50 return mem;
53 int force= 0; /* Force remove all. */
54 int debug= 0; /* Debug level. */
56 void days2time(unsigned long days, time_t *retired, time_t *dotretired)
58 struct tm *tm;
59 time_t t;
61 time(&t);
63 tm= localtime(&t);
64 tm->tm_hour= 0;
65 tm->tm_min= 0;
66 tm->tm_sec= 0; /* Step back to midnight of this day. */
67 t= mktime(tm);
69 if (t < (days - 1) * SEC_DAY) {
70 *retired= *dotretired= 0;
71 } else {
72 *retired= t - (days - 1) * SEC_DAY;
73 *dotretired= t - (DOTDAYS - 1) * SEC_DAY;
74 if (*dotretired > *retired) *dotretired= *retired;
76 if (debug >= 2) fprintf(stderr, "Retired: %s", ctime(retired));
77 if (debug >= 2) fprintf(stderr, "Dotretired: %s", ctime(dotretired));
80 /* Path name construction, addpath adds a component, delpath removes it.
81 * The string 'path' is used throughout the program as the file under
82 * examination.
85 char *path; /* Path name constructed in path[]. */
86 int plen= 0, pidx= 0; /* Lenght/index for path[]. */
88 void addpath(int *didx, const char *name)
89 /* Add a component to path. (name may also be a full path at the first call)
90 * The index where the current path ends is stored in *pdi.
93 if (plen == 0) path= (char *) alloc((plen= 32) * sizeof(path[0]));
95 *didx= pidx; /* Record point to go back to for delpath. */
97 if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/';
99 do {
100 if (*name != '/' || pidx == 0 || path[pidx-1] != '/') {
101 if (pidx == plen &&
102 (path= (char *) realloc((void *) path,
103 (plen*= 2) * sizeof(path[0]))) == nil
104 ) fatal("");
105 path[pidx++]= *name;
107 } while (*name++ != 0);
109 --pidx; /* Put pidx back at the null. The path[pidx++]= '/'
110 * statement will overwrite it at the next call.
112 assert(pidx < plen);
115 void delpath(int didx)
117 assert(0 <= didx);
118 assert(didx <= pidx);
119 path[pidx= didx]= 0;
122 struct file {
123 struct file *next;
124 char *name;
127 struct file *listdir(void)
129 DIR *dp;
130 struct dirent *entry;
131 struct file *first, **last= &first;
133 if ((dp= opendir(path)) == nil) {
134 report(path);
135 return nil;
138 while ((entry= readdir(dp)) != nil) {
139 struct file *new;
141 if (strcmp(entry->d_name, ".") == 0
142 || strcmp(entry->d_name, "..") == 0) continue;
144 new= (struct file *) alloc(sizeof(*new));
145 new->name= (char *) alloc((size_t) strlen(entry->d_name) + 1);
146 strcpy(new->name, entry->d_name);
148 *last= new;
149 last= &new->next;
151 closedir(dp);
152 *last= nil;
154 return first;
157 struct file *shorten(struct file *list)
159 struct file *junk;
161 assert(list != nil);
163 junk= list;
164 list= list->next;
166 free((void *) junk->name);
167 free((void *) junk);
169 return list;
172 /* Hash list of files to ignore. */
173 struct file *ignore_list[1024];
174 size_t n_ignored= 0;
176 unsigned ihash(const char *name)
177 /* A simple hashing function on a file name. */
179 unsigned h= 0;
181 while (*name != 0) h= (h * 0x1111) + *name++;
183 return h & (arraysize(ignore_list) - 1);
186 void do_ignore(int add, const char *name)
187 /* Add or remove a file to/from the list of files to ignore. */
189 struct file **ipp, *ip;
191 ipp= &ignore_list[ihash(name)];
192 while ((ip= *ipp) != nil) {
193 if (strcmp(name, ip->name) <= 0) break;
194 ipp= &ip->next;
197 if (add) {
198 ip= alloc(sizeof(*ip));
199 ip->name= alloc((strlen(name) + 1) * sizeof(ip->name[0]));
200 strcpy(ip->name, name);
201 ip->next= *ipp;
202 *ipp= ip;
203 n_ignored++;
204 } else {
205 assert(ip != nil);
206 *ipp= ip->next;
207 free(ip->name);
208 free(ip);
209 n_ignored--;
213 int is_ignored(const char *name)
214 /* Is a file in the list of ignored files? */
216 struct file *ip;
217 int r;
219 ip= ignore_list[ihash(name)];
220 while (ip != nil) {
221 if ((r = strcmp(name, ip->name)) <= 0) return (r == 0);
222 ip= ip->next;
224 return 0;
227 #define is_ignored(name) (n_ignored > 0 && (is_ignored)(name))
229 time_t retired, dotretired;
231 enum level { TOP, DOWN };
233 void cleandir(enum level level, time_t retired)
235 struct file *list;
236 struct stat st;
237 time_t ret;
239 if (debug >= 2) fprintf(stderr, "Cleaning %s\n", path);
241 list= listdir();
243 while (list != nil) {
244 int didx;
246 ret= (level == TOP && list->name[0] == '.') ?
247 dotretired : retired;
248 /* don't rm tmp/.* too soon. */
250 addpath(&didx, list->name);
252 if (is_ignored(path)) {
253 if (debug >= 1) fprintf(stderr, "ignoring %s\n", path);
254 do_ignore(0, path);
255 } else
256 if (is_ignored(list->name)) {
257 if (debug >= 1) fprintf(stderr, "ignoring %s\n", path);
258 } else
259 if (lstat(path, &st) < 0) {
260 report(path);
261 } else
262 if (S_ISDIR(st.st_mode)) {
263 cleandir(DOWN, ret);
264 if (force || st.st_mtime < ret) {
265 if (debug < 3 && rmdir(path) < 0) {
266 if (errno != ENOTEMPTY
267 && errno != EEXIST) {
268 report(path);
270 } else {
271 if (debug >= 1) {
272 fprintf(stderr,
273 "rmdir %s\n", path);
277 } else {
278 if (force || (st.st_atime < ret
279 && st.st_mtime < ret
280 && st.st_ctime < ret)
282 if (debug < 3 && unlink(path) < 0) {
283 if (errno != ENOENT) {
284 report(path);
286 } else {
287 if (debug >= 1) {
288 fprintf(stderr,
289 "rm %s\n", path);
294 delpath(didx);
295 list= shorten(list);
299 void usage(void)
301 fprintf(stderr,
302 "Usage: cleantmp [-d[level]] [-i file ] ... -days|-f directory ...\n");
303 exit(1);
306 int main(int argc, char **argv)
308 int i;
309 unsigned long days;
311 i= 1;
312 while (i < argc && argv[i][0] == '-') {
313 char *opt= argv[i++] + 1;
315 if (opt[0] == '-' && opt[1] == 0) break;
317 if (opt[0] == 'd') {
318 debug= 1;
319 if (opt[1] != 0) debug= atoi(opt + 1);
320 } else
321 if (opt[0] == 'i') {
322 if (*++opt == 0) {
323 if (i == argc) usage();
324 opt= argv[i++];
326 do_ignore(1, opt);
327 } else
328 if (opt[0] == 'f' && opt[1] == 0) {
329 force= 1;
330 days= 1;
331 } else {
332 char *end;
333 days= strtoul(opt, &end, 10);
334 if (*opt == 0 || *end != 0
335 || days == 0
336 || ((time_t) (days * SEC_DAY)) / SEC_DAY != days
338 fprintf(stderr,
339 "cleantmp: %s is not a valid number of days\n",
340 opt);
341 exit(1);
345 if (days == 0) usage();
347 days2time(days, &retired, &dotretired);
349 while (i < argc) {
350 int didx;
352 if (argv[i][0] == 0) {
353 fprintf(stderr, "cleantmp: empty pathname!\n");
354 exit(1);
356 addpath(&didx, argv[i]);
357 cleandir(TOP, retired);
358 delpath(didx);
359 assert(path[0] == 0);
360 i++;
362 exit(0);