15 #define DBPATH "/var/lib/pacman/"
16 #define CACHEDIR "/var/cache/pacman/pkg/"
18 const char *argp_program_version
= "pkgcacheclean "VERSION
;
19 const char *argp_program_bug_address
= "auguste@gmail.com";
22 "pkgcacheclean -- a simple utility to clean pacman cache.\v"
23 "For installed packages, preserve_number of versions are reserved. This\n"
24 "includes the current version and the newest (preserve_number - 1) of\n"
25 "the remaining. For uninstalled packages all versions are deleted.\n"
26 "The default number is 2.";
27 static char args_doc
[] = "[preserve_number]";
29 static struct argp_option options
[] =
32 { .name
= "dry-run", .key
= 'n', .doc
= "Perform a trial run with no changes made" },
33 { .name
= "verbose", .key
= 'v', .doc
= "Verbose output" },
34 { .name
= "quiet", .key
= 'q', .doc
= "Suppress output, default" },
38 static regex_t pkgnamesplit
;
39 static regex_t pkgnametest
;
55 static char *dupsubstr(const char *str
, const int start
, const int end
)
58 const int len
= end
- start
;
60 ret
= malloc(sizeof(char) * (len
+ 1));
61 strncpy(ret
, str
+ start
, len
);
67 static struct pkginfo
* get_pkginfo_from_filename(const char * const filename
)
70 static regmatch_t match
[3];
72 ret
= malloc(sizeof(struct pkginfo
));
73 regexec(&pkgnamesplit
, filename
, 3, match
, 0);
74 ret
->name
= dupsubstr(filename
, match
[1].rm_so
, match
[1].rm_eo
);
75 ret
->version
= dupsubstr(filename
, match
[2].rm_so
, match
[2].rm_eo
);
76 ret
->filename
= strdup(filename
);
81 static struct pkginfo
* get_pkginfo_from_pmpkg(alpm_pkg_t
*pmpkg
)
85 ret
= malloc(sizeof(struct pkginfo
));
86 ret
->name
= strdup(alpm_pkg_get_name(pmpkg
));
87 ret
->version
= strdup(alpm_pkg_get_version(pmpkg
));
93 static void free_pkginfo(struct pkginfo
* pkg
)
101 static int pkgcomp(const void *a
, const void *b
)
103 struct pkginfo
*ap
= *(struct pkginfo
**)a
;
104 struct pkginfo
*bp
= *(struct pkginfo
**)b
;
106 int namecomp
= strcmp(ap
->name
, bp
->name
);
108 return namecomp
? namecomp
: -alpm_pkg_vercmp(ap
->version
,
112 static int pkgnamecomp(const void *a
, const void *b
)
114 return strcmp((*(struct pkginfo
**)a
)->name
,
115 (*(struct pkginfo
**)b
)->name
);
118 static int ispackage(const struct dirent
*file
)
120 return regexec(&pkgnametest
, file
->d_name
, 0, NULL
, 0) == 0;
123 static void free_pkginfo_array(struct pkginfo
**pkgs
, const int n
)
127 for (i
= 0; i
< n
; i
++)
128 free_pkginfo(pkgs
[i
]);
132 static off_t
get_file_size(const char *filename
)
140 static error_t
parse_opt(int key
, char *arg
, struct argp_state
*state
)
142 struct arguments
*argument
= state
-> input
;
147 argument
->dry_run
= 1;
150 argument
->verbose
= 1;
153 argument
->verbose
= 0;
156 if (argument
->preserve
)
157 return ARGP_ERR_UNKNOWN
;
158 argument
->preserve
= atoi(arg
);
159 if (argument
->preserve
<= 0)
160 return ARGP_ERR_UNKNOWN
;
163 return ARGP_ERR_UNKNOWN
;
169 int main(const int argc
, char ** __restrict__ argv
)
171 int i
, n
, m
, len
, count
= 0;
172 off_t total_size
= 0;
173 alpm_handle_t
*handle
;
175 alpm_list_t
*pkglist
;
177 enum _alpm_errno_t error
;
178 struct pkginfo
**cachepkg
, **localpkg
;
179 struct pkginfo
**hit
= NULL
;
180 const char *current
= "", *name
;
181 char cachedir
[PATH_MAX
] = CACHEDIR
;
182 struct argp arg_parser
= { .options
= options
, .parser
= parse_opt
,
183 .args_doc
= args_doc
, .doc
= doc
};
184 struct arguments args
= { .dry_run
= 0, .preserve
= 0, .verbose
= 0 };
186 argp_parse(&arg_parser
, argc
, argv
, 0, NULL
, &args
);
190 if (!args
.dry_run
&& getuid())
192 puts("please run as root.");
196 len
= strlen(cachedir
);
197 regcomp(&pkgnamesplit
, "^(.*)-([^-]*-[^-]*)-[^-]*$", REG_EXTENDED
);
198 regcomp(&pkgnametest
, "^.*-("CARCH
"|any).[^-]*$", REG_EXTENDED
);
200 handle
= alpm_initialize(ROOT
, DBPATH
, &error
);
202 n
= scandir(cachedir
, &dir
, ispackage
, NULL
);
203 cachepkg
= malloc(sizeof(struct pkginfo
*) * n
);
204 for (i
= 0; i
< n
; free(dir
[i
]), i
++)
205 cachepkg
[i
] = get_pkginfo_from_filename(dir
[i
]->d_name
);
207 qsort(cachepkg
, n
, sizeof(struct pkginfo
*), pkgcomp
);
209 db
= alpm_option_get_localdb(handle
);
210 pkglist
= alpm_db_get_pkgcache(db
);
211 m
= alpm_list_count(pkglist
);
212 localpkg
= malloc(sizeof(struct pkginfo
*) * m
);
213 for (i
= 0; i
< m
; i
++, pkglist
= alpm_list_next(pkglist
))
214 localpkg
[i
] = get_pkginfo_from_pmpkg(alpm_list_getdata(pkglist
));
215 qsort(localpkg
, m
, sizeof(struct pkginfo
*), pkgnamecomp
);
216 alpm_list_free_inner(pkglist
, (alpm_list_fn_free
)alpm_pkg_free
);
217 alpm_list_free(pkglist
);
219 for (i
= 0; i
< n
; i
++)
221 name
= cachepkg
[i
]->name
;
222 if (strcmp(name
, current
))
225 hit
= bsearch(cachepkg
+ i
, localpkg
, m
,
226 sizeof(struct pkginfo
*), pkgnamecomp
);
227 count
= hit
? 0 : args
.preserve
;
229 if (!hit
|| alpm_pkg_vercmp((*hit
)->version
,
230 cachepkg
[i
]->version
))
233 if (count
>= args
.preserve
)
235 strcpy(cachedir
+ len
, cachepkg
[i
]->filename
);
238 printf("remove: %s\n", cachepkg
[i
]->filename
);
239 total_size
+= get_file_size(cachedir
);
248 printf("\ntotal: %"PRIuMAX
" bytes\n", (uintmax_t)total_size
);
250 free_pkginfo_array(cachepkg
, n
);
251 free_pkginfo_array(localpkg
, m
);
253 alpm_release(handle
);
254 regfree(&pkgnametest
);
255 regfree(&pkgnamesplit
);