1 /* cleantmp 1.6 - clean out a tmp dir. Author: Kees J. Bot
16 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
17 #define arraylimit(a) ((a) + arraysize(a))
20 /* There were no symlinks in medieval times. */
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
)
49 if ((mem
= (void *) malloc(s
)) == nil
) fatal("");
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
)
66 tm
->tm_sec
= 0; /* Step back to midnight of this day. */
69 if (t
< (days
- 1) * SEC_DAY
) {
70 *retired
= *dotretired
= 0;
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
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
++]= '/';
100 if (*name
!= '/' || pidx
== 0 || path
[pidx
-1] != '/') {
102 (path
= (char *) realloc((void *) path
,
103 (plen
*= 2) * sizeof(path
[0]))) == nil
107 } while (*name
++ != 0);
109 --pidx
; /* Put pidx back at the null. The path[pidx++]= '/'
110 * statement will overwrite it at the next call.
115 void delpath(int didx
)
118 assert(didx
<= pidx
);
127 struct file
*listdir(void)
130 struct dirent
*entry
;
131 struct file
*first
, **last
= &first
;
133 if ((dp
= opendir(path
)) == nil
) {
138 while ((entry
= readdir(dp
)) != nil
) {
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
);
157 struct file
*shorten(struct file
*list
)
166 free((void *) junk
->name
);
172 /* Hash list of files to ignore. */
173 struct file
*ignore_list
[1024];
176 unsigned ihash(const char *name
)
177 /* A simple hashing function on a file name. */
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;
198 ip
= alloc(sizeof(*ip
));
199 ip
->name
= alloc((strlen(name
) + 1) * sizeof(ip
->name
[0]));
200 strcpy(ip
->name
, name
);
213 int is_ignored(const char *name
)
214 /* Is a file in the list of ignored files? */
219 ip
= ignore_list
[ihash(name
)];
221 if ((r
= strcmp(name
, ip
->name
)) <= 0) return (r
== 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
)
239 if (debug
>= 2) fprintf(stderr
, "Cleaning %s\n", path
);
243 while (list
!= nil
) {
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
);
256 if (is_ignored(list
->name
)) {
257 if (debug
>= 1) fprintf(stderr
, "ignoring %s\n", path
);
259 if (lstat(path
, &st
) < 0) {
262 if (S_ISDIR(st
.st_mode
)) {
264 if (force
|| st
.st_mtime
< ret
) {
265 if (debug
< 3 && rmdir(path
) < 0) {
266 if (errno
!= ENOTEMPTY
267 && errno
!= EEXIST
) {
278 if (force
|| (st
.st_atime
< ret
280 && st
.st_ctime
< ret
)
282 if (debug
< 3 && unlink(path
) < 0) {
283 if (errno
!= ENOENT
) {
302 "Usage: cleantmp [-d[level]] [-i file ] ... -days|-f directory ...\n");
306 int main(int argc
, char **argv
)
312 while (i
< argc
&& argv
[i
][0] == '-') {
313 char *opt
= argv
[i
++] + 1;
315 if (opt
[0] == '-' && opt
[1] == 0) break;
319 if (opt
[1] != 0) debug
= atoi(opt
+ 1);
323 if (i
== argc
) usage();
328 if (opt
[0] == 'f' && opt
[1] == 0) {
333 days
= strtoul(opt
, &end
, 10);
334 if (*opt
== 0 || *end
!= 0
336 || ((time_t) (days
* SEC_DAY
)) / SEC_DAY
!= days
339 "cleantmp: %s is not a valid number of days\n",
345 if (days
== 0) usage();
347 days2time(days
, &retired
, &dotretired
);
352 if (argv
[i
][0] == 0) {
353 fprintf(stderr
, "cleantmp: empty pathname!\n");
356 addpath(&didx
, argv
[i
]);
357 cleandir(TOP
, retired
);
359 assert(path
[0] == 0);