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 handle)
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, handle, SOLVABLE_MEDIANR, atoi(sp[0]));
128 repodata_set_poolstr(pd->data, handle, SOLVABLE_MEDIADIR, sp[2]);
129 repodata_set_str(pd->data, handle, 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, handle, SOLVABLE_MEDIANR, medianr);
160 repodata_set_void(pd->data, handle, SOLVABLE_MEDIADIR);
161 repodata_set_void(pd->data, handle, SOLVABLE_MEDIAFILE);
165 repodata_set_constant(pd->data, handle, SOLVABLE_MEDIANR, medianr);
166 repodata_set_void(pd->data, handle, SOLVABLE_MEDIADIR);
167 repodata_set_str(pd->data, handle, SOLVABLE_MEDIAFILE, sp[1]);
178 add_source(struct parsedata *pd, char *line, Solvable *s, unsigned handle)
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, handle, SOLVABLE_SOURCENAME);
197 repodata_set_id(pd->data, handle, SOLVABLE_SOURCENAME, name);
199 repodata_set_void(pd->data, handle, SOLVABLE_SOURCEEVR);
201 repodata_set_id(pd->data, handle, SOLVABLE_SOURCEEVR, evr);
202 repodata_set_constantid(pd->data, handle, 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;
236 set_checksum(Repodata *data, int handle, Id keyname, char *line)
241 if (split(line, sp, 3) != 2)
243 fprintf(stderr, "Bad source line: %s\n", line);
246 if (!strcasecmp (sp[0], "sha1"))
247 l = SIZEOF_SHA1 * 2, type = REPOKEY_TYPE_SHA1;
248 else if (!strcasecmp (sp[0], "md5"))
249 l = SIZEOF_MD5 * 2, type = REPOKEY_TYPE_MD5;
252 fprintf(stderr, "Unknown checksum type: %s\n", sp[0]);
255 if (strlen(sp[1]) != l)
257 fprintf(stderr, "Invalid checksum length for %s: %s\n", sp[0], sp[1]);
260 repodata_set_checksum(data, handle, keyname, type, sp[1]);
270 id3_cmp (const void *v1, const void *v2)
274 return i1[0] - i2[0];
284 commit_diskusage (struct parsedata *pd, unsigned handle)
287 Dirpool *dp = &pd->data->dirpool;
288 /* Now sort in dirid order. This ensures that parents come before
291 qsort(pd->dirs, pd->ndirs, sizeof (pd->dirs[0]), id3_cmp);
292 /* Substract leaf numbers from all parents to make the numbers
293 non-cumulative. This must be done post-order (i.e. all leafs
294 adjusted before parents). We ensure this by starting at the end of
295 the array moving to the start, hence seeing leafs before parents. */
296 for (i = pd->ndirs; i--;)
298 unsigned p = dirpool_parent(dp, pd->dirs[i][0]);
300 for (; p; p = dirpool_parent(dp, p))
303 if (pd->dirs[j][0] == p)
307 if (pd->dirs[j][1] < pd->dirs[i][1])
310 pd->dirs[j][1] -= pd->dirs[i][1];
311 if (pd->dirs[j][2] < pd->dirs[i][2])
314 pd->dirs[j][2] -= pd->dirs[i][2];
317 /* Haven't found this parent in the list, look further if
318 we maybe find the parents parent. */
325 unsigned slen = sizeof (sbuf);
326 for (i = 0; i < pd->ndirs; i++)
328 dir2str (attr, pd->dirs[i][0], &buf, &slen);
329 fprintf (stderr, "have dir %d %d %d %s\n", pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2], buf);
334 for (i = 0; i < pd->ndirs; i++)
335 if (pd->dirs[i][1] || pd->dirs[i][2])
337 repodata_add_dirnumnum(pd->data, handle, SOLVABLE_DISKUSAGE, pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2]);
343 /* Unfortunately "a"[0] is no constant expression in the C languages,
344 so we need to pass the four characters individually :-/ */
345 #define CTAG(a,b,c,d) ((unsigned)(((unsigned char)a) << 24) \
346 | ((unsigned char)b << 16) \
347 | ((unsigned char)c << 8) \
348 | ((unsigned char)d))
355 static inline unsigned
356 tag_from_string (char *cs)
358 unsigned char *s = (unsigned char*) cs;
359 return ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]);
365 * Parse susetags file passed in fp, fill solvables into repo
367 * susetags is key,value based
371 * for long (multi-line) values,
378 * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata
379 * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/packages
380 * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/pattern
383 * All keys have 3 characters and end in ':'
387 finish_solvable(struct parsedata *pd, Solvable *s, int handle)
389 Pool *pool = pd->repo->pool;
392 /* move file provides to filelist */
393 /* relies on the fact that rpm inserts self-provides at the end */
396 Id *p, *lastreal, did;
397 const char *str, *sp;
398 lastreal = pd->repo->idarraydata + s->provides;
399 for (p = lastreal; *p; p++)
402 for (p = lastreal; *p; p++)
404 str = id2str(pool, *p);
410 for (p = lastreal; *p; p++)
412 str = id2str(pool, *p);
413 sp = strrchr(str, '/');
416 char *sdup = strdup(str);
418 did = repodata_str2dir(pd->data, sdup, 1);
424 strncpy(sdup, str, sp - str);
426 did = repodata_str2dir(pd->data, sdup, 1);
428 repodata_add_dirstr(pd->data, handle, SOLVABLE_FILELIST, did, sp + 1);
434 /* A self provide, except for source packages. This is harmless
435 to do twice (in case we see the same package twice). */
436 if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
437 s->provides = repo_addid_dep(pd->repo, s->provides,
438 rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
439 /* XXX This uses repo_addid_dep internally, so should also be
440 harmless to do twice. */
441 s->supplements = repo_fix_legacy(pd->repo, s->provides, s->supplements);
443 commit_diskusage (pd, handle);
447 repo_add_susetags(Repo *repo, FILE *fp, Id vendor, const char *language, int flags)
449 Pool *pool = repo->pool;
456 int last_found_pack = 0;
463 if ((flags & SUSETAGS_EXTEND) && repo->nrepodata)
466 /* use last repodata */
467 data = repo->repodata + repo->nrepodata - 1;
469 data = repo_add_repodata(repo, 0);
471 memset(&pd, 0, sizeof(pd));
475 pd.repo = pd.common.repo = repo;
477 pd.common.pool = pool;
482 /* XXX deactivate test code */
487 * collect values in 'struct parsedata pd'
488 * then build .solv (and .attr) file
494 if (linep - line + 16 > aline) /* (re-)alloc buffer */
496 aline = linep - line;
497 line = realloc(line, aline + 512);
498 linep = line + aline;
501 if (!fgets(linep, aline - (linep - line), fp)) /* read line */
503 linep += strlen(linep);
504 if (linep == line || linep[-1] != '\n')
510 /* check for multi-line value tags (+Key:/-Key:) */
512 int is_end = (linep[-intag - 2] == '-')
513 && (linep[-1] == ':')
514 && !strncmp(linep - 1 - intag, line + 1, intag)
515 && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - 3] == '\n');
516 if (cummulate && !is_end)
521 if (cummulate && is_end)
523 linep[-intag - 2] = 0;
524 if (linep[-intag - 3] == '\n')
525 linep[-intag - 3] = 0;
529 if (!cummulate && is_end)
535 if (!cummulate && !is_end)
536 linep = line + intag + 3;
541 if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
543 char *tagend = strchr(line, ':');
546 fprintf(stderr, "bad line: %s\n", line);
549 intag = tagend - (line + 1);
551 switch (tag_from_string (line)) /* check if accumulation is needed */
553 case CTAG('+', 'P', 'r', 'q'):
554 case CTAG('+', 'P', 'r', 'c'):
555 case CTAG('+', 'P', 's', 'g'):
556 if (!pd.kind || !(flags & SUSETAGS_KINDS_SEPARATELY))
558 case CTAG('+', 'D', 'e', 's'):
559 case CTAG('+', 'E', 'u', 'l'):
560 case CTAG('+', 'I', 'n', 's'):
561 case CTAG('+', 'D', 'e', 'l'):
562 case CTAG('+', 'A', 'u', 't'):
566 line[0] = '='; /* handle lines between +Key:/-Key: as =Key: */
567 line[intag + 2] = ' ';
568 linep = line + intag + 3;
571 if (*line == '#' || !*line)
573 if (! (line[0] && line[1] && line[2] && line[3] && line[4] == ':'))
575 tag = tag_from_string (line);
579 * start of (next) package or pattern
581 * =Pkg: <name> <version> <release> <architecture>
585 if ((tag == CTAG('=', 'P', 'k', 'g')
586 || tag == CTAG('=', 'P', 'a', 't')))
589 /* If we have an old solvable, complete it by filling in some
592 finish_solvable(&pd, s, handle);
606 if (split(line + 5, sp, 5) != 4)
608 fprintf(stderr, "Bad line: %s\n", line);
611 /* Lookup (but don't construct) the name and arch. */
613 name = str2id(pool, join2(pd.kind, ":", sp[0]), 0);
615 name = str2id(pool, sp[0], 0);
616 arch = str2id(pool, sp[3], 0);
617 evr = makeevr(pool, join2(sp[1], "-", sp[2]));
621 /* Now see if we know this solvable already. If we found neither
622 the name nor the arch at all in this repo
623 there's no chance of finding the exact solvable either. */
624 if (indesc >= 2 && name && arch)
627 /* Now look for a solvable with the given name,evr,arch.
628 Our input is structured so, that the second set of =Pkg
629 lines comes in roughly the same order as the first set, so we
630 have a hint at where to start our search, namely were we found
632 for (n = repo->start, nn = n + last_found_pack; n < repo->end; n++, nn++)
636 s = pool->solvables + nn;
637 if (s->repo == repo && s->name == name && s->evr == evr && s->arch == arch)
644 last_found_pack = nn - repo->start;
645 handle = repodata_get_handle(data, last_found_pack);
649 /* And if we still don't have a solvable, create a new one. */
652 s = pool_id2solvable(pool, repo_add_solvable(repo));
653 last_found_pack = (s - pool->solvables) - repo->start;
656 repodata_extend(data, s - pool->solvables);
657 handle = repodata_get_handle(data, last_found_pack);
662 s->name = str2id(pool, join2(pd.kind, ":", sp[0]), 1);
664 s->name = str2id(pool, sp[0], 1);
669 s->arch = str2id(pool, sp[3], 1);
674 /* If we have no current solvable to add to, ignore all further lines
675 for it. Probably invalid input data in the second set of
677 if (indesc >= 2 && !s)
679 fprintf (stderr, "Huh %s?\n", line);
684 case CTAG('=', 'P', 'r', 'v'): /* provides */
685 s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
687 case CTAG('=', 'R', 'e', 'q'): /* requires */
688 s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
690 case CTAG('=', 'P', 'r', 'q'): /* pre-requires / packages required */
693 if (flags & SUSETAGS_KINDS_SEPARATELY)
694 repodata_set_poolstr(data, handle, str2id(pool, "solvable:must", 1), line + 6);
696 s->requires = adddep(pool, &pd, s->requires, line, 0, 0); /* patterns: a required package */
699 s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
701 case CTAG('=', 'O', 'b', 's'): /* obsoletes */
702 s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
704 case CTAG('=', 'C', 'o', 'n'): /* conflicts */
705 s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
707 case CTAG('=', 'R', 'e', 'c'): /* recommends */
708 s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
710 case CTAG('=', 'S', 'u', 'p'): /* supplements */
711 s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
713 case CTAG('=', 'E', 'n', 'h'): /* enhances */
714 s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
716 case CTAG('=', 'S', 'u', 'g'): /* suggests */
717 s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
719 case CTAG('=', 'F', 'r', 'e'): /* freshens */
720 s->freshens = adddep(pool, &pd, s->freshens, line, 0, pd.kind);
722 case CTAG('=', 'P', 'r', 'c'): /* packages recommended */
723 if (flags & SUSETAGS_KINDS_SEPARATELY)
724 repodata_set_poolstr(data, handle, str2id(pool, "solvable:should", 1), line + 6);
726 s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
728 case CTAG('=', 'P', 's', 'g'): /* packages suggested */
729 if (flags & SUSETAGS_KINDS_SEPARATELY)
730 repodata_set_poolstr(data, handle, str2id(pool, "solvable:may", 1), line + 6);
732 s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
734 case CTAG('=', 'P', 'c', 'n'): /* pattern: package conflicts */
735 if (flags & SUSETAGS_KINDS_SEPARATELY)
736 fprintf (stderr, "Unsupported: pattern -> package conflicts\n");
738 s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
740 case CTAG('=', 'P', 'o', 'b'): /* pattern: package obsoletes */
741 if (flags & SUSETAGS_KINDS_SEPARATELY)
742 fprintf (stderr, "Unsupported: pattern -> package obsoletes\n");
744 s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
746 case CTAG('=', 'P', 'f', 'r'): /* pattern: package freshens */
747 if (flags & SUSETAGS_KINDS_SEPARATELY)
748 fprintf (stderr, "Unsupported: pattern -> package freshens\n");
750 s->freshens = adddep(pool, &pd, s->freshens, line, 0, 0);
752 case CTAG('=', 'P', 's', 'p'): /* pattern: package supplements */
753 if (flags & SUSETAGS_KINDS_SEPARATELY)
754 fprintf (stderr, "Unsupported: pattern -> package supplements\n");
756 s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
758 case CTAG('=', 'P', 'e', 'n'): /* pattern: package enhances */
759 if (flags & SUSETAGS_KINDS_SEPARATELY)
760 fprintf (stderr, "Unsupported: pattern -> package enhances\n");
762 s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
764 case CTAG('=', 'V', 'e', 'r'): /* - version - */
770 /* From here it's the attribute tags. */
771 case CTAG('=', 'G', 'r', 'p'):
772 repodata_set_poolstr(data, handle, SOLVABLE_GROUP, line + 6);
774 case CTAG('=', 'L', 'i', 'c'):
775 repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, line + 6);
777 case CTAG('=', 'L', 'o', 'c'):
778 add_location(&pd, line + 6, s, handle);
780 case CTAG('=', 'S', 'r', 'c'):
781 add_source(&pd, line + 6, s, handle);
783 case CTAG('=', 'S', 'i', 'z'):
784 if (split (line + 6, sp, 3) == 2)
786 repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, (atoi(sp[0]) + 1023) / 1024);
787 repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, (atoi(sp[1]) + 1023) / 1024);
790 case CTAG('=', 'T', 'i', 'm'):
792 unsigned int t = atoi (line + 6);
794 repodata_set_num(data, handle, SOLVABLE_BUILDTIME, t);
797 case CTAG('=', 'K', 'w', 'd'):
798 repodata_add_poolstr_array(data, handle, SOLVABLE_KEYWORDS, line + 6);
800 case CTAG('=', 'A', 'u', 't'):
801 repodata_set_str(data, handle, SOLVABLE_AUTHORS, line + 6);
803 case CTAG('=', 'S', 'u', 'm'):
804 repodata_set_str(data, handle, langtag(&pd, SOLVABLE_SUMMARY, language), line + 6);
806 case CTAG('=', 'D', 'e', 's'):
807 repodata_set_str(data, handle, langtag(&pd, SOLVABLE_DESCRIPTION, language), line + 6);
809 case CTAG('=', 'E', 'u', 'l'):
810 repodata_set_str(data, handle, langtag(&pd, SOLVABLE_EULA, language), line + 6);
812 case CTAG('=', 'I', 'n', 's'):
813 repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEINS, language), line + 6);
817 repo_set_str(repo, blanr, SOLVABLE_MESSAGEINS, line + 6);
821 case CTAG('=', 'D', 'e', 'l'):
822 repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEDEL, language), line + 6);
824 case CTAG('=', 'V', 'i', 's'):
826 /* Accept numbers and textual bools. */
829 if (k || !strcasecmp (line + 6, "true"))
830 repodata_set_constant(data, handle, SOLVABLE_ISVISIBLE, 1);
833 case CTAG('=', 'S', 'h', 'r'):
834 if (last_found_pack >= pd.nshare)
838 pd.share_with = realloc (pd.share_with, (last_found_pack + 256) * sizeof (*pd.share_with));
839 memset (pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof (*pd.share_with));
842 pd.share_with = calloc (last_found_pack + 256, sizeof (*pd.share_with));
843 pd.nshare = last_found_pack + 256;
845 pd.share_with[last_found_pack] = strdup (line + 6);
847 case CTAG('=', 'D', 'i', 'r'):
848 add_dirline (&pd, line + 6);
850 case CTAG('=', 'C', 'a', 't'):
851 repodata_set_poolstr(data, handle, SOLVABLE_CATEGORY, line + 6);
853 case CTAG('=', 'O', 'r', 'd'):
854 /* Order is a string not a number, so we can retroactively insert
855 new patterns in the middle, i.e. 1 < 15 < 2. */
856 repodata_set_str(data, handle, SOLVABLE_ORDER, line + 6);
858 case CTAG('=', 'I', 'c', 'o'):
859 repodata_set_str(data, handle, SOLVABLE_ICON, line + 6);
861 case CTAG('=', 'E', 'x', 't'):
862 repodata_add_poolstr_array(data, handle, SOLVABLE_EXTENDS, join2("pattern", ":", line + 6));
864 case CTAG('=', 'I', 'n', 'c'):
865 repodata_add_poolstr_array(data, handle, SOLVABLE_INCLUDES, join2("pattern", ":", line + 6));
867 case CTAG('=', 'C', 'k', 's'):
868 set_checksum(data, handle, SOLVABLE_CHECKSUM, line + 6);
870 case CTAG('=', 'L', 'a', 'n'):
871 language = strdup(line + 6);
874 case CTAG('=', 'P', 'a', 't'):
875 case CTAG('=', 'P', 'k', 'g'):
885 finish_solvable(&pd, s, handle);
888 * (e.g. multiple binaries built from same source)
894 for (i = 0; i < pd.nshare; i++)
895 if (pd.share_with[i])
897 if (split(pd.share_with[i], sp, 5) != 4)
899 fprintf(stderr, "Bad =Shr line: %s\n", pd.share_with[i]);
903 Id name = str2id(pool, sp[0], 1);
904 Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
905 Id arch = str2id(pool, sp[3], 1);
908 for (n = repo->start, nn = repo->start + last_found;
909 n < repo->end; n++, nn++)
913 found = pool->solvables + nn;
914 if (found->repo == repo
915 && found->name == name
917 && found->arch == arch)
919 last_found = nn - repo->start;
924 repodata_merge_attrs(data, i, last_found);
926 free (pd.share_with);
930 repodata_internalize(data);