forget difference between big and small commands - obsolete with vm.
[minix.git] / commands / simple / cleantmp.c
blobb98c547a10af5c5439f65b7ac35e75178fe433f3
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 #define NDEBUG
26 #endif
27 #include <assert.h>
29 #define SEC_DAY (24 * 3600L) /* A full day in seconds */
30 #define DOTDAYS 14 /* Don't remove tmp/.* in at least 14 days. */
32 void report(const char *label)
34 fprintf(stderr, "cleantmp: %s: %s\n", label, strerror(errno));
37 void fatal(const char *label)
39 report(label);
40 exit(1);
43 void *alloc(size_t s)
45 void *mem;
47 if ((mem= (void *) malloc(s)) == nil) fatal("");
48 return mem;
51 int force= 0; /* Force remove all. */
52 int debug= 0; /* Debug level. */
54 void days2time(unsigned long days, time_t *retired, time_t *dotretired)
56 struct tm *tm;
57 time_t t;
59 time(&t);
61 tm= localtime(&t);
62 tm->tm_hour= 0;
63 tm->tm_min= 0;
64 tm->tm_sec= 0; /* Step back to midnight of this day. */
65 t= mktime(tm);
67 if (t < (days - 1) * SEC_DAY) {
68 *retired= *dotretired= 0;
69 } else {
70 *retired= t - (days - 1) * SEC_DAY;
71 *dotretired= t - (DOTDAYS - 1) * SEC_DAY;
72 if (*dotretired > *retired) *dotretired= *retired;
74 if (debug >= 2) fprintf(stderr, "Retired: %s", ctime(retired));
75 if (debug >= 2) fprintf(stderr, "Dotretired: %s", ctime(dotretired));
78 /* Path name construction, addpath adds a component, delpath removes it.
79 * The string 'path' is used throughout the program as the file under
80 * examination.
83 char *path; /* Path name constructed in path[]. */
84 int plen= 0, pidx= 0; /* Lenght/index for path[]. */
86 void addpath(int *didx, char *name)
87 /* Add a component to path. (name may also be a full path at the first call)
88 * The index where the current path ends is stored in *pdi.
91 if (plen == 0) path= (char *) alloc((plen= 32) * sizeof(path[0]));
93 *didx= pidx; /* Record point to go back to for delpath. */
95 if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/';
97 do {
98 if (*name != '/' || pidx == 0 || path[pidx-1] != '/') {
99 if (pidx == plen &&
100 (path= (char *) realloc((void *) path,
101 (plen*= 2) * sizeof(path[0]))) == nil
102 ) fatal("");
103 path[pidx++]= *name;
105 } while (*name++ != 0);
107 --pidx; /* Put pidx back at the null. The path[pidx++]= '/'
108 * statement will overwrite it at the next call.
110 assert(pidx < plen);
113 void delpath(int didx)
115 assert(0 <= didx);
116 assert(didx <= pidx);
117 path[pidx= didx]= 0;
120 struct file {
121 struct file *next;
122 char *name;
125 struct file *listdir(void)
127 DIR *dp;
128 struct dirent *entry;
129 struct file *first, **last= &first;
131 if ((dp= opendir(path)) == nil) {
132 report(path);
133 return nil;
136 while ((entry= readdir(dp)) != nil) {
137 struct file *new;
139 if (strcmp(entry->d_name, ".") == 0
140 || strcmp(entry->d_name, "..") == 0) continue;
142 new= (struct file *) alloc(sizeof(*new));
143 new->name= (char *) alloc((size_t) strlen(entry->d_name) + 1);
144 strcpy(new->name, entry->d_name);
146 *last= new;
147 last= &new->next;
149 closedir(dp);
150 *last= nil;
152 return first;
155 struct file *shorten(struct file *list)
157 struct file *junk;
159 assert(list != nil);
161 junk= list;
162 list= list->next;
164 free((void *) junk->name);
165 free((void *) junk);
167 return list;
170 /* Hash list of files to ignore. */
171 struct file *ignore_list[1024];
172 size_t n_ignored= 0;
174 unsigned ihash(char *name)
175 /* A simple hashing function on a file name. */
177 unsigned h= 0;
179 while (*name != 0) h= (h * 0x1111) + *name++;
181 return h & (arraysize(ignore_list) - 1);
184 void do_ignore(int add, char *name)
185 /* Add or remove a file to/from the list of files to ignore. */
187 struct file **ipp, *ip;
189 ipp= &ignore_list[ihash(name)];
190 while ((ip= *ipp) != nil) {
191 if (strcmp(name, ip->name) <= 0) break;
192 ipp= &ip->next;
195 if (add) {
196 ip= alloc(sizeof(*ip));
197 ip->name= alloc((strlen(name) + 1) * sizeof(ip->name[0]));
198 strcpy(ip->name, name);
199 ip->next= *ipp;
200 *ipp= ip;
201 n_ignored++;
202 } else {
203 assert(ip != nil);
204 *ipp= ip->next;
205 free(ip->name);
206 free(ip);
207 n_ignored--;
211 int is_ignored(char *name)
212 /* Is a file in the list of ignored files? */
214 struct file *ip;
215 int r;
217 ip= ignore_list[ihash(name)];
218 while (ip != nil) {
219 if ((r = strcmp(name, ip->name)) <= 0) return (r == 0);
220 ip= ip->next;
222 return 0;
225 #define is_ignored(name) (n_ignored > 0 && (is_ignored)(name))
227 time_t retired, dotretired;
229 enum level { TOP, DOWN };
231 void cleandir(enum level level, time_t retired)
233 struct file *list;
234 struct stat st;
235 time_t ret;
237 if (debug >= 2) fprintf(stderr, "Cleaning %s\n", path);
239 list= listdir();
241 while (list != nil) {
242 int didx;
244 ret= (level == TOP && list->name[0] == '.') ?
245 dotretired : retired;
246 /* don't rm tmp/.* too soon. */
248 addpath(&didx, list->name);
250 if (is_ignored(path)) {
251 if (debug >= 1) fprintf(stderr, "ignoring %s\n", path);
252 do_ignore(0, path);
253 } else
254 if (is_ignored(list->name)) {
255 if (debug >= 1) fprintf(stderr, "ignoring %s\n", path);
256 } else
257 if (lstat(path, &st) < 0) {
258 report(path);
259 } else
260 if (S_ISDIR(st.st_mode)) {
261 cleandir(DOWN, ret);
262 if (force || st.st_mtime < ret) {
263 if (debug < 3 && rmdir(path) < 0) {
264 if (errno != ENOTEMPTY
265 && errno != EEXIST) {
266 report(path);
268 } else {
269 if (debug >= 1) {
270 fprintf(stderr,
271 "rmdir %s\n", path);
275 } else {
276 if (force || (st.st_atime < ret
277 && st.st_mtime < ret
278 && st.st_ctime < ret)
280 if (debug < 3 && unlink(path) < 0) {
281 if (errno != ENOENT) {
282 report(path);
284 } else {
285 if (debug >= 1) {
286 fprintf(stderr,
287 "rm %s\n", path);
292 delpath(didx);
293 list= shorten(list);
297 void usage(void)
299 fprintf(stderr,
300 "Usage: cleantmp [-d[level]] [-i file ] ... -days|-f directory ...\n");
301 exit(1);
304 int main(int argc, char **argv)
306 int i;
307 unsigned long days;
309 i= 1;
310 while (i < argc && argv[i][0] == '-') {
311 char *opt= argv[i++] + 1;
313 if (opt[0] == '-' && opt[1] == 0) break;
315 if (opt[0] == 'd') {
316 debug= 1;
317 if (opt[1] != 0) debug= atoi(opt + 1);
318 } else
319 if (opt[0] == 'i') {
320 if (*++opt == 0) {
321 if (i == argc) usage();
322 opt= argv[i++];
324 do_ignore(1, opt);
325 } else
326 if (opt[0] == 'f' && opt[1] == 0) {
327 force= 1;
328 days= 1;
329 } else {
330 char *end;
331 days= strtoul(opt, &end, 10);
332 if (*opt == 0 || *end != 0
333 || days == 0
334 || ((time_t) (days * SEC_DAY)) / SEC_DAY != days
336 fprintf(stderr,
337 "cleantmp: %s is not a valid number of days\n",
338 opt);
339 exit(1);
343 if (days == 0) usage();
345 days2time(days, &retired, &dotretired);
347 while (i < argc) {
348 int didx;
350 if (argv[i][0] == 0) {
351 fprintf(stderr, "cleantmp: empty pathname!\n");
352 exit(1);
354 addpath(&didx, argv[i]);
355 cleandir(TOP, retired);
356 delpath(didx);
357 assert(path[0] == 0);
358 i++;
360 exit(0);