]> pd.if.org Git - zpackage/blob - zpm-search.c
general search improvements
[zpackage] / zpm-search.c
1 #define _POSIX_C_SOURCE 200809L
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5
6 #include <glob.h>
7
8 #include "zpm.h"
9 #include "lib/jsw/jsw_hlib.h"
10 #include "lib/jsw/jsw_atree.h"
11
12 /*
13  * find packages, and lib deps
14  */
15
16 struct pkgloc {
17         char *id;
18         char *file;
19         int info;
20 };
21
22 struct search_ctl {
23         char *localdb, *repodir, *pkgdir;
24         struct zpm *zpmdb;
25         glob_t repos;
26         int matchallpkgfile;
27 };
28
29 char *pathcat(char *dir, char *path) {
30         size_t dirlen = 0, pathlen = 0;
31         char *cat;
32
33         /* chop off trailing / on dir */
34         if (dir) {
35                 dirlen = strlen(dir);
36                 while (dirlen && dir[dirlen-1] == '/') {
37                         dirlen--;
38                 }
39         }
40
41         if (path) {
42                 pathlen = strlen(path);
43                 while (*path && *path == '/') {
44                         path++;
45                         pathlen--;
46                 }
47         }
48
49         cat = malloc(dirlen + pathlen + 2);
50         if (cat) {
51                 strncpy(cat, dir, dirlen);
52                 cat[dirlen] = '/';
53                 strcpy(cat+dirlen+1, path);
54         }
55         return cat;
56 }
57
58 char *checkfile(char *pkgstr, char *path) {
59         struct zpm pkgfile;
60         char *pkgid = 0;
61
62         if (!zpm_open(&pkgfile, path)) {
63                 return NULL;
64         }
65         
66         pkgid = zpm_findpkg(&pkgfile, pkgstr, "hash is not null");
67         zpm_close(&pkgfile);
68
69         return pkgid;
70 }
71
72 char *checkfileforlib(char *soname, char *path) {
73         struct zpm pkgfile;
74         char *pkgid = 0;
75
76         if (!zpm_open(&pkgfile, path)) {
77                 return NULL;
78         }
79         
80         pkgid = zpm_findlib(&pkgfile, soname, "hash is not null");
81         if (pkgfile.error) {
82                 fprintf(stderr, "sql error: %s\n", pkgfile.errmsg);
83         }
84         zpm_close(&pkgfile);
85
86         return pkgid;
87 }
88
89 int find_globs(struct search_ctl *opt) {
90         glob_t *repos;
91         int rv;
92
93         repos = &opt->repos;
94         repos->gl_offs = 0;
95
96         int globopts = 0;
97
98         if (opt->repodir) {
99                 char *globstr = pathcat(opt->repodir, "*.repo");
100                 rv = glob(globstr, globopts, NULL, repos);
101                 switch (rv) {
102                         case GLOB_NOSPACE:
103                                 fprintf(stderr, "glob no mem\n");
104                                 return 0;
105                         case GLOB_ABORTED:
106                                 fprintf(stderr, "glob abort\n");
107                                 return 0;
108                         case GLOB_NOMATCH:
109                                 break;
110                         case 0:
111                                 break;
112                         default:
113                                 break;
114                 }
115                 globopts = GLOB_APPEND;
116                 free(globstr);
117         }
118
119         if (opt->pkgdir) {
120                 char *globstr = pathcat(opt->pkgdir, "*.zpm");
121                 rv = glob(globstr, globopts, NULL, repos);
122                 switch (rv) {
123                         case GLOB_NOSPACE:
124                                 fprintf(stderr, "glob no mem\n");
125                                 return 0;
126                         case GLOB_ABORTED:
127                                 fprintf(stderr, "glob abort\n");
128                                 return 0;
129                         case GLOB_NOMATCH:
130                                 break;
131                         case 0:
132                                 break;
133                         default:
134                                 break;
135                 }
136                 globopts = GLOB_APPEND;
137                 free(globstr);
138         }
139
140         return 1;
141 }
142
143 int find_lib(char *soname, struct search_ctl *opt, struct pkgloc *pkg) {
144         char *latest = 0;
145         char *installed = 0, *found = 0;
146         char *pkgfile = 0;
147         int rv;
148         unsigned int i;
149
150         if (opt->localdb) {
151                 installed = zpm_findlib(opt->zpmdb, soname, "status = 'installed'");
152                 if (installed) {
153                         /* we're done, the lib is installed */
154                         return 2;
155                 }
156         }
157
158         for (i = 0; i < opt->repos.gl_pathc; i++) {
159                 found = checkfileforlib(soname, opt->repos.gl_pathv[i]);
160                 if (found) {
161                         rv = zpm_vercmp(found, latest);
162                         if (rv == 1) {
163                                 latest = found;
164                                 free(pkgfile);
165                                 pkgfile = strdup(opt->repos.gl_pathv[i]);
166                         }
167                 }
168         }
169
170         if (latest && pkgfile) {
171                 pkg->id = strdup(latest);
172                 pkg->file = pkgfile;
173                 return 1;
174         }
175
176         return 0;
177 }
178
179 struct pkgloc *find_package(char *pkgstr, struct search_ctl *opt) {
180         char *latest = 0;
181         char *installed = 0, *found = 0;
182         char *pkgfile;
183         struct pkgloc *pkg = 0;
184         int rv;
185         unsigned int i;
186
187         if (opt->localdb) {
188                 installed = zpm_findpkg(opt->zpmdb, pkgstr, "status = 'installed'");
189                 if (installed) {
190                         latest = installed;
191                 }
192                 found = zpm_findpkg(opt->zpmdb, pkgstr, NULL);
193                 if (found) {
194                         rv = zpm_vercmp(found, latest);
195                         if (rv == 1) {
196                                 latest = found;
197                                 pkgfile = opt->zpmdb->path;
198                         }
199                 }
200         }
201
202         for (i = 0; i < opt->repos.gl_pathc; i++) {
203                 if (!opt->matchallpkgfile
204                                 && !strstr(opt->repos.gl_pathv[i], pkgstr)
205                                 && !strstr(opt->repos.gl_pathv[i], ".repo")
206                                 ) {
207                         continue;
208                 }
209                 found = checkfile(pkgstr, opt->repos.gl_pathv[i]);
210                 if (found) {
211                         rv = zpm_vercmp(found, latest);
212                         if (rv == 1) {
213                                 latest = found;
214                                 pkgfile = strdup(opt->repos.gl_pathv[i]);
215                         }
216                 }
217         }
218
219         if (latest && pkgfile) {
220                 pkg = malloc(sizeof *pkg);
221                 pkg->id = strdup(latest);
222                 pkg->file = pkgfile;
223         }
224
225         return pkg;
226 }
227
228 char *pkglibsneeded = "\
229 with pkglibs as (\
230                 select distinct EN.needed as soname, PF.pkgid\
231                 from elfneeded EN\
232                 join packagefiles_pkgid PF on PF.hash = EN.file\
233                 ),\
234      pkgprovides as (\
235                      select distinct EL.soname, PF.pkgid\
236                      from elflibraries EL\
237                      join packagefiles_pkgid PF on PF.hash = EL.file\
238                     )\
239      select distinct PL.soname, PP.soname is not null as selfsatisfied\
240      from pkglibs PL\
241      left join pkgprovides PP on PL.pkgid = PP.pkgid and PL.soname = PP.soname\
242      where PL.pkgid = %Q\
243      ";
244
245 void checklibs(struct search_ctl *opts,
246                 jsw_hash_t *check, jsw_hash_t *forlibs, jsw_hash_t *nfound) {
247         char *pkgid = 0, *pkgfile = 0;
248         struct zpm src;
249
250         /* checked_libs is a list of checked sonames
251          * checked is a list of checked packages
252          * needed is list of libs for package
253          */
254         jsw_atree_t *needed = 0, *checked, *checked_libs;
255
256         int libs;
257         jsw_atrav_t *i;
258         char *soname;
259         
260         checked = jsw_anew((cmp_f)strcmp, (dup_f)strdup, (rel_f)free);
261         checked_libs = jsw_anew((cmp_f)strcmp, (dup_f)strdup, (rel_f)free);
262
263         while (jsw_hsize(check) > 0) {
264                 free(pkgid);
265                 free(pkgfile);
266
267                 jsw_hreset(check);
268
269                 pkgid = strdup(jsw_hkey(check));
270                 pkgfile = strdup(jsw_hitem(check));
271
272                 if (!pkgid || !pkgfile) {
273                         break;
274                 }
275
276                 jsw_herase(check, pkgid);
277
278                 if (jsw_afind(checked, pkgid)) {
279                         /* already checked this one */
280                         continue;
281                 }
282
283                 /* we do this now so we catch self deps */
284                 jsw_ainsert(checked, pkgid);
285
286                 /* get the libraries needed by this package */
287                 if (!zpm_open(&src, pkgfile)) {
288                         fprintf(stderr, "can't zpm open %s\n", pkgfile);
289                         break;
290                 }
291
292                 if (needed) jsw_adelete(needed);
293                 needed = jsw_anew((cmp_f)strcmp, (dup_f)strdup, (rel_f)free);
294                 libs = zpm_libraries_needed(&src, pkgid, needed);
295                 zpm_close(&src);
296
297                 if (libs == 0) {
298                         continue;
299                 }
300
301                 i = jsw_atnew();
302
303                 for (soname = jsw_atfirst(i, needed); soname; soname = jsw_atnext(i)) {
304                         int found;
305                         struct pkgloc pkginfo;
306
307                         /* if it's in checked_libs, we've already looked at this one */
308                         if (jsw_afind(checked_libs, soname)) {
309                                 continue;
310                         }
311
312                         /* haven't found this soname */
313                         jsw_ainsert(checked_libs, soname);
314
315                         found = find_lib(soname, opts, &pkginfo);
316                         if (!found) {
317                                 if (!jsw_hinsert(nfound, soname, pkgid)) {
318                                         fprintf(stderr,"insert error\n");
319                                 }
320                                 continue;
321                         }
322
323                         if (found == 1) {
324                                 /* if not alreacy checked, add to check */
325                                 if (!jsw_afind(checked, pkginfo.file)) {
326                                         jsw_hinsert(check,pkginfo.id,pkginfo.file);
327                                 }
328                                 /* shouldn't be in there already */
329                                 jsw_hinsert(forlibs, pkginfo.id, pkginfo.file);
330                         } 
331                         /* otherwise found == 2, so just continue */
332                         if (found) {
333                                 free(pkginfo.file);
334                                 free(pkginfo.id);
335                         }
336                 }
337         }
338         free(pkgid);
339         free(pkgfile);
340 }
341
342 void print_pkghash(jsw_hash_t *hash, int json) {
343         const char *pkgid, *file;
344         char *fmt;
345         char *sep;
346         int count = 0;
347
348         fmt = json ? "\"%s\": \"%s\"" : "%s:%s";
349         sep = json ? ", " : "\n";
350
351         if (json) {
352                 printf("{ ");
353         }
354
355         if (jsw_hsize(hash) > 0) {
356                 for (jsw_hreset(hash); jsw_hitem(hash); jsw_hnext(hash)) {
357                         pkgid = jsw_hkey(hash);
358                         file = jsw_hitem(hash);
359                         if (count++) {
360                                 printf("%s", sep);
361                         }
362                         printf(fmt, pkgid, file);
363                 }
364         }
365         if (json) printf(" }");
366         printf("\n");
367 }
368
369 int main(int ac, char *av[]) {
370         int option, argn;
371         int findlibs = 0, json = 0;
372         struct pkgloc *found;
373         jsw_hash_t *packages, *check, *forlibs, *nolib;
374         jsw_atree_t *nfound;
375         struct search_ctl opt = { 0 };
376         struct zpm localdb;
377
378         opt.localdb = getenv("ZPMDB");
379         if (!opt.localdb) opt.localdb = "/var/lib/zpm/local.db";
380         opt.pkgdir = getenv("ZPM_PACKAGE_DIR");
381         if (!opt.pkgdir) opt.pkgdir = "/var/lib/zpm/packages";
382         opt.repodir = getenv("ZPM_REPO_DIR");
383         if (!opt.repodir) opt.repodir = "/var/lib/zpm/repo/";
384
385         /* -l also find packages needed for libs
386          * -j output json
387          *  -q quiet - suppress output
388          *  -v verbose - 
389          *  -d debug - trace each step
390          *
391          *  environment:
392          *  ZPMDB - path to localdb, /var/lib/zpm/local.db
393          *  ZPM_REPO_DIR - path to *.repo files, '/var/lib/zpm/repos'
394          *  ZPM_PACKAGE_DIRS - : separated paths to *.zpm files,
395          *  '/var/lib/zpm/packages'
396          *  ZPM_ROOT_DIR :- prepends to above paths
397          *
398          *  -M (missing) invert the output and only output missing
399          *  outputs
400          *  packages found (suppress with -P)
401          *  package strings not found (suppress with -S)
402          *  library dependency packages needed and found (suppress with -N)
403          *  library dependency packages already installed (suppress with -H)
404          *  library dependency packages in install set (suppress with -I)
405          *  library dependency sonames not found (suppress with -L)
406          *
407          *  exit value is 0 if no missing libs and no bad package strings
408          *  exit value is non zero otherwise
409          */
410
411         int output = 1;
412         while ((option = getopt(ac, av, "ljqPRDp:r:d:M")) != -1) {
413                 switch (option) {
414                         case 'l': findlibs = 1; break;
415                         case 'j': json = 1; break;
416                         case 'q': output = 0;
417                         case 'd': opt.localdb = optarg; break;
418                         case 'p': opt.pkgdir = optarg; break;
419                         case 'r': opt.repodir = optarg; break;
420                         case 'P': opt.pkgdir = 0; break;
421                         case 'R': opt.repodir = 0; break;
422                         case 'D': opt.localdb = 0; break;
423                         case 'M': opt.matchallpkgfile = 1; break;
424                         default:
425                                   exit(EXIT_FAILURE);
426                                   break;
427                 }
428         }
429         argn = optind;
430
431         if (opt.localdb) {
432                 if (zpm_open(&localdb, NULL)) {
433                         opt.zpmdb = &localdb;
434                 } else {
435                         fprintf(stderr, "can't open localdb\n");
436                         return 2;
437                 }
438         }
439
440
441         if (!find_globs(&opt)) {
442                 fprintf(stderr, "bad globs\n");
443                 return 3;
444         }
445
446         packages = jsw_hnew(ac,NULL,(cmp_f)strcmp,(keydup_f)strdup,
447                         (itemdup_f)strdup,free,free);
448         check = jsw_hnew(ac,NULL,(cmp_f)strcmp,(keydup_f)strdup,
449                         (itemdup_f)strdup,free,free);
450         forlibs = jsw_hnew(ac,NULL,(cmp_f)strcmp,(keydup_f)strdup,
451                         (itemdup_f)strdup,free,free);
452         nolib = jsw_hnew(ac,NULL,(cmp_f)strcmp,(keydup_f)strdup,
453                         (itemdup_f)strdup,free,free);
454         nfound = jsw_anew((cmp_f)strcmp, (dup_f)strdup, (rel_f)free);
455
456         int arg;
457         for (arg = argn; arg < ac; arg++) {
458                 found = find_package(av[arg], &opt);
459                 if (found) {
460                         jsw_hinsert(packages, found->id, found->file);
461                         jsw_hinsert(check, found->id, found->file);
462                         free(found->id);
463                         free(found->file);
464                         free(found);
465                 } else {
466                         jsw_ainsert(nfound, av[arg]);
467                 }
468
469         }
470
471         if (findlibs) {
472                 checklibs(&opt, check, forlibs, nolib);
473         }
474
475         if (output) {
476                 print_pkghash(packages, json);
477                 if (jsw_hsize(forlibs)) {
478                         print_pkghash(forlibs, json);
479                 }
480         }
481
482         if (jsw_hsize(nolib) > 0) {
483                 for (jsw_hreset(nolib); jsw_hitem(nolib); jsw_hnext(nolib)) {
484                         const char *pkgid, *file;
485                         pkgid = jsw_hkey(nolib);
486                         file = jsw_hitem(nolib);
487                         fprintf(stderr, "no lib found %s:%s\n", pkgid, file);
488                 }
489         }
490
491         if (jsw_asize(nfound) > 0) {
492                 jsw_atrav_t *i;
493                 i = jsw_atnew();
494                 char *pkgstr;
495
496                 for (pkgstr = jsw_atfirst(i, nfound); pkgstr; pkgstr = jsw_atnext(i)) {
497                         fprintf(stderr, "%s: not found\n", pkgstr);
498                 }
499         }
500
501         return jsw_asize(nfound) > 0 || jsw_hsize(nolib) > 0 ? 1 : 0;
502 }