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;
236 set_checksum(Repodata *data, int last_found_pack, 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, last_found_pack, 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 entry)
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, entry, 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 last_found_pack)
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, last_found_pack, 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, last_found_pack);
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;
461 if ((flags & SUSETAGS_EXTEND) && repo->nrepodata)
464 /* use last repodata */
465 data = repo->repodata + repo->nrepodata - 1;
467 data = repo_add_repodata(repo, 0);
469 memset(&pd, 0, sizeof(pd));
473 pd.repo = pd.common.repo = repo;
475 pd.common.pool = pool;
483 * collect values in 'struct parsedata pd'
484 * then build .solv (and .attr) file
490 if (linep - line + 16 > aline) /* (re-)alloc buffer */
492 aline = linep - line;
493 line = realloc(line, aline + 512);
494 linep = line + aline;
497 if (!fgets(linep, aline - (linep - line), fp)) /* read line */
499 linep += strlen(linep);
500 if (linep == line || linep[-1] != '\n')
506 /* check for multi-line value tags (+Key:/-Key:) */
508 int is_end = (linep[-intag - 2] == '-')
509 && (linep[-1] == ':')
510 && !strncmp(linep - 1 - intag, line + 1, intag)
511 && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - 3] == '\n');
512 if (cummulate && !is_end)
517 if (cummulate && is_end)
519 linep[-intag - 2] = 0;
520 if (linep[-intag - 3] == '\n')
521 linep[-intag - 3] = 0;
525 if (!cummulate && is_end)
531 if (!cummulate && !is_end)
532 linep = line + intag + 3;
537 if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
539 char *tagend = strchr(line, ':');
542 fprintf(stderr, "bad line: %s\n", line);
545 intag = tagend - (line + 1);
547 switch (tag_from_string (line)) /* check if accumulation is needed */
549 case CTAG('+', 'P', 'r', 'q'):
550 case CTAG('+', 'P', 'r', 'c'):
551 case CTAG('+', 'P', 's', 'g'):
552 if (!pd.kind || !(flags & SUSETAGS_KINDS_SEPARATELY))
554 case CTAG('+', 'D', 'e', 's'):
555 case CTAG('+', 'E', 'u', 'l'):
556 case CTAG('+', 'I', 'n', 's'):
557 case CTAG('+', 'D', 'e', 'l'):
558 case CTAG('+', 'A', 'u', 't'):
562 line[0] = '='; /* handle lines between +Key:/-Key: as =Key: */
563 line[intag + 2] = ' ';
564 linep = line + intag + 3;
567 if (*line == '#' || !*line)
569 if (! (line[0] && line[1] && line[2] && line[3] && line[4] == ':'))
571 tag = tag_from_string (line);
575 * start of (next) package or pattern
577 * =Pkg: <name> <version> <release> <architecture>
581 if ((tag == CTAG('=', 'P', 'k', 'g')
582 || tag == CTAG('=', 'P', 'a', 't')))
585 /* If we have an old solvable, complete it by filling in some
588 finish_solvable(&pd, s, last_found_pack);
602 if (split(line + 5, sp, 5) != 4)
604 fprintf(stderr, "Bad line: %s\n", line);
607 /* Lookup (but don't construct) the name and arch. */
609 name = str2id(pool, join2(pd.kind, ":", sp[0]), 0);
611 name = str2id(pool, sp[0], 0);
612 arch = str2id(pool, sp[3], 0);
613 evr = makeevr(pool, join2(sp[1], "-", sp[2]));
617 /* Now see if we know this solvable already. If we found neither
618 the name nor the arch at all in this repo
619 there's no chance of finding the exact solvable either. */
620 if (indesc >= 2 && name && arch)
623 /* Now look for a solvable with the given name,evr,arch.
624 Our input is structured so, that the second set of =Pkg
625 lines comes in roughly the same order as the first set, so we
626 have a hint at where to start our search, namely were we found
628 for (n = repo->start, nn = n + last_found_pack; n < repo->end; n++, nn++)
632 s = pool->solvables + nn;
633 if (s->repo == repo && s->name == name && s->evr == evr && s->arch == arch)
639 last_found_pack = nn - repo->start;
642 /* And if we still don't have a solvable, create a new one. */
645 s = pool_id2solvable(pool, repo_add_solvable(repo));
646 last_found_pack = (s - pool->solvables) - repo->start;
648 repodata_extend(data, s - pool->solvables);
652 s->name = str2id(pool, join2(pd.kind, ":", sp[0]), 1);
654 s->name = str2id(pool, sp[0], 1);
659 s->arch = str2id(pool, sp[3], 1);
664 /* If we have no current solvable to add to, ignore all further lines
665 for it. Probably invalid input data in the second set of
667 if (indesc >= 2 && !s)
669 fprintf (stderr, "Huh %s?\n", line);
674 case CTAG('=', 'P', 'r', 'v'): /* provides */
675 s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
677 case CTAG('=', 'R', 'e', 'q'): /* requires */
678 s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
680 case CTAG('=', 'P', 'r', 'q'): /* pre-requires / packages required */
683 if (flags & SUSETAGS_KINDS_SEPARATELY)
684 repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:must", 1), line + 6);
686 s->requires = adddep(pool, &pd, s->requires, line, 0, 0); /* patterns: a required package */
689 s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
691 case CTAG('=', 'O', 'b', 's'): /* obsoletes */
692 s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
694 case CTAG('=', 'C', 'o', 'n'): /* conflicts */
695 s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
697 case CTAG('=', 'R', 'e', 'c'): /* recommends */
698 s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
700 case CTAG('=', 'S', 'u', 'p'): /* supplements */
701 s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
703 case CTAG('=', 'E', 'n', 'h'): /* enhances */
704 s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
706 case CTAG('=', 'S', 'u', 'g'): /* suggests */
707 s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
709 case CTAG('=', 'F', 'r', 'e'): /* freshens */
710 s->freshens = adddep(pool, &pd, s->freshens, line, 0, pd.kind);
712 case CTAG('=', 'P', 'r', 'c'): /* packages recommended */
713 if (flags & SUSETAGS_KINDS_SEPARATELY)
714 repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:should", 1), line + 6);
716 s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
718 case CTAG('=', 'P', 's', 'g'): /* packages suggested */
719 if (flags & SUSETAGS_KINDS_SEPARATELY)
720 repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:may", 1), line + 6);
722 s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
724 case CTAG('=', 'P', 'c', 'n'): /* pattern: package conflicts */
725 if (flags & SUSETAGS_KINDS_SEPARATELY)
726 fprintf (stderr, "Unsupported: pattern -> package conflicts\n");
728 s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
730 case CTAG('=', 'P', 'o', 'b'): /* pattern: package obsoletes */
731 if (flags & SUSETAGS_KINDS_SEPARATELY)
732 fprintf (stderr, "Unsupported: pattern -> package obsoletes\n");
734 s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
736 case CTAG('=', 'P', 'f', 'r'): /* pattern: package freshens */
737 if (flags & SUSETAGS_KINDS_SEPARATELY)
738 fprintf (stderr, "Unsupported: pattern -> package freshens\n");
740 s->freshens = adddep(pool, &pd, s->freshens, line, 0, 0);
742 case CTAG('=', 'P', 's', 'p'): /* pattern: package supplements */
743 if (flags & SUSETAGS_KINDS_SEPARATELY)
744 fprintf (stderr, "Unsupported: pattern -> package supplements\n");
746 s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
748 case CTAG('=', 'P', 'e', 'n'): /* pattern: package enhances */
749 if (flags & SUSETAGS_KINDS_SEPARATELY)
750 fprintf (stderr, "Unsupported: pattern -> package enhances\n");
752 s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
754 case CTAG('=', 'V', 'e', 'r'): /* - version - */
759 /* From here it's the attribute tags. */
760 case CTAG('=', 'G', 'r', 'p'):
761 repodata_set_poolstr(data, last_found_pack, SOLVABLE_GROUP, line + 6);
763 case CTAG('=', 'L', 'i', 'c'):
764 repodata_set_poolstr(data, last_found_pack, SOLVABLE_LICENSE, line + 6);
766 case CTAG('=', 'L', 'o', 'c'):
767 add_location(&pd, line + 6, s, last_found_pack);
769 case CTAG('=', 'S', 'r', 'c'):
770 add_source(&pd, line + 6, s, last_found_pack);
772 case CTAG('=', 'S', 'i', 'z'):
773 if (split (line + 6, sp, 3) == 2)
775 repodata_set_num(data, last_found_pack, SOLVABLE_DOWNLOADSIZE, (atoi(sp[0]) + 1023) / 1024);
776 repodata_set_num(data, last_found_pack, SOLVABLE_INSTALLSIZE, (atoi(sp[1]) + 1023) / 1024);
779 case CTAG('=', 'T', 'i', 'm'):
781 unsigned int t = atoi (line + 6);
783 repodata_set_num(data, last_found_pack, SOLVABLE_BUILDTIME, t);
786 case CTAG('=', 'K', 'w', 'd'):
787 repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_KEYWORDS, line + 6);
789 case CTAG('=', 'A', 'u', 't'):
790 repodata_set_str(data, last_found_pack, SOLVABLE_AUTHORS, line + 6);
792 case CTAG('=', 'S', 'u', 'm'):
793 repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_SUMMARY, language), line + 6);
795 case CTAG('=', 'D', 'e', 's'):
796 repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_DESCRIPTION, language), line + 6);
798 case CTAG('=', 'E', 'u', 'l'):
799 repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_EULA, language), line + 6);
801 case CTAG('=', 'I', 'n', 's'):
802 repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_MESSAGEINS, language), line + 6);
804 case CTAG('=', 'D', 'e', 'l'):
805 repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_MESSAGEDEL, language), line + 6);
807 case CTAG('=', 'V', 'i', 's'):
809 /* Accept numbers and textual bools. */
812 if (k || !strcasecmp (line + 6, "true"))
813 repodata_set_constant(data, last_found_pack, SOLVABLE_ISVISIBLE, 1);
816 case CTAG('=', 'S', 'h', 'r'):
817 if (last_found_pack >= pd.nshare)
821 pd.share_with = realloc (pd.share_with, (last_found_pack + 256) * sizeof (*pd.share_with));
822 memset (pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof (*pd.share_with));
825 pd.share_with = calloc (last_found_pack + 256, sizeof (*pd.share_with));
826 pd.nshare = last_found_pack + 256;
828 pd.share_with[last_found_pack] = strdup (line + 6);
830 case CTAG('=', 'D', 'i', 'r'):
831 add_dirline (&pd, line + 6);
833 case CTAG('=', 'C', 'a', 't'):
834 repodata_set_poolstr(data, last_found_pack, SOLVABLE_CATEGORY, line + 6);
836 case CTAG('=', 'O', 'r', 'd'):
837 /* Order is a string not a number, so we can retroactively insert
838 new patterns in the middle, i.e. 1 < 15 < 2. */
839 repodata_set_str(data, last_found_pack, SOLVABLE_ORDER, line + 6);
841 case CTAG('=', 'I', 'c', 'o'):
842 repodata_set_str(data, last_found_pack, SOLVABLE_ICON, line + 6);
844 case CTAG('=', 'E', 'x', 't'):
845 repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_EXTENDS, line + 6);
847 case CTAG('=', 'I', 'n', 'c'):
848 repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_INCLUDES, line + 6);
850 case CTAG('=', 'C', 'k', 's'):
851 set_checksum(data, last_found_pack, SOLVABLE_CHECKSUM, line + 6);
854 case CTAG('=', 'P', 'a', 't'):
855 case CTAG('=', 'P', 'k', 'g'):
865 finish_solvable(&pd, s, last_found_pack);
868 * (e.g. multiple binaries built from same source)
874 for (i = 0; i < pd.nshare; i++)
875 if (pd.share_with[i])
877 if (split(pd.share_with[i], sp, 5) != 4)
879 fprintf(stderr, "Bad =Shr line: %s\n", pd.share_with[i]);
883 Id name = str2id(pool, sp[0], 1);
884 Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
885 Id arch = str2id(pool, sp[3], 1);
888 for (n = repo->start, nn = repo->start + last_found;
889 n < repo->end; n++, nn++)
893 found = pool->solvables + nn;
894 if (found->repo == repo
895 && found->name == name
897 && found->arch == arch)
899 last_found = nn - repo->start;
904 repodata_merge_attrs(data, i, last_found);
906 free (pd.share_with);
910 repodata_internalize(data);