2 * Copyright (c) 2007, Novell Inc.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
17 #include "tools_util.h"
18 #include "repo_susetags.h"
24 struct parsedata_common common;
25 int last_found_source;
28 Id (*dirs)[3]; // dirid, size, nfiles
30 Id langcache[ID_NUM_INTERNAL];
33 static char *flagtab[] = {
44 langtag(struct parsedata *pd, Id tag, const char *language)
49 if (!language || tag >= ID_NUM_INTERNAL)
51 if (!pd->langcache[tag])
53 tagname = id2str(pd->repo->pool, tag);
54 p = sat_malloc(strlen(tagname) + strlen(language) + 2);
55 sprintf(p, "%s:%s", tagname, language);
56 pd->langcache[tag] = str2id(pd->repo->pool, p, 1);
59 return pd->langcache[tag];
64 * create and add dependency
68 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker, char *kind)
74 i = split(line + 5, sp, 4); /* name, <op>, evr, ? */
75 if (i != 1 && i != 3) /* expect either 'name' or 'name' <op> 'evr' */
77 fprintf(stderr, "Bad dependency line: %s\n", line);
81 id = str2id(pool, join2(kind, ":", sp[0]), 1);
83 id = str2id(pool, sp[0], 1);
86 evrid = makeevr(pool, sp[2]);
87 for (flags = 0; flags < 6; flags++)
88 if (!strcmp(sp[1], flagtab[flags]))
92 fprintf(stderr, "Unknown relation '%s'\n", sp[1]);
95 id = rel2id(pool, id, evrid, flags + 1, 1);
97 return repo_addid_dep(pd->repo, olddeps, id, marker);
107 add_location(struct parsedata *pd, char *line, Solvable *s, unsigned entry)
109 Pool *pool = s->repo->pool;
113 i = split(line, sp, 3);
114 if (i != 2 && i != 3)
116 fprintf(stderr, "Bad location line: %s\n", line);
119 /* If we have a dirname, let's see if it's the same as arch. In that
120 case don't store it. */
121 if (i == 3 && !strcmp (sp[2], id2str (pool, s->arch)))
125 /* medianr filename dir
126 don't optimize this one */
127 repodata_set_constant(pd->data, entry, SOLVABLE_MEDIANR, atoi(sp[0]));
128 repodata_set_poolstr(pd->data, entry, SOLVABLE_MEDIADIR, sp[2]);
129 repodata_set_str(pd->data, entry, SOLVABLE_MEDIAFILE, sp[1]);
134 /* Let's see if we can optimize this a bit. If the media file name
135 can be formed by the base rpm information we don't store it, but
136 only a flag that we've seen it. */
137 unsigned int medianr = atoi (sp[0]);
138 const char *n1 = sp[1];
139 const char *n2 = id2str (pool, s->name);
140 for (n2 = id2str (pool, s->name); *n2; n1++, n2++)
143 if (*n2 || *n1 != '-')
147 for (n2 = id2str (pool, s->evr); *n2; n1++, n2++)
150 if (*n2 || *n1 != '.')
153 for (n2 = id2str (pool, s->arch); *n2; n1++, n2++)
156 if (*n2 || strcmp (n1, ".rpm"))
159 repodata_set_constant(pd->data, entry, SOLVABLE_MEDIANR, medianr);
160 repodata_set_void(pd->data, entry, SOLVABLE_MEDIADIR);
161 repodata_set_void(pd->data, entry, SOLVABLE_MEDIAFILE);
165 repodata_set_constant(pd->data, entry, SOLVABLE_MEDIANR, medianr);
166 repodata_set_void(pd->data, entry, SOLVABLE_MEDIADIR);
167 repodata_set_str(pd->data, entry, SOLVABLE_MEDIAFILE, sp[1]);
178 add_source(struct parsedata *pd, char *line, Solvable *s, unsigned entry)
180 Repo *repo = s->repo;
181 Pool *pool = repo->pool;
184 if (split(line, sp, 5) != 4)
186 fprintf(stderr, "Bad source line: %s\n", line);
190 Id name = str2id(pool, sp[0], 1);
191 Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
192 Id arch = str2id(pool, sp[3], 1);
193 /* XXX: could record a dep here, depends on where we want to store the data */
195 repodata_set_void(pd->data, entry, SOLVABLE_SOURCENAME);
197 repodata_set_id(pd->data, entry, SOLVABLE_SOURCENAME, name);
199 repodata_set_void(pd->data, entry, SOLVABLE_SOURCEEVR);
201 repodata_set_id(pd->data, entry, SOLVABLE_SOURCEEVR, evr);
202 repodata_set_constantid(pd->data, entry, SOLVABLE_SOURCEARCH, arch);
207 * add a line with directory information
212 add_dirline (struct parsedata *pd, char *line)
215 if (split(line, sp, 6) != 5)
217 pd->dirs = sat_extend(pd->dirs, pd->ndirs, 1, sizeof(pd->dirs[0]), 31);
218 long filesz = strtol (sp[1], 0, 0);
219 filesz += strtol (sp[2], 0, 0);
220 long filenum = strtol (sp[3], 0, 0);
221 filenum += strtol (sp[4], 0, 0);
222 /* hack: we know that there's room for a / */
225 unsigned dirid = repodata_str2dir(pd->data, sp[0], 1);
227 fprintf(stderr, "%s -> %d\n", sp[0], dirid);
229 pd->dirs[pd->ndirs][0] = dirid;
230 pd->dirs[pd->ndirs][1] = filesz;
231 pd->dirs[pd->ndirs][2] = filenum;
243 id3_cmp (const void *v1, const void *v2)
247 return i1[0] - i2[0];
257 commit_diskusage (struct parsedata *pd, unsigned entry)
260 Dirpool *dp = &pd->data->dirpool;
261 /* Now sort in dirid order. This ensures that parents come before
264 qsort(pd->dirs, pd->ndirs, sizeof (pd->dirs[0]), id3_cmp);
265 /* Substract leaf numbers from all parents to make the numbers
266 non-cumulative. This must be done post-order (i.e. all leafs
267 adjusted before parents). We ensure this by starting at the end of
268 the array moving to the start, hence seeing leafs before parents. */
269 for (i = pd->ndirs; i--;)
271 unsigned p = dirpool_parent(dp, pd->dirs[i][0]);
273 for (; p; p = dirpool_parent(dp, p))
276 if (pd->dirs[j][0] == p)
280 if (pd->dirs[j][1] < pd->dirs[i][1])
283 pd->dirs[j][1] -= pd->dirs[i][1];
284 if (pd->dirs[j][2] < pd->dirs[i][2])
287 pd->dirs[j][2] -= pd->dirs[i][2];
290 /* Haven't found this parent in the list, look further if
291 we maybe find the parents parent. */
298 unsigned slen = sizeof (sbuf);
299 for (i = 0; i < pd->ndirs; i++)
301 dir2str (attr, pd->dirs[i][0], &buf, &slen);
302 fprintf (stderr, "have dir %d %d %d %s\n", pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2], buf);
307 for (i = 0; i < pd->ndirs; i++)
308 if (pd->dirs[i][1] || pd->dirs[i][2])
310 repodata_add_dirnumnum(pd->data, entry, SOLVABLE_DISKUSAGE, pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2]);
316 /* Unfortunately "a"[0] is no constant expression in the C languages,
317 so we need to pass the four characters individually :-/ */
318 #define CTAG(a,b,c,d) ((unsigned)(((unsigned char)a) << 24) \
319 | ((unsigned char)b << 16) \
320 | ((unsigned char)c << 8) \
321 | ((unsigned char)d))
328 static inline unsigned
329 tag_from_string (char *cs)
331 unsigned char *s = (unsigned char*) cs;
332 return ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]);
338 * Parse susetags file passed in fp, fill solvables into repo
340 * susetags is key,value based
344 * for long (multi-line) values,
351 * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata
352 * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/packages
353 * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/pattern
356 * All keys have 3 characters and end in ':'
360 finish_solvable(struct parsedata *pd, Solvable *s, int last_found_pack)
362 Pool *pool = pd->repo->pool;
365 /* move file provides to filelist */
366 /* relies on the fact that rpm inserts self-provides at the end */
369 Id *p, *lastreal, did;
370 const char *str, *sp;
371 lastreal = pd->repo->idarraydata + s->provides;
372 for (p = lastreal; *p; p++)
375 for (p = lastreal; *p; p++)
377 str = id2str(pool, *p);
383 for (p = lastreal; *p; p++)
385 str = id2str(pool, *p);
386 sp = strrchr(str, '/');
389 char *sdup = strdup(str);
391 did = repodata_str2dir(pd->data, sdup, 1);
397 strncpy(sdup, str, sp - str);
399 did = repodata_str2dir(pd->data, sdup, 1);
401 repodata_add_dirstr(pd->data, last_found_pack, SOLVABLE_FILELIST, did, sp + 1);
407 /* A self provide, except for source packages. This is harmless
408 to do twice (in case we see the same package twice). */
409 if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
410 s->provides = repo_addid_dep(pd->repo, s->provides,
411 rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
412 /* XXX This uses repo_addid_dep internally, so should also be
413 harmless to do twice. */
414 s->supplements = repo_fix_legacy(pd->repo, s->provides, s->supplements);
416 commit_diskusage (pd, last_found_pack);
420 repo_add_susetags(Repo *repo, FILE *fp, Id vendor, const char *language, int flags)
422 Pool *pool = repo->pool;
429 int last_found_pack = 0;
434 if ((flags & SUSETAGS_EXTEND) && repo->nrepodata)
437 /* use last repodata */
438 data = repo->repodata + repo->nrepodata - 1;
440 data = repo_add_repodata(repo, 0);
442 memset(&pd, 0, sizeof(pd));
446 pd.repo = pd.common.repo = repo;
448 pd.common.pool = pool;
456 * collect values in 'struct parsedata pd'
457 * then build .solv (and .attr) file
463 if (linep - line + 16 > aline) /* (re-)alloc buffer */
465 aline = linep - line;
466 line = realloc(line, aline + 512);
467 linep = line + aline;
470 if (!fgets(linep, aline - (linep - line), fp)) /* read line */
472 linep += strlen(linep);
473 if (linep == line || linep[-1] != '\n')
479 /* check for multi-line value tags (+Key:/-Key:) */
481 int is_end = (linep[-intag - 2] == '-')
482 && (linep[-1] == ':')
483 && !strncmp(linep - 1 - intag, line + 1, intag)
484 && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - 3] == '\n');
485 if (cummulate && !is_end)
490 if (cummulate && is_end)
492 linep[-intag - 2] = 0;
493 if (linep[-intag - 3] == '\n')
494 linep[-intag - 3] = 0;
498 if (!cummulate && is_end)
504 if (!cummulate && !is_end)
505 linep = line + intag + 3;
510 if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
512 char *tagend = strchr(line, ':');
515 fprintf(stderr, "bad line: %s\n", line);
518 intag = tagend - (line + 1);
520 switch (tag_from_string (line)) /* check if accumulation is needed */
522 case CTAG('+', 'P', 'r', 'q'):
523 case CTAG('+', 'P', 'r', 'c'):
524 case CTAG('+', 'P', 's', 'g'):
525 if (!pd.kind || !(flags & SUSETAGS_KINDS_SEPARATELY))
527 case CTAG('+', 'D', 'e', 's'):
528 case CTAG('+', 'E', 'u', 'l'):
529 case CTAG('+', 'I', 'n', 's'):
530 case CTAG('+', 'D', 'e', 'l'):
531 case CTAG('+', 'A', 'u', 't'):
535 line[0] = '='; /* handle lines between +Key:/-Key: as =Key: */
536 line[intag + 2] = ' ';
537 linep = line + intag + 3;
540 if (*line == '#' || !*line)
542 if (! (line[0] && line[1] && line[2] && line[3] && line[4] == ':'))
544 tag = tag_from_string (line);
548 * start of (next) package or pattern
550 * =Pkg: <name> <version> <release> <architecture>
554 if ((tag == CTAG('=', 'P', 'k', 'g')
555 || tag == CTAG('=', 'P', 'a', 't')))
558 /* If we have an old solvable, complete it by filling in some
561 finish_solvable(&pd, s, last_found_pack);
575 if (split(line + 5, sp, 5) != 4)
577 fprintf(stderr, "Bad line: %s\n", line);
580 /* Lookup (but don't construct) the name and arch. */
582 name = str2id(pool, join2(pd.kind, ":", sp[0]), 0);
584 name = str2id(pool, sp[0], 0);
585 arch = str2id(pool, sp[3], 0);
586 evr = makeevr(pool, join2(sp[1], "-", sp[2]));
590 /* Now see if we know this solvable already. If we found neither
591 the name nor the arch at all in this repo
592 there's no chance of finding the exact solvable either. */
593 if (indesc >= 2 && name && arch)
596 /* Now look for a solvable with the given name,evr,arch.
597 Our input is structured so, that the second set of =Pkg
598 lines comes in roughly the same order as the first set, so we
599 have a hint at where to start our search, namely were we found
601 for (n = repo->start, nn = n + last_found_pack; n < repo->end; n++, nn++)
605 s = pool->solvables + nn;
606 if (s->repo == repo && s->name == name && s->evr == evr && s->arch == arch)
612 last_found_pack = nn - repo->start;
615 /* And if we still don't have a solvable, create a new one. */
618 s = pool_id2solvable(pool, repo_add_solvable(repo));
619 last_found_pack = (s - pool->solvables) - repo->start;
621 repodata_extend(data, s - pool->solvables);
625 s->name = str2id(pool, join2(pd.kind, ":", sp[0]), 1);
627 s->name = str2id(pool, sp[0], 1);
632 s->arch = str2id(pool, sp[3], 1);
637 /* If we have no current solvable to add to, ignore all further lines
638 for it. Probably invalid input data in the second set of
640 if (indesc >= 2 && !s)
642 fprintf (stderr, "Huh %s?\n", line);
647 case CTAG('=', 'P', 'r', 'v'): /* provides */
648 s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
650 case CTAG('=', 'R', 'e', 'q'): /* requires */
651 s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
653 case CTAG('=', 'P', 'r', 'q'): /* pre-requires / packages required */
656 if (flags & SUSETAGS_KINDS_SEPARATELY)
657 repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:must", 1), line + 6);
659 s->requires = adddep(pool, &pd, s->requires, line, 0, 0); /* patterns: a required package */
662 s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
664 case CTAG('=', 'O', 'b', 's'): /* obsoletes */
665 s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
667 case CTAG('=', 'C', 'o', 'n'): /* conflicts */
668 s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
670 case CTAG('=', 'R', 'e', 'c'): /* recommends */
671 s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
673 case CTAG('=', 'S', 'u', 'p'): /* supplements */
674 s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
676 case CTAG('=', 'E', 'n', 'h'): /* enhances */
677 s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
679 case CTAG('=', 'S', 'u', 'g'): /* suggests */
680 s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
682 case CTAG('=', 'F', 'r', 'e'): /* freshens */
683 s->freshens = adddep(pool, &pd, s->freshens, line, 0, pd.kind);
685 case CTAG('=', 'P', 'r', 'c'): /* packages recommended */
686 if (flags & SUSETAGS_KINDS_SEPARATELY)
687 repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:should", 1), line + 6);
689 s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
691 case CTAG('=', 'P', 's', 'g'): /* packages suggested */
692 if (flags & SUSETAGS_KINDS_SEPARATELY)
693 repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:may", 1), line + 6);
695 s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
697 case CTAG('=', 'P', 'c', 'n'): /* pattern: package conflicts */
698 if (flags & SUSETAGS_KINDS_SEPARATELY)
699 fprintf (stderr, "Unsupported: pattern -> package conflicts\n");
701 s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
703 case CTAG('=', 'P', 'o', 'b'): /* pattern: package obsoletes */
704 if (flags & SUSETAGS_KINDS_SEPARATELY)
705 fprintf (stderr, "Unsupported: pattern -> package obsoletes\n");
707 s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
709 case CTAG('=', 'P', 'f', 'r'): /* pattern: package freshens */
710 if (flags & SUSETAGS_KINDS_SEPARATELY)
711 fprintf (stderr, "Unsupported: pattern -> package freshens\n");
713 s->freshens = adddep(pool, &pd, s->freshens, line, 0, 0);
715 case CTAG('=', 'P', 's', 'p'): /* pattern: package supplements */
716 if (flags & SUSETAGS_KINDS_SEPARATELY)
717 fprintf (stderr, "Unsupported: pattern -> package supplements\n");
719 s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
721 case CTAG('=', 'P', 'e', 'n'): /* pattern: package enhances */
722 if (flags & SUSETAGS_KINDS_SEPARATELY)
723 fprintf (stderr, "Unsupported: pattern -> package enhances\n");
725 s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
727 case CTAG('=', 'V', 'e', 'r'): /* - version - */
732 /* From here it's the attribute tags. */
733 case CTAG('=', 'G', 'r', 'p'):
734 repodata_set_poolstr(data, last_found_pack, SOLVABLE_GROUP, line + 6);
736 case CTAG('=', 'L', 'i', 'c'):
737 repodata_set_poolstr(data, last_found_pack, SOLVABLE_LICENSE, line + 6);
739 case CTAG('=', 'L', 'o', 'c'):
740 add_location(&pd, line + 6, s, last_found_pack);
742 case CTAG('=', 'S', 'r', 'c'):
743 add_source(&pd, line + 6, s, last_found_pack);
745 case CTAG('=', 'S', 'i', 'z'):
746 if (split (line + 6, sp, 3) == 2)
748 repodata_set_num(data, last_found_pack, SOLVABLE_DOWNLOADSIZE, (atoi(sp[0]) + 1023) / 1024);
749 repodata_set_num(data, last_found_pack, SOLVABLE_INSTALLSIZE, (atoi(sp[1]) + 1023) / 1024);
752 case CTAG('=', 'T', 'i', 'm'):
754 unsigned int t = atoi (line + 6);
756 repodata_set_num(data, last_found_pack, SOLVABLE_BUILDTIME, t);
759 case CTAG('=', 'K', 'w', 'd'):
760 repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_KEYWORDS, line + 6);
762 case CTAG('=', 'A', 'u', 't'):
763 repodata_set_str(data, last_found_pack, SOLVABLE_AUTHORS, line + 6);
765 case CTAG('=', 'S', 'u', 'm'):
766 repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_SUMMARY, language), line + 6);
768 case CTAG('=', 'D', 'e', 's'):
769 repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_DESCRIPTION, language), line + 6);
771 case CTAG('=', 'E', 'u', 'l'):
772 repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_EULA, language), line + 6);
774 case CTAG('=', 'I', 'n', 's'):
775 repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_MESSAGEINS, language), line + 6);
777 case CTAG('=', 'D', 'e', 'l'):
778 repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_MESSAGEDEL, language), line + 6);
780 case CTAG('=', 'V', 'i', 's'):
782 /* Accept numbers and textual bools. */
785 if (k || !strcasecmp (line + 6, "true"))
786 repodata_set_constant(data, last_found_pack, SOLVABLE_ISVISIBLE, 1);
789 case CTAG('=', 'S', 'h', 'r'):
790 if (last_found_pack >= pd.nshare)
794 pd.share_with = realloc (pd.share_with, (last_found_pack + 256) * sizeof (*pd.share_with));
795 memset (pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof (*pd.share_with));
798 pd.share_with = calloc (last_found_pack + 256, sizeof (*pd.share_with));
799 pd.nshare = last_found_pack + 256;
801 pd.share_with[last_found_pack] = strdup (line + 6);
803 case CTAG('=', 'D', 'i', 'r'):
804 add_dirline (&pd, line + 6);
806 case CTAG('=', 'C', 'a', 't'):
807 repodata_set_poolstr(data, last_found_pack, SOLVABLE_CATEGORY, line + 6);
809 case CTAG('=', 'O', 'r', 'd'):
810 /* Order is a string not a number, so we can retroactively insert
811 new patterns in the middle, i.e. 1 < 15 < 2. */
812 repodata_set_str(data, last_found_pack, SOLVABLE_ORDER, line + 6);
814 case CTAG('=', 'I', 'c', 'o'):
815 repodata_set_str(data, last_found_pack, SOLVABLE_ICON, line + 6);
817 case CTAG('=', 'E', 'x', 't'):
818 repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_EXTENDS, line + 6);
820 case CTAG('=', 'I', 'n', 'c'):
821 repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_INCLUDES, line + 6);
824 case CTAG('=', 'P', 'a', 't'):
825 case CTAG('=', 'P', 'k', 'g'):
835 finish_solvable(&pd, s, last_found_pack);
838 * (e.g. multiple binaries built from same source)
844 for (i = 0; i < pd.nshare; i++)
845 if (pd.share_with[i])
847 if (split(pd.share_with[i], sp, 5) != 4)
849 fprintf(stderr, "Bad =Shr line: %s\n", pd.share_with[i]);
853 Id name = str2id(pool, sp[0], 1);
854 Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
855 Id arch = str2id(pool, sp[3], 1);
858 for (n = repo->start, nn = repo->start + last_found;
859 n < repo->end; n++, nn++)
863 found = pool->solvables + nn;
864 if (found->repo == repo
865 && found->name == name
867 && found->arch == arch)
869 last_found = nn - repo->start;
874 repodata_merge_attrs(data, i, last_found);
876 free (pd.share_with);
880 repodata_internalize(data);