1 /*
2  *  files.c
3  *
4  *  Copyright (c) 2015-2018 Pacman Development Team <pacman-dev@archlinux.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <alpm.h>
21 #include <alpm_list.h>
22 #include <regex.h>
23 
24 /* pacman */
25 #include "pacman.h"
26 #include "util.h"
27 #include "conf.h"
28 #include "package.h"
29 
print_line_machinereadable(alpm_db_t * db,alpm_pkg_t * pkg,char * filename)30 static void print_line_machinereadable(alpm_db_t *db, alpm_pkg_t *pkg, char *filename)
31 {
32 	/* Fields are repo, pkgname, pkgver, filename separated with \0 */
33 	fputs(alpm_db_get_name(db), stdout);
34 	fputc(0, stdout);
35 	fputs(alpm_pkg_get_name(pkg), stdout);
36 	fputc(0, stdout);
37 	fputs(alpm_pkg_get_version(pkg), stdout);
38 	fputc(0, stdout);
39 	fputs(filename, stdout);
40 	fputs("\n", stdout);
41 }
42 
dump_pkg_machinereadable(alpm_db_t * db,alpm_pkg_t * pkg)43 static void dump_pkg_machinereadable(alpm_db_t *db, alpm_pkg_t *pkg)
44 {
45 	alpm_filelist_t *pkgfiles = alpm_pkg_get_files(pkg);
46 	for(size_t filenum = 0; filenum < pkgfiles->count; filenum++) {
47 		const alpm_file_t *file = pkgfiles->files + filenum;
48 		print_line_machinereadable(db, pkg, file->name);
49 	}
50 }
51 
files_fileowner(alpm_list_t * syncs,alpm_list_t * targets)52 static int files_fileowner(alpm_list_t *syncs, alpm_list_t *targets) {
53 	int ret = 0;
54 	alpm_list_t *t;
55 
56 	for(t = targets; t; t = alpm_list_next(t)) {
57 		char *filename = t->data;
58 		int found = 0;
59 		alpm_list_t *s;
60 		size_t len = strlen(filename);
61 
62 		while(len > 1 && filename[0] == '/') {
63 			filename++;
64 			len--;
65 		}
66 
67 		for(s = syncs; s; s = alpm_list_next(s)) {
68 			alpm_list_t *p;
69 			alpm_db_t *repo = s->data;
70 			alpm_list_t *packages = alpm_db_get_pkgcache(repo);
71 
72 			for(p = packages; p; p = alpm_list_next(p)) {
73 				alpm_pkg_t *pkg = p->data;
74 				alpm_filelist_t *files = alpm_pkg_get_files(pkg);
75 
76 				if(alpm_filelist_contains(files, filename)) {
77 					if(config->op_f_machinereadable) {
78 						print_line_machinereadable(repo, pkg, filename);
79 					} else if(!config->quiet) {
80 						const colstr_t *colstr = &config->colstr;
81 						printf(_("%s is owned by %s%s/%s%s %s%s%s\n"), filename,
82 								colstr->repo, alpm_db_get_name(repo), colstr->title,
83 								alpm_pkg_get_name(pkg), colstr->version,
84 								alpm_pkg_get_version(pkg), colstr->nocolor);
85 					} else {
86 						printf("%s/%s\n", alpm_db_get_name(repo), alpm_pkg_get_name(pkg));
87 					}
88 
89 					found = 1;
90 				}
91 			}
92 		}
93 
94 		if(!found) {
95 			ret++;
96 		}
97 	}
98 
99 	return 0;
100 }
101 
files_search(alpm_list_t * syncs,alpm_list_t * targets,int regex)102 static int files_search(alpm_list_t *syncs, alpm_list_t *targets, int regex) {
103 	int ret = 0;
104 	alpm_list_t *t;
105 	const colstr_t *colstr = &config->colstr;
106 
107 	for(t = targets; t; t = alpm_list_next(t)) {
108 		char *targ = t->data;
109 		alpm_list_t *s;
110 		int found = 0;
111 		regex_t reg;
112 
113 		if(regex) {
114 			if(regcomp(&reg, targ, REG_EXTENDED | REG_NOSUB | REG_ICASE | REG_NEWLINE) != 0) {
115 				/* TODO: error message */
116 				goto notfound;
117 			}
118 		}
119 
120 		for(s = syncs; s; s = alpm_list_next(s)) {
121 			alpm_list_t *p;
122 			alpm_db_t *repo = s->data;
123 			alpm_list_t *packages = alpm_db_get_pkgcache(repo);
124 			int m;
125 
126 			for(p = packages; p; p = alpm_list_next(p)) {
127 				size_t f = 0;
128 				char* c;
129 				alpm_pkg_t *pkg = p->data;
130 				alpm_filelist_t *files = alpm_pkg_get_files(pkg);
131 				alpm_list_t *match = NULL;
132 
133 				while(f < files->count) {
134 					c = strrchr(files->files[f].name, '/');
135 					if(c && *(c + 1)) {
136 						if(regex) {
137 							m = regexec(&reg, (c + 1), 0, 0, 0);
138 						} else {
139 							m = strcmp(c + 1, targ);
140 						}
141 						if(m == 0) {
142 							match = alpm_list_add(match, files->files[f].name);
143 							found = 1;
144 						}
145 					}
146 					f++;
147 				}
148 
149 				if(match != NULL) {
150 					if(config->op_f_machinereadable) {
151 						alpm_list_t *ml;
152 						for(ml = match; ml; ml = alpm_list_next(ml)) {
153 							char *filename = ml->data;
154 							print_line_machinereadable(repo, pkg, filename);
155 						}
156 					} else if(config->quiet) {
157 						printf("%s/%s\n", alpm_db_get_name(repo), alpm_pkg_get_name(pkg));
158 					} else {
159 						alpm_list_t *ml;
160 						printf("%s%s/%s%s %s%s%s\n", colstr->repo, alpm_db_get_name(repo),
161 							colstr->title, alpm_pkg_get_name(pkg),
162 							colstr->version, alpm_pkg_get_version(pkg), colstr->nocolor);
163 
164 						for(ml = match; ml; ml = alpm_list_next(ml)) {
165 							c = ml->data;
166 							printf("    %s\n", c);
167 						}
168 					}
169 					alpm_list_free(match);
170 				}
171 			}
172 		}
173 
174 		if(regex) {
175 			regfree(&reg);
176 		}
177 
178 notfound:
179 		if(!found) {
180 			ret++;
181 		}
182 	}
183 
184 	return 0;
185 }
186 
dump_file_list(alpm_pkg_t * pkg)187 static void dump_file_list(alpm_pkg_t *pkg) {
188 	const char *pkgname;
189 	alpm_filelist_t *pkgfiles;
190 	size_t i;
191 
192 	pkgname = alpm_pkg_get_name(pkg);
193 	pkgfiles = alpm_pkg_get_files(pkg);
194 
195 	for(i = 0; i < pkgfiles->count; i++) {
196 		const alpm_file_t *file = pkgfiles->files + i;
197 		/* Regular: '<pkgname> <filepath>\n'
198 		 * Quiet  : '<filepath>\n'
199 		 */
200 		if(!config->quiet) {
201 			printf("%s%s%s ", config->colstr.title, pkgname, config->colstr.nocolor);
202 		}
203 		printf("%s\n", file->name);
204 	}
205 
206 	fflush(stdout);
207 }
208 
files_list(alpm_list_t * syncs,alpm_list_t * targets)209 static int files_list(alpm_list_t *syncs, alpm_list_t *targets) {
210 	alpm_list_t *i, *j;
211 	int ret = 0;
212 
213 	if(targets != NULL) {
214 		for(i = targets; i; i = alpm_list_next(i)) {
215 			int found = 0;
216 			char *targ = i->data;
217 			char *repo = NULL;
218 			char *c = strchr(targ, '/');
219 
220 			if(c) {
221 				if(! *(c + 1)) {
222 					pm_printf(ALPM_LOG_ERROR,
223 						_("invalid package: '%s'\n"), targ);
224 					ret += 1;
225 					continue;
226 				}
227 
228 				repo = strndup(targ, c - targ);
229 				targ = c + 1;
230 			}
231 
232 			for(j = syncs; j; j = alpm_list_next(j)) {
233 				alpm_pkg_t *pkg;
234 				alpm_db_t *db = j->data;
235 
236 				if(repo) {
237 					if(strcmp(alpm_db_get_name(db), repo) != 0) {
238 						continue;
239 					}
240 				}
241 
242 				if((pkg = alpm_db_get_pkg(db, targ)) != NULL) {
243 					found = 1;
244 					if(config->op_f_machinereadable) {
245 						dump_pkg_machinereadable(db, pkg);
246 					} else {
247 						dump_file_list(pkg);
248 					}
249 					break;
250 				}
251 			}
252 			if(!found) {
253 				targ = i->data;
254 				pm_printf(ALPM_LOG_ERROR,
255 						_("package '%s' was not found\n"), targ);
256 				ret += 1;
257 			}
258 			free(repo);
259 		}
260 	} else {
261 		for(i = syncs; i; i = alpm_list_next(i)) {
262 		alpm_db_t *db = i->data;
263 
264 			for(j = alpm_db_get_pkgcache(db); j; j = alpm_list_next(j)) {
265 				alpm_pkg_t *pkg = j->data;
266 				if(config->op_f_machinereadable) {
267 					dump_pkg_machinereadable(db, pkg);
268 				} else {
269 					dump_file_list(pkg);
270 				}
271 			}
272 		}
273 	}
274 
275 	return ret;
276 }
277 
278 
pacman_files(alpm_list_t * targets)279 int pacman_files(alpm_list_t *targets)
280 {
281 	alpm_list_t *files_dbs = NULL;
282 
283 	if(check_syncdbs(1, 0)) {
284 		return 1;
285 	}
286 
287 	files_dbs = alpm_get_syncdbs(config->handle);
288 
289 	if(config->op_s_sync) {
290 		/* grab a fresh package list */
291 		colon_printf(_("Synchronizing package databases...\n"));
292 		alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
293 				"synchronizing package lists\n");
294 		if(!sync_syncdbs(config->op_s_sync, files_dbs)) {
295 			return 1;
296 		}
297 	}
298 
299 	if(targets == NULL && (config->op_q_owns | config->op_s_search)) {
300 		pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
301 		return 1;
302 	}
303 
304 	/* determine the owner of a file */
305 	if(config->op_q_owns) {
306 		return files_fileowner(files_dbs, targets);
307 	}
308 
309 	/* search for a file */
310 	if(config->op_s_search) {
311 		return files_search(files_dbs, targets, config->op_f_regex);
312 	}
313 
314 	/* get a listing of files in sync DBs */
315 	if(config->op_q_list) {
316 		return files_list(files_dbs, targets);
317 	}
318 
319 	if(targets != NULL) {
320 		pm_printf(ALPM_LOG_ERROR, _("no options specified (use -h for help)\n"));
321 		return 1;
322 	}
323 
324 	return 0;
325 }
326