/[gentoo-projects]/portage-utils/qdepends.c
Gentoo

Contents of /portage-utils/qdepends.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.66 - (show annotations) (download) (as text)
Sun Sep 29 22:19:39 2013 UTC (9 months, 1 week ago) by vapier
Branch: MAIN
Changes since 1.65: +12 -10 lines
File MIME type: text/x-csrc
qdepends: warn if no matches were found #459970

1 /*
2 * Copyright 2005-2013 Gentoo Foundation
3 * Distributed under the terms of the GNU General Public License v2
4 * $Header: /var/cvsroot/gentoo-projects/portage-utils/qdepends.c,v 1.65 2013/09/29 10:36:08 vapier Exp $
5 *
6 * Copyright 2005-2010 Ned Ludd - <solar@gentoo.org>
7 * Copyright 2005-2013 Mike Frysinger - <vapier@gentoo.org>
8 */
9
10 #ifdef APPLET_qdepends
11
12 #define QDEPENDS_FLAGS "drpafNk:Q:" COMMON_FLAGS
13 static struct option const qdepends_long_opts[] = {
14 {"depend", no_argument, NULL, 'd'},
15 {"rdepend", no_argument, NULL, 'r'},
16 {"pdepend", no_argument, NULL, 'p'},
17 {"key", a_argument, NULL, 'k'},
18 {"query", a_argument, NULL, 'Q'},
19 {"name-only", no_argument, NULL, 'N'},
20 {"all", no_argument, NULL, 'a'},
21 {"format", no_argument, NULL, 'f'},
22 COMMON_LONG_OPTS
23 };
24 static const char * const qdepends_opts_help[] = {
25 "Show DEPEND info (default)",
26 "Show RDEPEND info",
27 "Show PDEPEND info",
28 "User defined vdb key",
29 "Query reverse deps",
30 "Only show package name",
31 "Show all DEPEND info",
32 "Pretty format specified depend strings",
33 COMMON_OPTS_HELP
34 };
35 static const char qdepends_rcsid[] = "$Id: qdepends.c,v 1.65 2013/09/29 10:36:08 vapier Exp $";
36 #define qdepends_usage(ret) usage(ret, QDEPENDS_FLAGS, qdepends_long_opts, qdepends_opts_help, lookup_applet_idx("qdepends"))
37
38 static char qdep_name_only = 0;
39
40 /* structures / types / etc ... */
41 typedef enum {
42 DEP_NULL = 0,
43 DEP_NORM = 1,
44 DEP_USE = 2,
45 DEP_OR = 3,
46 DEP_GROUP = 4
47 } dep_type;
48 static const char * const _dep_names[] = { "NULL", "NORM", "USE", "OR", "GROUP" };
49
50 struct _dep_node {
51 dep_type type;
52 char *info;
53 char info_on_heap;
54 depend_atom *atom;
55 struct _dep_node *parent;
56 struct _dep_node *neighbor;
57 struct _dep_node *children;
58 };
59 typedef struct _dep_node dep_node;
60
61 static const dep_node null_node = {
62 .type = DEP_NULL,
63 };
64
65 /* prototypes */
66 #ifdef NDEBUG
67 # define dep_dump_tree(r)
68 #else
69 # define dep_dump_tree(r) _dep_print_tree(stdout, r, 0)
70 #endif
71 _q_static void _dep_print_tree(FILE *fp, const dep_node *root, size_t space);
72 void dep_burn_tree(dep_node *root);
73 char *dep_flatten_tree(const dep_node *root);
74 _q_static void _dep_attach(dep_node *root, dep_node *attach_me, int type);
75 _q_static void _dep_flatten_tree(const dep_node *root, char *buf, size_t *pos);
76 _q_static void _dep_burn_node(dep_node *node);
77 int qdepends_main_vdb(const char *depend_file, int argc, char **argv);
78 int qdepends_vdb_deep(const char *depend_file, const char *query);
79
80 #ifdef EBUG
81 _q_static void print_word(const char *ptr, size_t num)
82 {
83 while (num--)
84 printf("%c", *ptr++);
85 printf("\n");
86 }
87 #endif
88
89 _q_static dep_node *
90 _dep_grow_node(dep_type type, const char *info, size_t info_len)
91 {
92 dep_node *ret;
93 size_t len;
94
95 if (type == DEP_OR || type == DEP_GROUP)
96 info = NULL;
97
98 len = sizeof(*ret);
99 if (info) {
100 if (!info_len)
101 info_len = strlen(info);
102 len += info_len + 1;
103 }
104 ret = xzalloc(len);
105
106 ret->type = type;
107 if (info) {
108 ret->info = ((char*)ret) + sizeof(*ret);
109 memcpy(ret->info, info, info_len);
110 if (type == DEP_NORM)
111 ret->atom = atom_explode(info);
112 }
113
114 return ret;
115 }
116
117 void _dep_burn_node(dep_node *node)
118 {
119 assert(node);
120 if (node->info_on_heap) free(node->info);
121 if (node->atom) atom_implode(node->atom);
122 free(node);
123 }
124
125 enum {
126 _DEP_NEIGH = 1,
127 _DEP_CHILD = 2
128 };
129
130 void _dep_attach(dep_node *root, dep_node *attach_me, int type)
131 {
132 if (type == _DEP_NEIGH) {
133 if (!root->neighbor) {
134 root->neighbor = attach_me;
135 attach_me->parent = root->parent;
136 } else
137 _dep_attach(root->neighbor, attach_me, _DEP_NEIGH);
138 } else {
139 if (!root->children) {
140 root->children = attach_me;
141 attach_me->parent = root;
142 } else
143 _dep_attach(root->children, attach_me, _DEP_NEIGH);
144 }
145 }
146
147 _q_static dep_node *dep_grow_tree(const char *depend)
148 {
149 bool saw_whitespace;
150 signed long paren_balanced;
151 const char *ptr, *word;
152 int curr_attach;
153 dep_node *ret, *curr_node, *new_node;
154 dep_type prev_type;
155
156 ret = curr_node = new_node = NULL;
157 prev_type = DEP_NULL;
158 paren_balanced = 0;
159 curr_attach = _DEP_NEIGH;
160 word = NULL;
161
162 #define _maybe_consume_word(t) \
163 do { \
164 if (!word) break; \
165 /*printf("Found word:%i ", curr_attach);*/ \
166 /*print_word(word, ptr-word);*/ \
167 new_node = _dep_grow_node(t, word, ptr-word); \
168 if (!ret) \
169 ret = curr_node = new_node; \
170 else { \
171 _dep_attach(curr_node, new_node, curr_attach); \
172 curr_attach = _DEP_NEIGH; \
173 curr_node = new_node; \
174 } \
175 prev_type = t; \
176 word = NULL; \
177 } while (0)
178
179 saw_whitespace = true;
180 for (ptr = depend; *ptr; ++ptr) {
181 if (isspace(*ptr)) {
182 saw_whitespace = true;
183 _maybe_consume_word(DEP_NORM);
184 continue;
185 }
186
187 switch (*ptr) {
188 case '?': {
189 if (word == NULL) {
190 warnf("Found a ? but no USE flag");
191 goto error_out;
192 }
193 _maybe_consume_word(DEP_USE);
194 curr_attach = _DEP_CHILD;
195 continue;
196 }
197 case '|': {
198 if (!saw_whitespace)
199 break;
200 if (ptr[1] != '|') {
201 warnf("Found a | but not ||");
202 goto error_out;
203 }
204 word = ptr++;
205 _maybe_consume_word(DEP_OR);
206 curr_attach = _DEP_CHILD;
207 continue;
208 }
209 case '(': {
210 ++paren_balanced;
211 if (!saw_whitespace)
212 break;
213 if (prev_type == DEP_OR || prev_type == DEP_USE) {
214 _maybe_consume_word(DEP_NORM);
215 prev_type = DEP_NULL;
216 } else {
217 if (word) {
218 warnf("New group has word in queue");
219 goto error_out;
220 }
221 word = ptr;
222 _maybe_consume_word(DEP_GROUP);
223 curr_attach = _DEP_CHILD;
224 }
225 break;
226 }
227 case ')': {
228 --paren_balanced;
229 if (!saw_whitespace)
230 break;
231 _maybe_consume_word(DEP_NORM);
232
233 if (curr_node->parent == NULL) {
234 warnf("Group lacks a parent");
235 goto error_out;
236 }
237 curr_node = curr_node->parent;
238 curr_attach = _DEP_NEIGH;
239 break;
240 }
241 default:
242 if (!word)
243 word = ptr;
244 }
245 saw_whitespace = false;
246
247 /* fall through to the paren failure below */
248 if (paren_balanced < 0)
249 break;
250 }
251
252 if (paren_balanced != 0) {
253 warnf("Parenthesis unbalanced");
254 goto error_out;
255 }
256
257 /* if the depend buffer wasnt terminated with a space,
258 * we may have a word sitting in the buffer to consume */
259 _maybe_consume_word(DEP_NORM);
260
261 #undef _maybe_consume_word
262
263 return ret ? : xmemdup(&null_node, sizeof(null_node));
264
265 error_out:
266 warnf("DEPEND: %s", depend);
267 if (ret) {
268 dep_dump_tree(ret);
269 dep_burn_tree(ret);
270 }
271 return NULL;
272 }
273
274 _q_static void _dep_print_tree(FILE *fp, const dep_node *root, size_t space)
275 {
276 size_t s;
277
278 assert(root);
279 if (root->type == DEP_NULL)
280 goto this_node_sucks;
281
282 for (s = space; s; --s)
283 fprintf(fp, "\t");
284
285 if (verbose > 1)
286 fprintf(fp, "Node [%s]: ", _dep_names[root->type]);
287 /*printf("Node %p [%s] %p %p %p: ", root, _dep_names[root->type], root->parent, root->neighbor, root->children);*/
288 if (root->type == DEP_OR)
289 fprintf(fp, "|| (");
290 if (root->info) {
291 fprintf(fp, "%s", root->info);
292 /* If there is only one child, be nice to one-line: foo? ( pkg ) */
293 if (root->type == DEP_USE)
294 fprintf(fp, "? (");
295 }
296 fprintf(fp, "\n");
297
298 if (root->children)
299 _dep_print_tree(fp, root->children, space+1);
300
301 if (root->type == DEP_OR || root->type == DEP_USE) {
302 for (s = space; s; --s)
303 fprintf(fp, "\t");
304 fprintf(fp, ")\n");
305 }
306 this_node_sucks:
307 if (root->neighbor)
308 _dep_print_tree(fp, root->neighbor, space);
309 }
310
311 _q_static bool dep_print_depend(FILE *fp, const char *depend)
312 {
313 dep_node *dep_tree;
314
315 dep_tree = dep_grow_tree(depend);
316 if (dep_tree == NULL)
317 return false;
318
319 if (!quiet)
320 fprintf(fp, "DEPEND=\"\n");
321
322 _dep_print_tree(fp, dep_tree, 1);
323
324 dep_burn_tree(dep_tree);
325 if (!quiet)
326 fprintf(fp, "\"\n");
327
328 return true;
329 }
330
331 void dep_burn_tree(dep_node *root)
332 {
333 assert(root);
334 if (root->children) dep_burn_tree(root->children);
335 if (root->neighbor) dep_burn_tree(root->neighbor);
336 _dep_burn_node(root);
337 }
338
339 _q_static void dep_prune_use(dep_node *root, const char *use)
340 {
341 if (root->neighbor) dep_prune_use(root->neighbor, use);
342 if (root->type == DEP_USE) {
343 char *useflag = NULL;
344 int notfound, invert = (root->info[0] == '!' ? 1 : 0);
345 xasprintf(&useflag, " %s ", root->info+invert);
346 notfound = (strstr(use, useflag) == NULL ? 1 : 0);
347 free(useflag);
348 if (notfound ^ invert) {
349 root->type = DEP_NULL;
350 return;
351 }
352 }
353 if (root->children) dep_prune_use(root->children, use);
354 }
355
356 void _dep_flatten_tree(const dep_node *root, char *buf, size_t *pos)
357 {
358 if (root->type == DEP_NULL) goto this_node_sucks;
359 if (root->type == DEP_NORM) {
360 size_t len = strlen(root->info);
361 #if 1
362 if (*root->info == 'v')
363 if (strncmp(root->info, "virtual/", 8) == 0) {
364 if (virtuals == NULL)
365 virtuals = resolve_virtuals();
366 IF_DEBUG(fprintf(stderr, "(%s->%s)", root->info, virtual(root->info, virtuals)));
367 }
368 #endif
369 memcpy(buf + *pos, root->info, len);
370 *pos += len+1;
371 buf[*pos-1] = ' ';
372 }
373 if (root->children) _dep_flatten_tree(root->children, buf, pos);
374 this_node_sucks:
375 if (root->neighbor) _dep_flatten_tree(root->neighbor, buf, pos);
376 }
377
378 char *dep_flatten_tree(const dep_node *root)
379 {
380 static char flat[8192];
381 size_t pos = 0;
382 _dep_flatten_tree(root, flat, &pos);
383 if (pos == 0) {
384 /* all the nodes were squashed ... for example:
385 * USE=-selinux RDEPEND="selinux? ( sys-libs/libselinux )"
386 */
387 return NULL;
388 }
389 flat[pos-1] = '\0';
390 return flat;
391 }
392
393 struct qdepends_opt_state {
394 int argc;
395 char **argv;
396 const char *depend_file;
397 const char *query;
398 };
399
400 _q_static int qdepends_main_vdb_cb(q_vdb_pkg_ctx *pkg_ctx, void *priv)
401 {
402 struct qdepends_opt_state *state = priv;
403 const char *catname = pkg_ctx->cat_ctx->name;
404 const char *pkgname = pkg_ctx->name;
405 size_t len;
406 int i;
407 char *ptr;
408 char buf[_Q_PATH_MAX];
409 char depend[65536], use[8192];
410 dep_node *dep_tree;
411
412 /* see if this cat/pkg is requested */
413 for (i = optind; i < state->argc; ++i) {
414 snprintf(buf, sizeof(buf), "%s/%s", catname, pkgname);
415 if (rematch(state->argv[i], buf, REG_EXTENDED) == 0)
416 break;
417 if (rematch(state->argv[i], pkgname, REG_EXTENDED) == 0)
418 break;
419 }
420 if (i == state->argc)
421 return 0;
422
423 IF_DEBUG(warn("matched %s/%s", catname, pkgname));
424
425 if (!eat_file_at(pkg_ctx->fd, state->depend_file, depend, sizeof(depend)))
426 return 0;
427
428 IF_DEBUG(warn("growing tree..."));
429 dep_tree = dep_grow_tree(depend);
430 if (dep_tree == NULL)
431 return 0;
432 IF_DEBUG(puts(depend));
433 IF_DEBUG(dep_dump_tree(dep_tree));
434
435 if (qdep_name_only) {
436 depend_atom *atom = NULL;
437 snprintf(buf, sizeof(buf), "%s/%s", catname, pkgname);
438 if ((atom = atom_explode(buf)) != NULL) {
439 printf("%s%s/%s%s%s: ", BOLD, catname, BLUE, atom->PN, NORM);
440 atom_implode(atom);
441 }
442 } else {
443 printf("%s%s/%s%s%s: ", BOLD, catname, BLUE, pkgname, NORM);
444 }
445
446 if (!eat_file_at(pkg_ctx->fd, "USE", use, sizeof(use))) {
447 warn("Could not eat_file(%s), you'll prob have incorrect output", buf);
448 } else {
449 for (ptr = use; *ptr; ++ptr)
450 if (*ptr == '\n' || *ptr == '\t')
451 *ptr = ' ';
452 len = strlen(use);
453 assert(len+1 < sizeof(use));
454 use[len] = ' ';
455 use[len+1] = '\0';
456 memmove(use+1, use, len);
457 use[0] = ' ';
458
459 dep_prune_use(dep_tree, use);
460 }
461
462 /*dep_dump_tree(dep_tree);*/
463 ptr = dep_flatten_tree(dep_tree);
464 printf("%s\n", (ptr == NULL ? "" : ptr));
465
466 dep_burn_tree(dep_tree);
467
468 return 1;
469 }
470
471 _q_static int qdepends_vdb_deep_cb(q_vdb_pkg_ctx *pkg_ctx, void *priv)
472 {
473 struct qdepends_opt_state *state = priv;
474 const char *catname = pkg_ctx->cat_ctx->name;
475 const char *pkgname = pkg_ctx->name;
476 size_t len;
477 char *ptr;
478 char buf[_Q_PATH_MAX];
479 char depend[16384], use[8192];
480 dep_node *dep_tree;
481
482 IF_DEBUG(warn("matched %s/%s for %s", catname, pkgname, state->depend_file));
483
484 if (!eat_file_at(pkg_ctx->fd, state->depend_file, depend, sizeof(depend)))
485 return 0;
486
487 IF_DEBUG(warn("growing tree..."));
488 dep_tree = dep_grow_tree(depend);
489 if (dep_tree == NULL)
490 return 0;
491 IF_DEBUG(puts(depend));
492 IF_DEBUG(dep_dump_tree(dep_tree));
493
494 if (eat_file_at(pkg_ctx->fd, "USE", use, sizeof(use)) == 1)
495 use[0] = ' ';
496
497 for (ptr = use; *ptr; ++ptr)
498 if (*ptr == '\n' || *ptr == '\t')
499 *ptr = ' ';
500 len = strlen(use);
501 assert(len+1 < sizeof(use));
502 use[len] = ' ';
503 use[len+1] = '\0';
504 memmove(use+1, use, len);
505 use[0] = ' ';
506
507 dep_prune_use(dep_tree, use);
508
509 ptr = dep_flatten_tree(dep_tree);
510 if (ptr && rematch(state->query, ptr, REG_EXTENDED) == 0) {
511 if (qdep_name_only) {
512 depend_atom *atom = NULL;
513 snprintf(buf, sizeof(buf), "%s/%s", catname, pkgname);
514 if ((atom = atom_explode(buf)) != NULL) {
515 printf("%s%s/%s%s%s%c", BOLD, catname, BLUE, atom->PN, NORM, verbose ? ':' : '\n');
516 atom_implode(atom);
517 }
518 } else {
519 printf("%s%s/%s%s%s%c", BOLD, catname, BLUE, pkgname, NORM, verbose ? ':' : '\n');
520 }
521 if (verbose)
522 printf(" %s\n", ptr);
523 }
524 dep_burn_tree(dep_tree);
525
526 return 1;
527 }
528
529 int qdepends_main(int argc, char **argv)
530 {
531 struct qdepends_opt_state state = {
532 .argc = argc,
533 .argv = argv,
534 };
535 q_vdb_pkg_cb *cb;
536 int i, ret;
537 bool do_format = false;
538 const char *query = NULL;
539 const char *depend_file;
540 const char *depend_files[] = { "DEPEND", "RDEPEND", "PDEPEND", NULL, NULL };
541
542 depend_file = depend_files[0];
543
544 DBG("argc=%d argv[0]=%s argv[1]=%s",
545 argc, argv[0], argc > 1 ? argv[1] : "NULL?");
546
547 while ((i = GETOPT_LONG(QDEPENDS, qdepends, "")) != -1) {
548 switch (i) {
549 COMMON_GETOPTS_CASES(qdepends)
550
551 case 'd': depend_file = depend_files[0]; break;
552 case 'r': depend_file = depend_files[1]; break;
553 case 'p': depend_file = depend_files[2]; break;
554 case 'k': depend_file = optarg; break;
555 case 'a': depend_file = NULL; break;
556 case 'Q': query = optarg; break;
557 case 'N': qdep_name_only = 1; break;
558 case 'f': do_format = true; break;
559 }
560 }
561 if ((argc == optind) && (query == NULL) && !do_format)
562 qdepends_usage(EXIT_FAILURE);
563
564 if (do_format) {
565 while (optind < argc) {
566 if (!dep_print_depend(stdout, argv[optind++]))
567 return EXIT_FAILURE;
568 if (optind < argc)
569 fprintf(stdout, "\n");
570 }
571 return EXIT_SUCCESS;
572 }
573
574 state.depend_file = depend_file;
575 state.query = query;
576 cb = query ? qdepends_vdb_deep_cb : qdepends_main_vdb_cb;
577
578 if (!depend_file) {
579 ret = 0;
580 for (i = 0; depend_files[i]; ++i) {
581 printf(" %s*%s %s\n", GREEN, NORM, depend_files[i]);
582 state.depend_file = depend_files[i];
583 ret |= q_vdb_foreach_pkg(cb, &state, NULL);
584 }
585 } else
586 ret = q_vdb_foreach_pkg(cb, &state, NULL);
587
588 if (!ret && !quiet)
589 warn("no matches found for your query");
590 return ret ? EXIT_SUCCESS : EXIT_FAILURE;
591 }
592
593 #else
594 DEFINE_APPLET_STUB(qdepends)
595 #endif

  ViewVC Help
Powered by ViewVC 1.1.20