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. */
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
)
47 if ((mem
= (void *) malloc(s
)) == nil
) fatal("");
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
)
64 tm
->tm_sec
= 0; /* Step back to midnight of this day. */
67 if (t
< (days
- 1) * SEC_DAY
) {
68 *retired
= *dotretired
= 0;
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
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
++]= '/';
98 if (*name
!= '/' || pidx
== 0 || path
[pidx
-1] != '/') {
100 (path
= (char *) realloc((void *) path
,
101 (plen
*= 2) * sizeof(path
[0]))) == nil
105 } while (*name
++ != 0);
107 --pidx
; /* Put pidx back at the null. The path[pidx++]= '/'
108 * statement will overwrite it at the next call.
113 void delpath(int didx
)
116 assert(didx
<= pidx
);
125 struct file
*listdir(void)
128 struct dirent
*entry
;
129 struct file
*first
, **last
= &first
;
131 if ((dp
= opendir(path
)) == nil
) {
136 while ((entry
= readdir(dp
)) != nil
) {
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
);
155 struct file
*shorten(struct file
*list
)
164 free((void *) junk
->name
);
170 /* Hash list of files to ignore. */
171 struct file
*ignore_list
[1024];
174 unsigned ihash(char *name
)
175 /* A simple hashing function on a file name. */
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;
196 ip
= alloc(sizeof(*ip
));
197 ip
->name
= alloc((strlen(name
) + 1) * sizeof(ip
->name
[0]));
198 strcpy(ip
->name
, name
);
211 int is_ignored(char *name
)
212 /* Is a file in the list of ignored files? */
217 ip
= ignore_list
[ihash(name
)];
219 if ((r
= strcmp(name
, ip
->name
)) <= 0) return (r
== 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
)
237 if (debug
>= 2) fprintf(stderr
, "Cleaning %s\n", path
);
241 while (list
!= nil
) {
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
);
254 if (is_ignored(list
->name
)) {
255 if (debug
>= 1) fprintf(stderr
, "ignoring %s\n", path
);
257 if (lstat(path
, &st
) < 0) {
260 if (S_ISDIR(st
.st_mode
)) {
262 if (force
|| st
.st_mtime
< ret
) {
263 if (debug
< 3 && rmdir(path
) < 0) {
264 if (errno
!= ENOTEMPTY
265 && errno
!= EEXIST
) {
276 if (force
|| (st
.st_atime
< ret
278 && st
.st_ctime
< ret
)
280 if (debug
< 3 && unlink(path
) < 0) {
281 if (errno
!= ENOENT
) {
300 "Usage: cleantmp [-d[level]] [-i file ] ... -days|-f directory ...\n");
304 int main(int argc
, char **argv
)
310 while (i
< argc
&& argv
[i
][0] == '-') {
311 char *opt
= argv
[i
++] + 1;
313 if (opt
[0] == '-' && opt
[1] == 0) break;
317 if (opt
[1] != 0) debug
= atoi(opt
+ 1);
321 if (i
== argc
) usage();
326 if (opt
[0] == 'f' && opt
[1] == 0) {
331 days
= strtoul(opt
, &end
, 10);
332 if (*opt
== 0 || *end
!= 0
334 || ((time_t) (days
* SEC_DAY
)) / SEC_DAY
!= days
337 "cleantmp: %s is not a valid number of days\n",
343 if (days
== 0) usage();
345 days2time(days
, &retired
, &dotretired
);
350 if (argv
[i
][0] == 0) {
351 fprintf(stderr
, "cleantmp: empty pathname!\n");
354 addpath(&didx
, argv
[i
]);
355 cleandir(TOP
, retired
);
357 assert(path
[0] == 0);