Pull together attribute store and repo. dumpsolv actually now really
[platform/upstream/libsolv.git] / src / repo_solv.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repo_solv.c
10  * 
11  * Read the binary dump of a Repo and create a Repo * from it
12  * 
13  *  See
14  *   Repo *pool_addrepo_solv(Pool *pool, FILE *fp)
15  * below
16  * 
17  */
18
19
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25
26 #include "repo_solv.h"
27 #include "util.h"
28 #include "attr_store_p.h"
29
30 #define INTERESTED_START        SOLVABLE_NAME
31 #define INTERESTED_END          SOLVABLE_FRESHENS
32
33 static Pool *mypool;            /* for pool_debug... */
34
35 /*-----------------------------------------------------------------*/
36 /* .solv read functions */
37
38 /*
39  * read u32
40  */
41
42 static unsigned int
43 read_u32(FILE *fp)
44 {
45   int c, i;
46   unsigned int x = 0;
47
48   for (i = 0; i < 4; i++)
49     {
50       c = getc(fp);
51       if (c == EOF)
52         {
53           pool_debug(mypool, SAT_FATAL, "unexpected EOF\n");
54           exit(1);
55         }
56       x = (x << 8) | c;
57     }
58   return x;
59 }
60
61
62 /*
63  * read u8
64  */
65
66 static unsigned int
67 read_u8(FILE *fp)
68 {
69   int c;
70   c = getc(fp);
71   if (c == EOF)
72     {
73       pool_debug(mypool, SAT_FATAL, "unexpected EOF\n");
74       exit(1);
75     }
76   return c;
77 }
78
79
80 /*
81  * read Id
82  */
83
84 static Id
85 read_id(FILE *fp, Id max)
86 {
87   unsigned int x = 0;
88   int c, i;
89
90   for (i = 0; i < 5; i++)
91     {
92       c = getc(fp);
93       if (c == EOF)
94         {
95           pool_debug(mypool, SAT_FATAL, "unexpected EOF\n");
96           exit(1);
97         }
98       if (!(c & 128))
99         {
100           x = (x << 7) | c;
101           if (max && x >= max)
102             {
103               pool_debug(mypool, SAT_FATAL, "read_id: id too large (%u/%u)\n", x, max);
104               exit(1);
105             }
106           return x;
107         }
108       x = (x << 7) ^ c ^ 128;
109     }
110   pool_debug(mypool, SAT_FATAL, "read_id: id too long\n");
111   exit(1);
112 }
113
114
115 /*
116  * read array of Ids
117  */
118
119 static Id *
120 read_idarray(FILE *fp, Id max, Id *map, Id *store, Id *end, int relative)
121 {
122   unsigned int x = 0;
123   int c;
124   Id old = 0;
125   for (;;)
126     {
127       c = getc(fp);
128       if (c == EOF)
129         {
130           pool_debug(mypool, SAT_FATAL, "unexpected EOF\n");
131           exit(1);
132         }
133       if ((c & 128) == 0)
134         {
135           x = (x << 6) | (c & 63);
136           if (relative)
137             {
138               if (x == 0 && c == 0x40)
139                 {
140                   /* prereq hack */
141                   if (store == end)
142                     {
143                       pool_debug(mypool, SAT_FATAL, "read_idarray: array overflow\n");
144                       exit(1);
145                     }
146                   *store++ = SOLVABLE_PREREQMARKER;
147                   old = 0;
148                   x = 0;
149                   continue;
150                 }
151               x = (x - 1) + old;
152               old = x;
153             }
154           if (x >= max)
155             {
156               pool_debug(mypool, SAT_FATAL, "read_idarray: id too large (%u/%u)\n", x, max);
157               exit(1);
158             }
159           if (map)
160             x = map[x];
161           if (store == end)
162             {
163               pool_debug(mypool, SAT_FATAL, "read_idarray: array overflow\n");
164               exit(1);
165             }
166           *store++ = x;
167           if ((c & 64) == 0)
168             {
169               if (x == 0)       /* already have trailing zero? */
170                 return store;
171               if (store == end)
172                 {
173                   pool_debug(mypool, SAT_FATAL, "read_idarray: array overflow\n");
174                   exit(1);
175                 }
176               *store++ = 0;
177               return store;
178             }
179           x = 0;
180           continue;
181         }
182       x = (x << 7) ^ c ^ 128;
183     }
184 }
185
186
187 /*-----------------------------------------------------------------*/
188
189 struct key {
190   Id name;
191   Id type;
192   Id size;
193 };
194
195 // ----------------------------------------------
196
197 /*
198  * read repo from .solv file
199  *  and add it to pool
200  */
201
202 void
203 repo_add_solv(Repo *repo, FILE *fp)
204 {
205   Pool *pool = repo->pool;
206   int i, l;
207   unsigned int numid, numrel, numsolv;
208   unsigned int numkeys, numschemata, numinfo;
209   Attrstore *embedded_store = 0;
210
211   int type;
212   Offset sizeid;
213   Offset *str;                         /* map Id -> Offset into string space */
214   char *strsp;                         /* repo string space */
215   char *sp;                            /* pointer into string space */
216   Id *idmap;                           /* map of repo Ids to pool Ids */
217   Id id;
218   unsigned int hashmask, h;
219   int hh;
220   Id *hashtbl;
221   Id name, evr, did;
222   int flags;
223   Reldep *ran;
224   unsigned int size_idarray;
225   Id *idarraydatap, *idarraydataend;
226   Offset ido;
227   Solvable *s;
228   unsigned int solvflags;
229   unsigned int solvversion;
230   struct key *keys;
231   Id *schemadata, *schemadatap, *schemadataend;
232   Id *schemata, key;
233
234   mypool = pool;
235
236   if (read_u32(fp) != ('S' << 24 | 'O' << 16 | 'L' << 8 | 'V'))
237     {
238       pool_debug(pool, SAT_FATAL, "not a SOLV file\n");
239       exit(1);
240     }
241   solvversion = read_u32(fp);
242   switch (solvversion)
243     {
244       case SOLV_VERSION_1:
245       case SOLV_VERSION_2:
246         break;
247       default:
248         pool_debug(pool, SAT_FATAL, "unsupported SOLV version\n");
249         exit(1);
250     }
251
252   pool_freeidhashes(pool);
253
254   numid = read_u32(fp);
255   numrel = read_u32(fp);
256   numsolv = read_u32(fp);
257   numkeys = read_u32(fp);
258   numschemata = read_u32(fp);
259   numinfo = read_u32(fp);
260   solvflags = read_u32(fp);
261
262   /*******  Part 1: string IDs  *****************************************/
263
264   sizeid = read_u32(fp);               /* size of string+Id space */
265
266   /*
267    * read strings and Ids
268    * 
269    */
270
271   
272   /*
273    * alloc buffers
274    */
275
276   /* alloc string buffer */
277   strsp = (char *)xrealloc(pool->ss.stringspace, pool->ss.sstrings + sizeid + 1);
278   /* alloc string offsets (Id -> Offset into string space) */
279   str = (Offset *)xrealloc(pool->ss.strings, (pool->ss.nstrings + numid) * sizeof(Offset));
280
281   pool->ss.stringspace = strsp;
282   pool->ss.strings = str;                      /* array of offsets into strsp, indexed by Id */
283
284   /* point to _BEHIND_ already allocated string/Id space */
285   strsp += pool->ss.sstrings;
286
287   /* alloc id map for name and rel Ids. this maps ids in the solv files
288    * to the ids in our pool */
289   idmap = (Id *)xcalloc(numid + numrel, sizeof(Id));
290
291   /*
292    * read new repo at end of pool
293    */
294   
295   if ((solvflags & SOLV_FLAG_PREFIX_POOL) == 0)
296     {
297       if (fread(strsp, sizeid, 1, fp) != 1)
298         {
299           pool_debug(pool, SAT_FATAL, "read error while reading strings\n");
300           exit(1);
301         }
302     }
303   else
304     {
305       unsigned int pfsize = read_u32 (fp);
306       char *prefix = xmalloc (pfsize);
307       char *pp = prefix;
308       char *old_str = "";
309       char *dest = strsp;
310       if (fread (prefix, pfsize, 1, fp) != 1)
311         {
312           pool_debug(pool, SAT_FATAL, "read error while reading strings\n");
313           exit(1);
314         }
315       for (i = 1; i < numid; i++)
316         {
317           int same = (unsigned char)*pp++;
318           size_t len = strlen (pp) + 1;
319           if (same)
320             memcpy (dest, old_str, same);
321           memcpy (dest + same, pp, len);
322           pp += len;
323           old_str = dest;
324           dest += same + len;
325         }
326       xfree (prefix);
327     }
328   strsp[sizeid] = 0;                   /* make string space \0 terminated */
329   sp = strsp;
330
331   /*
332    * build hashes for all read strings
333    * 
334    */
335   
336   hashmask = mkmask(pool->ss.nstrings + numid);
337
338 #if 0
339   POOL_DEBUG(SAT_DEBUG_STATS, "read %d strings\n", numid);
340   POOL_DEBUG(SAT_DEBUG_STATS, "string hash buckets: %d\n", hashmask + 1);
341 #endif
342
343   /*
344    * ensure sufficient hash size
345    */
346   
347   hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
348
349   /*
350    * fill hashtable with strings already in pool
351    */
352   
353   for (i = 1; i < pool->ss.nstrings; i++)  /* leave out our dummy zero id */
354     {
355       h = strhash(pool->ss.stringspace + pool->ss.strings[i]) & hashmask;
356       hh = HASHCHAIN_START;
357       while (hashtbl[h])
358         h = HASHCHAIN_NEXT(h, hh, hashmask);
359       hashtbl[h] = i;
360     }
361
362   /*
363    * run over string space, calculate offsets
364    * 
365    * build id map (maps solv Id -> pool Id)
366    */
367   
368   for (i = 1; i < numid; i++)
369     {
370       if (sp >= strsp + sizeid)
371         {
372           pool_debug(pool, SAT_FATAL, "not enough strings\n");
373           exit(1);
374         }
375       if (!*sp)                        /* empty string */
376         {
377           idmap[i] = ID_EMPTY;
378           sp++;
379           continue;
380         }
381
382       /* find hash slot */
383       h = strhash(sp) & hashmask;
384       hh = HASHCHAIN_START;
385       for (;;)
386         {
387           id = hashtbl[h];
388           if (id == 0)
389             break;
390           if (!strcmp(pool->ss.stringspace + pool->ss.strings[id], sp))
391             break;                     /* existing string */
392           h = HASHCHAIN_NEXT(h, hh, hashmask);
393         }
394
395       /* length == offset to next string */
396       l = strlen(sp) + 1;
397       if (id == ID_NULL)               /* end of hash chain -> new string */
398         {
399           id = pool->ss.nstrings++;
400           hashtbl[h] = id;
401           str[id] = pool->ss.sstrings;    /* save Offset */
402           if (sp != pool->ss.stringspace + pool->ss.sstrings)   /* not at end-of-buffer */
403             memmove(pool->ss.stringspace + pool->ss.sstrings, sp, l);   /* append to pool buffer */
404           pool->ss.sstrings += l;
405         }
406       idmap[i] = id;                   /* repo relative -> pool relative */
407       sp += l;                         /* next string */
408     }
409   xfree(hashtbl);
410   pool_shrink_strings(pool);           /* vacuum */
411
412   
413   /*******  Part 2: Relation IDs  ***************************************/
414
415   /*
416    * read RelDeps
417    * 
418    */
419   
420   if (numrel)
421     {
422       /* extend rels */
423       ran = (Reldep *)xrealloc(pool->rels, (pool->nrels + numrel) * sizeof(Reldep));
424       if (!ran)
425         {
426           pool_debug(pool, SAT_FATAL, "no mem for rel space\n");
427           exit(1);
428         }
429       pool->rels = ran;        /* extended rel space */
430
431       hashmask = mkmask(pool->nrels + numrel);
432 #if 0
433       POOL_DEBUG(SAT_DEBUG_STATS, "read %d rels\n", numrel);
434       POOL_DEBUG(SAT_DEBUG_STATS, "rel hash buckets: %d\n", hashmask + 1);
435 #endif
436       /*
437        * prep hash table with already existing RelDeps
438        */
439       
440       hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
441       for (i = 1; i < pool->nrels; i++)
442         {
443           h = relhash(ran[i].name, ran[i].evr, ran[i].flags) & hashmask;
444           hh = HASHCHAIN_START;
445           while (hashtbl[h])
446             h = HASHCHAIN_NEXT(h, hh, hashmask);
447           hashtbl[h] = i;
448         }
449
450       /*
451        * read RelDeps from repo
452        */
453       
454       for (i = 0; i < numrel; i++)
455         {
456           name = read_id(fp, i + numid);        /* read (repo relative) Ids */
457           evr = read_id(fp, i + numid);
458           flags = read_u8(fp);
459           name = idmap[name];           /* map to (pool relative) Ids */
460           evr = idmap[evr];
461           h = relhash(name, evr, flags) & hashmask;
462           hh = HASHCHAIN_START;
463           for (;;)
464             {
465               id = hashtbl[h];
466               if (id == ID_NULL)        /* end of hash chain */
467                 break;
468               if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == flags)
469                 break;
470               h = HASHCHAIN_NEXT(h, hh, hashmask);
471             }
472           if (id == ID_NULL)            /* new RelDep */
473             {
474               id = pool->nrels++;
475               hashtbl[h] = id;
476               ran[id].name = name;
477               ran[id].evr = evr;
478               ran[id].flags = flags;
479             }
480           idmap[i + numid] = MAKERELDEP(id);   /* fill Id map */
481         }
482       xfree(hashtbl);
483       pool_shrink_rels(pool);           /* vacuum */
484     }
485
486
487   /*******  Part 3: Keys  ***********************************************/
488
489   keys = xcalloc(numkeys, sizeof(*keys));
490   /* keys start at 1 */
491   for (i = 1; i < numkeys; i++)
492     {
493       keys[i].name = idmap[read_id(fp, numid)];
494       keys[i].type = read_id(fp, 0);
495       keys[i].size = read_id(fp, 0);
496     }
497
498   /*******  Part 4: Schemata ********************************************/
499   
500   id = read_id(fp, 0);
501   schemadata = xcalloc(id, sizeof(Id));
502   schemadatap = schemadata;
503   schemadataend = schemadata + id;
504   schemata = xcalloc(numschemata, sizeof(Id));
505   for (i = 0; i < numschemata; i++)
506     {
507       schemata[i] = schemadatap - schemadata;
508       schemadatap = read_idarray(fp, numid, 0, schemadatap, schemadataend, 0);
509     }
510
511   /*******  Part 5: Info  ***********************************************/
512   /* we skip the info for now... */
513   for (i = 0; i < numinfo; i++)
514     {
515       Id *schema = schemadata + schemata[read_id(fp, numschemata)];
516       while ((key = *schema++) != 0)
517         {
518           type = keys[key].type;
519           switch (type)
520             {
521               case TYPE_ID:
522                 read_id(fp, numid + numrel);   /* just check Id */
523                 break;
524               case TYPE_ATTR_CHUNK:
525                 read_id(fp, 0);
526                 /* Fallthrough.  */
527               case TYPE_ATTR_INT:
528                 read_id(fp, 0);
529                 break;
530               case TYPE_U32:
531                 read_u32(fp);
532                 break;
533               case TYPE_ATTR_STRING:
534               case TYPE_STR:
535                 while(read_u8(fp) != 0)
536                   ;
537                 break;
538               case TYPE_IDARRAY:
539               case TYPE_REL_IDARRAY:
540                 while ((read_u8(fp) & 0xc0) != 0)
541                   ;
542                 break;
543               case TYPE_ATTR_INTLIST:
544               case TYPE_ATTR_LOCALIDS:
545                 while (read_id(fp, 0) != 0)
546                   ;
547                 break;
548               default:
549                 pool_debug(pool, SAT_FATAL, "unknown type %d\n", type);
550                 exit(0);
551             }
552         }
553     }
554
555   /*******  Part 6: packed sizes (optional)  ****************************/
556   char *exists = 0;
557   if ((solvflags & SOLV_FLAG_PACKEDSIZES) != 0)
558     {
559       exists = xmalloc (numsolv);
560       for (i = 0; i < numsolv; i++)
561         exists[i] = read_id(fp, 0) != 0;
562     }
563
564   /*******  Part 7: item data *******************************************/
565
566   /* calculate idarray size */
567   size_idarray = 0;
568   for (i = 1; i < numkeys; i++)
569     {
570       id = keys[i].name;
571       if ((keys[i].type == TYPE_IDARRAY || keys[i].type == TYPE_REL_IDARRAY)
572           && id >= INTERESTED_START && id <= INTERESTED_END)
573         size_idarray += keys[i].size;
574     }
575
576   /* allocate needed space in repo */
577   if (size_idarray)
578     {
579       repo_reserve_ids(repo, 0, size_idarray);
580       idarraydatap = repo->idarraydata + repo->idarraysize;
581       repo->idarraysize += size_idarray;
582       idarraydataend = repo->idarraydata + repo->idarraysize;
583       repo->lastoff = 0;
584     }
585   else
586     {
587       idarraydatap = 0;
588       idarraydataend = 0;
589     }
590
591   /* read solvables */
592   s = pool_id2solvable(pool, repo_add_solvable_block(repo, numsolv));
593
594   if ((solvflags & SOLV_FLAG_VERTICAL) != 0)
595     {
596       Id *solvschema = xcalloc(numsolv, sizeof(Id));
597       unsigned char *used = xmalloc(numschemata);
598       Solvable *sstart = s;
599       Id type;
600
601       for (i = 0; i < numsolv; i++)
602         solvschema[i] = read_id(fp, numschemata);
603       for (key = 1; key < numkeys; key++)
604         {
605           id = keys[key].name;
606           type = keys[key].type;
607           memset(used, 0, numschemata);
608           for (i = 0; i < numschemata; i++)
609             {
610               Id *keyp = schemadata + schemata[i];
611               while (*keyp)
612                 if (*keyp++ == key)
613                   {
614                     used[i] = 1;
615                     break;
616                   }
617             }
618           for (i = 0, s = sstart; i < numsolv; i++, s++)
619              {
620               if (!used[solvschema[i]])
621                 continue;
622               switch (type)
623                 {
624                 case TYPE_ID:
625                   did = idmap[read_id(fp, numid + numrel)];
626                   if (id == SOLVABLE_NAME)
627                     s->name = did;
628                   else if (id == SOLVABLE_ARCH)
629                     s->arch = did;
630                   else if (id == SOLVABLE_EVR)
631                     s->evr = did;
632                   else if (id == SOLVABLE_VENDOR)
633                     s->vendor = did;
634                   break;
635                 case TYPE_U32:
636                   h = read_u32(fp);
637                   if (id == RPM_RPMDBID)
638                     {
639                       if (!repo->rpmdbid)
640                         repo->rpmdbid = (Id *)xcalloc(numsolv, sizeof(Id));
641                       repo->rpmdbid[i] = h;
642                     }
643                   break;
644                 case TYPE_STR:
645                   while(read_u8(fp) != 0)
646                     ;
647                   break;
648                 case TYPE_IDARRAY:
649                 case TYPE_REL_IDARRAY:
650                   if (id < INTERESTED_START || id > INTERESTED_END)
651                     {
652                       /* not interested in array */
653                       while ((read_u8(fp) & 0xc0) != 0)
654                         ;
655                       break;
656                     }
657                   ido = idarraydatap - repo->idarraydata;
658                   idarraydatap = read_idarray(fp, numid + numrel, idmap, idarraydatap, idarraydataend, type == TYPE_REL_IDARRAY);
659                   if (id == SOLVABLE_PROVIDES)
660                     s->provides = ido;
661                   else if (id == SOLVABLE_OBSOLETES)
662                     s->obsoletes = ido;
663                   else if (id == SOLVABLE_CONFLICTS)
664                     s->conflicts = ido;
665                   else if (id == SOLVABLE_REQUIRES)
666                     s->requires = ido;
667                   else if (id == SOLVABLE_RECOMMENDS)
668                     s->recommends= ido;
669                   else if (id == SOLVABLE_SUPPLEMENTS)
670                     s->supplements = ido;
671                   else if (id == SOLVABLE_SUGGESTS)
672                     s->suggests = ido;
673                   else if (id == SOLVABLE_ENHANCES)
674                     s->enhances = ido;
675                   else if (id == SOLVABLE_FRESHENS)
676                     s->freshens = ido;
677                   break;
678                 case TYPE_ATTR_INT:
679                 case TYPE_ATTR_CHUNK:
680                 case TYPE_ATTR_STRING:
681                 case TYPE_ATTR_INTLIST:
682                 case TYPE_ATTR_LOCALIDS:
683                   if (!embedded_store)
684                     embedded_store = new_store (pool);
685                   add_attr_from_file (embedded_store, i, id, type, idmap, numid, fp);
686                   break;
687                 }
688             }
689         }
690       xfree(used);
691       xfree(solvschema);
692       xfree(idmap);
693       xfree(schemata);
694       xfree(schemadata);
695       xfree(keys);
696       mypool = 0;
697       return;
698     }
699   for (i = 0; i < numsolv; i++, s++)
700     {
701       if (exists && !exists[i])
702         continue;
703       Id *keyp = schemadata + schemata[read_id(fp, numschemata)];
704       while ((key = *keyp++) != 0)
705         {
706           id = keys[key].name;
707           switch (keys[key].type)
708             {
709             case TYPE_ID:
710               did = idmap[read_id(fp, numid + numrel)];
711               if (id == SOLVABLE_NAME)
712                 s->name = did;
713               else if (id == SOLVABLE_ARCH)
714                 s->arch = did;
715               else if (id == SOLVABLE_EVR)
716                 s->evr = did;
717               else if (id == SOLVABLE_VENDOR)
718                 s->vendor = did;
719 #if 0
720               POOL_DEBUG(SAT_DEBUG_STATS, "%s -> %s\n", id2str(pool, id), id2str(pool, did));
721 #endif
722               break;
723             case TYPE_U32:
724               h = read_u32(fp);
725 #if 0
726               POOL_DEBUG(SAT_DEBUG_STATS, "%s -> %u\n", id2str(pool, id), h);
727 #endif
728               if (id == RPM_RPMDBID)
729                 {
730                   if (!repo->rpmdbid)
731                     repo->rpmdbid = (Id *)xcalloc(numsolv, sizeof(Id));
732                   repo->rpmdbid[i] = h;
733                 }
734               break;
735             case TYPE_STR:
736               while(read_u8(fp) != 0)
737                 ;
738               break;
739             case TYPE_IDARRAY:
740             case TYPE_REL_IDARRAY:
741               if (id < INTERESTED_START || id > INTERESTED_END)
742                 {
743                   /* not interested in array */
744                   while ((read_u8(fp) & 0xc0) != 0)
745                     ;
746                   break;
747                 }
748               ido = idarraydatap - repo->idarraydata;
749               idarraydatap = read_idarray(fp, numid + numrel, idmap, idarraydatap, idarraydataend, keys[key].type == TYPE_REL_IDARRAY);
750               if (id == SOLVABLE_PROVIDES)
751                 s->provides = ido;
752               else if (id == SOLVABLE_OBSOLETES)
753                 s->obsoletes = ido;
754               else if (id == SOLVABLE_CONFLICTS)
755                 s->conflicts = ido;
756               else if (id == SOLVABLE_REQUIRES)
757                 s->requires = ido;
758               else if (id == SOLVABLE_RECOMMENDS)
759                 s->recommends= ido;
760               else if (id == SOLVABLE_SUPPLEMENTS)
761                 s->supplements = ido;
762               else if (id == SOLVABLE_SUGGESTS)
763                 s->suggests = ido;
764               else if (id == SOLVABLE_ENHANCES)
765                 s->enhances = ido;
766               else if (id == SOLVABLE_FRESHENS)
767                 s->freshens = ido;
768 #if 0
769               POOL_DEBUG(SAT_DEBUG_STATS, "%s ->\n", id2str(pool, id));
770               for (; repo->idarraydata[ido]; ido++)
771                 POOL_DEBUG(SAT_DEBUG_STATS,"  %s\n", dep2str(pool, repo->idarraydata[ido]));
772 #endif
773               break;
774             case TYPE_ATTR_INT:
775             case TYPE_ATTR_CHUNK:
776             case TYPE_ATTR_STRING:
777             case TYPE_ATTR_INTLIST:
778             case TYPE_ATTR_LOCALIDS:
779               if (!embedded_store)
780                 embedded_store = new_store (pool);
781               add_attr_from_file (embedded_store, i, id, keys[key].type, idmap, numid, fp);
782               break;
783             }
784         }
785     }
786   xfree(exists);
787   if (embedded_store)
788     {
789       attr_store_pack (embedded_store);
790       repo_add_attrstore (repo, embedded_store);
791     }
792   xfree(idmap);
793   xfree(schemata);
794   xfree(schemadata);
795   xfree(keys);
796   mypool = 0;
797 }
798
799 // EOF