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

Contents of /portage-utils/qdepends.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.67 - (show annotations) (download) (as text)
Tue Feb 18 07:26:14 2014 UTC (8 months ago) by vapier
Branch: MAIN
Changes since 1.66: +2 -10 lines
File MIME type: text/x-csrc
drop support for old style virtuals as the tree has not carried them in a long time

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.66 2013/09/29 22:19:39 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.66 2013/09/29 22:19:39 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 memcpy(buf + *pos, root->info, len);
362 *pos += len+1;
363 buf[*pos-1] = ' ';
364 }
365 if (root->children) _dep_flatten_tree(root->children, buf, pos);
366 this_node_sucks:
367 if (root->neighbor) _dep_flatten_tree(root->neighbor, buf, pos);
368 }
369
370 char *dep_flatten_tree(const dep_node *root)
371 {
372 static char flat[8192];
373 size_t pos = 0;
374 _dep_flatten_tree(root, flat, &pos);
375 if (pos == 0) {
376 /* all the nodes were squashed ... for example:
377 * USE=-selinux RDEPEND="selinux? ( sys-libs/libselinux )"
378 */
379 return NULL;
380 }
381 flat[pos-1] = '\0';
382 return flat;
383 }
384
385 struct qdepends_opt_state {
386 int argc;
387 char **argv;
388 const char *depend_file;
389 const char *query;
390 };
391
392 _q_static int qdepends_main_vdb_cb(q_vdb_pkg_ctx *pkg_ctx, void *priv)
393 {
394 struct qdepends_opt_state *state = priv;
395 const char *catname = pkg_ctx->cat_ctx->name;
396 const char *pkgname = pkg_ctx->name;
397 size_t len;
398 int i;
399 char *ptr;
400 char buf[_Q_PATH_MAX];
401 char depend[65536], use[8192];
402 dep_node *dep_tree;
403
404 /* see if this cat/pkg is requested */
405 for (i = optind; i < state->argc; ++i) {
406 snprintf(buf, sizeof(buf), "%s/%s", catname, pkgname);
407 if (rematch(state->argv[i], buf, REG_EXTENDED) == 0)
408 break;
409 if (rematch(state->argv[i], pkgname, REG_EXTENDED) == 0)
410 break;
411 }
412 if (i == state->argc)
413 return 0;
414
415 IF_DEBUG(warn("matched %s/%s", catname, pkgname));
416
417 if (!eat_file_at(pkg_ctx->fd, state->depend_file, depend, sizeof(depend)))
418 return 0;
419
420 IF_DEBUG(warn("growing tree..."));
421 dep_tree = dep_grow_tree(depend);
422 if (dep_tree == NULL)
423 return 0;
424 IF_DEBUG(puts(depend));
425 IF_DEBUG(dep_dump_tree(dep_tree));
426
427 if (qdep_name_only) {
428 depend_atom *atom = NULL;
429 snprintf(buf, sizeof(buf), "%s/%s", catname, pkgname);
430 if ((atom = atom_explode(buf)) != NULL) {
431 printf("%s%s/%s%s%s: ", BOLD, catname, BLUE, atom->PN, NORM);
432 atom_implode(atom);
433 }
434 } else {
435 printf("%s%s/%s%s%s: ", BOLD, catname, BLUE, pkgname, NORM);
436 }
437
438 if (!eat_file_at(pkg_ctx->fd, "USE", use, sizeof(use))) {
439 warn("Could not eat_file(%s), you'll prob have incorrect output", buf);
440 } else {
441 for (ptr = use; *ptr; ++ptr)
442 if (*ptr == '\n' || *ptr == '\t')
443 *ptr = ' ';
444 len = strlen(use);
445 assert(len+1 < sizeof(use));
446 use[len] = ' ';
447 use[len+1] = '\0';
448 memmove(use+1, use, len);
449 use[0] = ' ';
450
451 dep_prune_use(dep_tree, use);
452 }
453
454 /*dep_dump_tree(dep_tree);*/
455 ptr = dep_flatten_tree(dep_tree);
456 printf("%s\n", (ptr == NULL ? "" : ptr));
457
458 dep_burn_tree(dep_tree);
459
460 return 1;
461 }
462
463 _q_static int qdepends_vdb_deep_cb(q_vdb_pkg_ctx *pkg_ctx, void *priv)
464 {
465 struct qdepends_opt_state *state = priv;
466 const char *catname = pkg_ctx->cat_ctx->name;
467 const char *pkgname = pkg_ctx->name;
468 size_t len;
469 char *ptr;
470 char buf[_Q_PATH_MAX];
471 char depend[16384], use[8192];
472 dep_node *dep_tree;
473
474 IF_DEBUG(warn("matched %s/%s for %s", catname, pkgname, state->depend_file));
475
476 if (!eat_file_at(pkg_ctx->fd, state->depend_file, depend, sizeof(depend)))
477 return 0;
478
479 IF_DEBUG(warn("growing tree..."));
480 dep_tree = dep_grow_tree(depend);
481 if (dep_tree == NULL)
482 return 0;
483 IF_DEBUG(puts(depend));
484 IF_DEBUG(dep_dump_tree(dep_tree));
485
486 if (eat_file_at(pkg_ctx->fd, "USE", use, sizeof(use)) == 1)
487 use[0] = ' ';
488
489 for (ptr = use; *ptr; ++ptr)
490 if (*ptr == '\n' || *ptr == '\t')
491 *ptr = ' ';
492 len = strlen(use);
493 assert(len+1 < sizeof(use));
494 use[len] = ' ';
495 use[len+1] = '\0';
496 memmove(use+1, use, len);
497 use[0] = ' ';
498
499 dep_prune_use(dep_tree, use);
500
501 ptr = dep_flatten_tree(dep_tree);
502 if (ptr && rematch(state->query, ptr, REG_EXTENDED) == 0) {
503 if (qdep_name_only) {
504 depend_atom *atom = NULL;
505 snprintf(buf, sizeof(buf), "%s/%s", catname, pkgname);
506 if ((atom = atom_explode(buf)) != NULL) {
507 printf("%s%s/%s%s%s%c", BOLD, catname, BLUE, atom->PN, NORM, verbose ? ':' : '\n');
508 atom_implode(atom);
509 }
510 } else {
511 printf("%s%s/%s%s%s%c", BOLD, catname, BLUE, pkgname, NORM, verbose ? ':' : '\n');
512 }
513 if (verbose)
514 printf(" %s\n", ptr);
515 }
516 dep_burn_tree(dep_tree);
517
518 return 1;
519 }
520
521 int qdepends_main(int argc, char **argv)
522 {
523 struct qdepends_opt_state state = {
524 .argc = argc,
525 .argv = argv,
526 };
527 q_vdb_pkg_cb *cb;
528 int i, ret;
529 bool do_format = false;
530 const char *query = NULL;
531 const char *depend_file;
532 const char *depend_files[] = { "DEPEND", "RDEPEND", "PDEPEND", NULL, NULL };
533
534 depend_file = depend_files[0];
535
536 DBG("argc=%d argv[0]=%s argv[1]=%s",
537 argc, argv[0], argc > 1 ? argv[1] : "NULL?");
538
539 while ((i = GETOPT_LONG(QDEPENDS, qdepends, "")) != -1) {
540 switch (i) {
541 COMMON_GETOPTS_CASES(qdepends)
542
543 case 'd': depend_file = depend_files[0]; break;
544 case 'r': depend_file = depend_files[1]; break;
545 case 'p': depend_file = depend_files[2]; break;
546 case 'k': depend_file = optarg; break;
547 case 'a': depend_file = NULL; break;
548 case 'Q': query = optarg; break;
549 case 'N': qdep_name_only = 1; break;
550 case 'f': do_format = true; break;
551 }
552 }
553 if ((argc == optind) && (query == NULL) && !do_format)
554 qdepends_usage(EXIT_FAILURE);
555
556 if (do_format) {
557 while (optind < argc) {
558 if (!dep_print_depend(stdout, argv[optind++]))
559 return EXIT_FAILURE;
560 if (optind < argc)
561 fprintf(stdout, "\n");
562 }
563 return EXIT_SUCCESS;
564 }
565
566 state.depend_file = depend_file;
567 state.query = query;
568 cb = query ? qdepends_vdb_deep_cb : qdepends_main_vdb_cb;
569
570 if (!depend_file) {
571 ret = 0;
572 for (i = 0; depend_files[i]; ++i) {
573 printf(" %s*%s %s\n", GREEN, NORM, depend_files[i]);
574 state.depend_file = depend_files[i];
575 ret |= q_vdb_foreach_pkg(cb, &state, NULL);
576 }
577 } else
578 ret = q_vdb_foreach_pkg(cb, &state, NULL);
579
580 if (!ret && !quiet)
581 warn("no matches found for your query");
582 return ret ? EXIT_SUCCESS : EXIT_FAILURE;
583 }
584
585 #else
586 DEFINE_APPLET_STUB(qdepends)
587 #endif

  ViewVC Help
Powered by ViewVC 1.1.20