Some infrastructure for directory trees.
[platform/upstream/libsolv.git] / src / attr_store.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 /* We need FNM_CASEFOLD and strcasestr.  */
9 #ifndef _GNU_SOURCE
10 #define _GNU_SOURCE
11 #endif
12
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <assert.h>
21 #include <fnmatch.h>
22 #include <regex.h>
23
24 #include "attr_store.h"
25 #include "pool.h"
26 #include "repo.h"
27 #include "util.h"
28
29 #include "attr_store_p.h"
30
31 #include "fastlz.c"
32
33 /* #define DEBUG_PAGING */
34
35 #define BLOB_BLOCK 65535
36
37 #define STRINGSPACE_BLOCK 1023
38 #define STRING_BLOCK 127
39 #define LOCALID_NULL  0
40 #define LOCALID_EMPTY 1
41
42 static Id add_key (Attrstore *s, Id name, unsigned type, unsigned size);
43
44 Attrstore *
45 new_store (Pool *pool)
46 {
47   static const char *predef_strings[] = {
48     "<NULL>",
49     "",
50     0
51   };
52   Attrstore *s = calloc (1, sizeof (Attrstore));
53   s->pool = pool;
54   stringpool_init (&s->ss, predef_strings);
55   add_key (s, 0, 0, 0);
56
57   return s;
58 }
59
60 LocalId
61 str2localid (Attrstore *s, const char *str, int create)
62 {
63   return stringpool_str2id (&s->ss, str, create);
64 }
65
66 const char *
67 localid2str(Attrstore *s, LocalId id)
68 {
69   return s->ss.stringspace + s->ss.strings[id];
70 }
71
72 static void
73 setup_dirs (Attrstore *s)
74 {
75   static const char *ss_init_strs[] =
76   {
77     "<NULL>",
78     "",
79     0
80   };
81
82   s->dirtree.dirs = calloc (1024, sizeof (s->dirtree.dirs[0]));
83   s->dirtree.ndirs = 2;
84   s->dirtree.dirs[0].child = 0;
85   s->dirtree.dirs[0].sibling = 0;
86   s->dirtree.dirs[0].name = 0;
87   s->dirtree.dirs[1].child = 0;
88   s->dirtree.dirs[1].sibling = 0;
89   s->dirtree.dirs[1].name = STRID_EMPTY;
90
91   s->dirtree.dirstack_size = 16;
92   s->dirtree.dirstack = malloc (s->dirtree.dirstack_size * sizeof (s->dirtree.dirstack[0]));
93   s->dirtree.ndirstack = 0;
94   s->dirtree.dirstack[s->dirtree.ndirstack++] = 1; //dir-id of /
95
96   stringpool_init (&s->dirtree.ss, ss_init_strs);
97 }
98
99 static unsigned
100 dir_lookup_1 (Attrstore *s, unsigned dir, const char *name, unsigned insert)
101 {
102   Id nameid;
103   while (*name == '/')
104     name++;
105   if (!*name)
106     return dir;
107   const char *end = strchrnul (name, '/');
108   nameid = stringpool_strn2id (&s->dirtree.ss, name, end - name, 1);
109   unsigned c, num = 0;
110   Dir *dirs = s->dirtree.dirs;
111   for (c = dirs[dir].child; c; c = dirs[c].sibling, num++)
112     if (nameid == dirs[c].name)
113       break;
114   if (!c && !insert)
115     return 0;
116   if (!c)
117     {
118       c = s->dirtree.ndirs++;
119       if (!(c & 1023))
120         dirs = realloc (dirs, (c + 1024) * sizeof (dirs[0]));
121       dirs[c].child = 0;
122       dirs[c].sibling = dirs[dir].child;
123       dirs[c].name = nameid;
124       dirs[c].parent = dir;
125       dirs[dir].child = c;
126       s->dirtree.dirs = dirs;
127     }
128   if (!(s->dirtree.ndirstack & 15))
129     {
130       s->dirtree.dirstack_size += 16;
131       s->dirtree.dirstack = realloc (s->dirtree.dirstack, s->dirtree.dirstack_size * sizeof (s->dirtree.dirstack[0]));
132     }
133   s->dirtree.dirstack[s->dirtree.ndirstack++] = c;
134   if (!*end)
135     return c;
136   unsigned ret = dir_lookup_1 (s, c, end + 1, insert);
137   return ret;
138 }
139
140 unsigned
141 dir_lookup (Attrstore *s, const char *name, unsigned insert)
142 {
143   if (!s->dirtree.ndirs)
144     setup_dirs (s);
145
146   /* Detect number of common path components.  Accept multiple // .  */
147   const char *new_start;
148   unsigned components;
149   for (components = 1, new_start = name; components < s->dirtree.ndirstack; )
150     {
151       int ofs;
152       const char *dirname;
153       while (*new_start == '/')
154         new_start++;
155       dirname = stringpool_id2str (&s->dirtree.ss, s->dirtree.dirs[s->dirtree.dirstack[components]].name);
156       for (ofs = 0;; ofs++)
157         {
158           char n = new_start[ofs];
159           char d = dirname[ofs];
160           if (d == 0 && (n == 0 || n == '/'))
161             {
162               new_start += ofs;
163               components++;
164               if (n == 0)
165                 goto endmatch2;
166               break;
167             }
168           if (n != d)
169             goto endmatch;
170         }
171     }
172 endmatch:
173   while (*new_start == '/')
174     new_start++;
175 endmatch2:
176
177   /* We have always / on the stack.  */
178   //assert (ndirstack);
179   //assert (ndirstack >= components);
180   s->dirtree.ndirstack = components;
181   unsigned ret = s->dirtree.dirstack[s->dirtree.ndirstack - 1];
182   if (*new_start)
183     ret = dir_lookup_1 (s, ret, new_start, insert);
184   //assert (ret == dirstack[ndirstack - 1]);
185   return ret;
186 }
187
188 unsigned
189 dir_parent (Attrstore *s, unsigned dir)
190 {
191   return s->dirtree.dirs[dir].parent;
192 }
193
194 void
195 dir2str (Attrstore *s, unsigned dir, char **str, unsigned *len)
196 {
197   unsigned l = 0;
198   Id ids[s->dirtree.dirstack_size + 1];
199   unsigned i, ii;
200   for (i = 0; dir > 1; dir = dir_parent (s, dir), i++)
201     ids[i] = s->dirtree.dirs[dir].name;
202   ii = i;
203   l = 1;
204   for (i = 0; i < ii; i++)
205     l += 1 + strlen (stringpool_id2str (&s->dirtree.ss, ids[i]));
206   l++;
207   if (l > *len)
208     {
209       *str = malloc (l);
210       *len = l;
211     }
212   char *dest = *str;
213   *dest++ = '/';
214   for (i = ii; i--;)
215     {
216       const char *name = stringpool_id2str (&s->dirtree.ss, ids[i]);
217       dest = mempcpy (dest, name, strlen (name));
218       *dest++ = '/';
219     }
220   *dest = 0;
221 }
222
223 void
224 ensure_entry (Attrstore *s, unsigned int entry)
225 {
226   unsigned int old_num = s->entries;
227   if (entry < s->entries)
228     return;
229   s->entries = entry + 1;
230   if (((old_num + 127) & ~127) != ((s->entries + 127) & ~127))
231     {
232       if (s->attrs)
233         s->attrs = realloc (s->attrs, (((s->entries+127) & ~127) * sizeof (s->attrs[0])));
234       else
235         s->attrs = malloc (((s->entries+127) & ~127) * sizeof (s->attrs[0]));
236     }
237   memset (s->attrs + old_num, 0, (s->entries - old_num) * sizeof (s->attrs[0]));
238 }
239
240 unsigned int
241 new_entry (Attrstore *s)
242 {
243   if ((s->entries & 127) == 0)
244     {
245       if (s->attrs)
246         s->attrs = realloc (s->attrs, ((s->entries+128) * sizeof (s->attrs[0])));
247       else
248         s->attrs = malloc ((s->entries+128) * sizeof (s->attrs[0]));
249     }
250   s->attrs[s->entries++] = 0;
251   return s->entries - 1;
252 }
253
254 static LongNV *
255 find_attr (Attrstore *s, unsigned int entry, Id name)
256 {
257   LongNV *nv;
258   if (entry >= s->entries)
259     return 0;
260   nv = s->attrs[entry];
261   if (nv)
262     {
263       while (nv->key && s->keys[nv->key].name != name)
264         nv++;
265       if (nv->key)
266         return nv;
267     }
268   return 0;
269 }
270
271 static void
272 add_attr (Attrstore *s, unsigned int entry, LongNV attr)
273 {
274   LongNV *nv;
275   unsigned int len;
276   ensure_entry (s, entry);
277   if (attr.key >= s->nkeys)
278     return;
279   nv = s->attrs[entry];
280   len = 0;
281   if (nv)
282     {
283       while (nv->key && nv->key != attr.key)
284         nv++;
285       if (nv->key)
286         return;
287       len = nv - s->attrs[entry];
288     }
289   len += 2;
290   if (s->attrs[entry])
291     s->attrs[entry] = realloc (s->attrs[entry], len * sizeof (LongNV));
292   else
293     s->attrs[entry] = malloc (len * sizeof (LongNV));
294   nv = s->attrs[entry] + len - 2;
295   *nv++ = attr;
296   nv->key = 0;
297 }
298
299 void
300 add_attr_int (Attrstore *s, unsigned int entry, Id name, unsigned int val)
301 {
302   LongNV nv;
303   nv.key = add_key (s, name, TYPE_ATTR_INT, 0);
304   nv.v.i[0] = val;
305   add_attr (s, entry, nv);
306 }
307
308 void
309 add_attr_special_int (Attrstore *s, unsigned int entry, Id name, unsigned int val)
310 {
311   if (val > (TYPE_ATTR_SPECIAL_END - TYPE_ATTR_SPECIAL_START))
312     add_attr_int (s, entry, name, val);
313   else
314     {
315       LongNV nv;
316       nv.key = add_key (s, name, TYPE_ATTR_SPECIAL_START + val, 0);
317       add_attr (s, entry, nv);
318     }
319 }
320
321 static void
322 add_attr_chunk (Attrstore *s, unsigned int entry, Id name, unsigned int ofs, unsigned int len)
323 {
324   LongNV nv;
325   nv.key = add_key (s, name, TYPE_ATTR_CHUNK, 0);
326   nv.v.i[0] = ofs;
327   nv.v.i[1] = len;
328   add_attr (s, entry, nv);
329 }
330
331 void
332 add_attr_blob (Attrstore *s, unsigned int entry, Id name, const void *ptr, unsigned int len)
333 {
334   if (((s->blob_next_free + BLOB_BLOCK) & ~BLOB_BLOCK)
335       != ((s->blob_next_free + len + BLOB_BLOCK) & ~BLOB_BLOCK))
336     {
337       unsigned int blobsz = (s->blob_next_free + len + BLOB_BLOCK) &~BLOB_BLOCK;
338       s->blob_store = xrealloc (s->blob_store, blobsz);
339     }
340   memcpy (s->blob_store + s->blob_next_free, ptr, len);
341   add_attr_chunk (s, entry, name, s->blob_next_free, len);
342   s->blob_next_free += len;
343
344   unsigned int npages = (s->blob_next_free + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
345   if (npages != s->num_pages)
346     {
347       Attrblobpage *p;
348       s->pages = xrealloc (s->pages, npages * sizeof (s->pages[0]));
349       for (p = s->pages + s->num_pages; s->num_pages < npages;
350            p++, s->num_pages++)
351         {
352           p->mapped_at = s->num_pages * BLOB_PAGESIZE;
353           p->file_offset = 0;
354           p->file_size = 0;
355         }
356     }
357 }
358
359 void
360 add_attr_string (Attrstore *s, unsigned int entry, Id name, const char *val)
361 {
362   LongNV nv;
363   nv.key = add_key (s, name, TYPE_ATTR_STRING, 0);
364   nv.v.str = strdup (val);
365   add_attr (s, entry, nv);
366 }
367
368 void
369 add_attr_intlist_int (Attrstore *s, unsigned int entry, Id name, int val)
370 {
371   LongNV *nv = find_attr (s, entry, name);
372   if (val == 0)
373     return;
374   if (nv)
375     {
376       unsigned len = 0;
377       while (nv->v.intlist[len])
378         len++;
379       nv->v.intlist = realloc (nv->v.intlist, (len + 2) * sizeof (nv->v.intlist[0]));
380       nv->v.intlist[len] = val;
381       nv->v.intlist[len+1] = 0;
382     }
383   else
384     {
385       LongNV mynv;
386       mynv.key = add_key (s, name, TYPE_ATTR_INTLIST, 0);
387       mynv.v.intlist = malloc (2 * sizeof (mynv.v.intlist[0]));
388       mynv.v.intlist[0] = val;
389       mynv.v.intlist[1] = 0;
390       add_attr (s, entry, mynv);
391     }
392 }
393
394 void
395 add_attr_localids_id (Attrstore *s, unsigned int entry, Id name, LocalId id)
396 {
397   LongNV *nv = find_attr (s, entry, name);
398   if (nv)
399     {
400       unsigned len = 0;
401       while (nv->v.localids[len])
402         len++;
403       nv->v.localids = realloc (nv->v.localids, (len + 2) * sizeof (nv->v.localids[0]));
404       nv->v.localids[len] = id;
405       nv->v.localids[len+1] = 0;
406     }
407   else
408     {
409       LongNV mynv;
410       mynv.key = add_key (s, name, TYPE_ATTR_LOCALIDS, 0);
411       mynv.v.localids = malloc (2 * sizeof (mynv.v.localids[0]));
412       mynv.v.localids[0] = id;
413       mynv.v.localids[1] = 0;
414       add_attr (s, entry, mynv);
415     }
416 }
417
418 void
419 add_attr_void (Attrstore *s, unsigned int entry, Id name)
420 {
421   LongNV nv;
422   nv.key = add_key (s, name, TYPE_VOID, 0);
423   add_attr (s, entry, nv);
424 }
425
426 void
427 merge_attrs (Attrstore *s, unsigned dest, unsigned src)
428 {
429   LongNV *nv;
430   ensure_entry (s, dest);
431   nv = s->attrs[src];
432   if (nv)
433     {
434       for (; nv->key; nv++)
435         if (!find_attr (s, dest, s->keys[nv->key].name))
436           switch (s->keys[nv->key].type)
437             {
438               case TYPE_ATTR_INTLIST:
439                 {
440                   unsigned len = 0;
441                   while (nv->v.intlist[len])
442                     add_attr_intlist_int (s, dest, s->keys[nv->key].name, nv->v.intlist[len++]);
443                 }
444                 break;
445               case TYPE_ATTR_LOCALIDS:
446                 {
447                   unsigned len = 0;
448                   while (nv->v.localids[len])
449                     add_attr_localids_id (s, dest, s->keys[nv->key].name, nv->v.localids[len++]);
450                 }
451                 break;
452               case TYPE_ATTR_STRING:
453                 add_attr_string (s, dest, s->keys[nv->key].name, nv->v.str);
454                 break;
455               default:
456                 add_attr (s, dest, *nv);
457                 break;
458             }
459     }
460 }
461
462 #define pool_debug(a,b,...) fprintf (stderr, __VA_ARGS__)
463
464 static Id read_id (FILE *fp, Id max);
465
466 /* This routine is used only when attributes are embedded into the
467    normal repo SOLV file.  */
468 void
469 add_attr_from_file (Attrstore *s, unsigned entry, Id name, int type, Id *idmap, unsigned maxid, FILE *fp)
470 {
471   Pool *pool = s->pool;
472   //fprintf (stderr, "%s: attribute in a repo SOLV?\n", id2str (pool, name));
473   switch (type)
474     {
475       case TYPE_VOID:
476         add_attr_void (s, entry, name);
477         break;
478       case TYPE_ATTR_CHUNK:
479         {
480           unsigned ofs = read_id (fp, 0);
481           unsigned len = read_id (fp, 0);
482           add_attr_chunk (s, entry, name, ofs, len);
483         }
484         break;
485       case TYPE_ATTR_INT:
486         {
487           unsigned i = read_id(fp, 0);
488           add_attr_int (s, entry, name, i);
489         }
490         break;
491       case TYPE_ATTR_STRING:
492         {
493           unsigned char localbuf[1024];
494           int c;
495           unsigned char *buf = localbuf;
496           unsigned len = sizeof (localbuf);
497           unsigned ofs = 0;
498           while((c = getc (fp)) != 0)
499             {
500               if (c == EOF)
501                 {
502                   pool_debug (mypool, SAT_FATAL, "unexpected EOF\n");
503                   exit (1);
504                 }
505               /* Plus 1 as we also want to add the 0.  */
506               if (ofs + 1 >= len)
507                 {
508                   len += 256;
509                   if (buf == localbuf)
510                     {
511                       buf = xmalloc (len);
512                       memcpy (buf, localbuf, len - 256);
513                     }
514                   else
515                     buf = xrealloc (buf, len);
516                 }
517               buf[ofs++] = c;
518             }
519           buf[ofs++] = 0;
520           add_attr_string (s, entry, name, (char*) buf);
521           if (buf != localbuf)
522             xfree (buf);
523         }
524         break;
525       case TYPE_ATTR_INTLIST:
526         {
527           unsigned i;
528           while ((i = read_id(fp, 0)) != 0)
529             add_attr_intlist_int (s, entry, name, i);
530         }
531         break;
532       case TYPE_ATTR_LOCALIDS:
533         {
534           Id i;
535           /* The read ID will be pool-based.  */
536           while ((i = read_id(fp, maxid)) != 0)
537             {
538               if (idmap)
539                 i = idmap[i];
540               add_attr_localids_id (s, entry, name, str2localid (s, id2str (pool, i), 1));
541             }
542         }
543         break;
544       default:
545         if (type >= TYPE_ATTR_SPECIAL_START && type <= TYPE_ATTR_SPECIAL_END)
546           {
547             add_attr_special_int (s, entry, name, type - TYPE_ATTR_SPECIAL_START);
548             break;
549           }
550         pool_debug(pool, SAT_FATAL, "unknown type %d\n", type);
551         exit(0);
552     }
553 }
554
555 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
556    and are consecutive.  Return a pointer to the mapping of PSTART.  */
557 static const void *
558 load_page_range (Attrstore *s, unsigned int pstart, unsigned int pend)
559 {
560   unsigned char buf[BLOB_PAGESIZE];
561   unsigned int i;
562
563   /* Quick check in case all pages are there already and consecutive.  */
564   for (i = pstart; i <= pend; i++)
565     if (s->pages[i].mapped_at == -1
566         || (i > pstart
567             && s->pages[i].mapped_at
568                != s->pages[i-1].mapped_at + BLOB_PAGESIZE))
569       break;
570   if (i > pend)
571     return s->blob_store + s->pages[pstart].mapped_at;
572
573   /* Ensure that we can map the numbers of pages we need at all.  */
574   if (pend - pstart + 1 > s->ncanmap)
575     {
576       unsigned int oldcan = s->ncanmap;
577       s->ncanmap = pend - pstart + 1;
578       if (s->ncanmap < 4)
579         s->ncanmap = 4;
580       s->mapped = xrealloc (s->mapped, s->ncanmap * sizeof (s->mapped[0]));
581       memset (s->mapped + oldcan, 0, (s->ncanmap - oldcan) * sizeof (s->mapped[0]));
582       s->blob_store = xrealloc (s->blob_store, s->ncanmap * BLOB_PAGESIZE);
583 #ifdef DEBUG_PAGING
584       fprintf (stderr, "PAGE: can map %d pages\n", s->ncanmap);
585 #endif
586     }
587
588   /* Now search for "cheap" space in our store.  Space is cheap if it's either
589      free (very cheap) or contains pages we search for anyway.  */
590
591   /* Setup cost array.  */
592   unsigned int cost[s->ncanmap];
593   for (i = 0; i < s->ncanmap; i++)
594     {
595       unsigned int pnum = s->mapped[i];
596       if (pnum == 0)
597         cost[i] = 0;
598       else
599         {
600           pnum--;
601           Attrblobpage *p = s->pages + pnum;
602           assert (p->mapped_at != -1);
603           if (pnum >= pstart && pnum <= pend)
604             cost[i] = 1;
605           else
606             cost[i] = 3;
607         }
608     }
609
610   /* And search for cheapest space.  */
611   unsigned int best_cost = -1;
612   unsigned int best = 0;
613   unsigned int same_cost = 0;
614   for (i = 0; i + pend - pstart < s->ncanmap; i++)
615     {
616       unsigned int c = cost[i];
617       unsigned int j;
618       for (j = 0; j < pend - pstart + 1; j++)
619         c += cost[i+j];
620       if (c < best_cost)
621         best_cost = c, best = i;
622       else if (c == best_cost)
623         same_cost++;
624       /* A null cost won't become better.  */
625       if (c == 0)
626         break;
627     }
628   /* If all places have the same cost we would thrash on slot 0.  Avoid
629      this by doing a round-robin strategy in this case.  */
630   if (same_cost == s->ncanmap - pend + pstart - 1)
631     best = s->rr_counter++ % (s->ncanmap - pend + pstart);
632
633   /* So we want to map our pages from [best] to [best+pend-pstart].
634      Use a very simple strategy, which doesn't make the best use of
635      our resources, but works.  Throw away all pages in that range
636      (even ours) then copy around ours (in case they were outside the 
637      range) or read them in.  */
638   for (i = best; i < best + pend - pstart + 1; i++)
639     {
640       unsigned int pnum = s->mapped[i];
641       if (pnum--
642           /* If this page is exactly at the right place already,
643              no need to evict it.  */
644           && pnum != pstart + i - best)
645         {
646           /* Evict this page.  */
647 #ifdef DEBUG_PAGING
648           fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
649 #endif
650           cost[i] = 0;
651           s->mapped[i] = 0;
652           s->pages[pnum].mapped_at = -1;
653         }
654     }
655
656   /* Everything is free now.  Read in the pages we want.  */
657   for (i = pstart; i <= pend; i++)
658     {
659       Attrblobpage *p = s->pages + i;
660       unsigned int pnum = i - pstart + best;
661       void *dest = s->blob_store + pnum * BLOB_PAGESIZE;
662       if (p->mapped_at != -1)
663         {
664           if (p->mapped_at != pnum * BLOB_PAGESIZE)
665             {
666 #ifdef DEBUG_PAGING
667               fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
668 #endif
669               /* Still mapped somewhere else, so just copy it from there.  */
670               memcpy (dest, s->blob_store + p->mapped_at, BLOB_PAGESIZE);
671               s->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
672             }
673         }
674       else
675         {
676           unsigned int in_len = p->file_size;
677           unsigned int compressed = in_len & 1;
678           in_len >>= 1;
679 #ifdef DEBUG_PAGING
680           fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
681 #endif
682           /* Not mapped, so read in this page.  */
683           if (fseek (s->file, p->file_offset, SEEK_SET) < 0)
684             {
685               perror ("mapping fseek");
686               exit (1);
687             }
688           if (fread (compressed ? buf : dest, in_len, 1, s->file) != 1)
689             {
690               perror ("mapping fread");
691               exit (1);
692             }
693           if (compressed)
694             {
695               unsigned int out_len;
696               out_len = unchecked_decompress_buf (buf, in_len,
697                                                   dest, BLOB_PAGESIZE);
698               if (out_len != BLOB_PAGESIZE
699                   && i < s->num_pages - 1)
700                 {
701                   fprintf (stderr, "can't decompress\n");
702                   exit (1);
703                 }
704 #ifdef DEBUG_PAGING
705               fprintf (stderr, " (expand %d to %d)", in_len, out_len);
706 #endif
707             }
708 #ifdef DEBUG_PAGING
709           fprintf (stderr, "\n");
710 #endif
711         }
712       p->mapped_at = pnum * BLOB_PAGESIZE;
713       s->mapped[pnum] = i + 1;
714     }
715
716   return s->blob_store + best * BLOB_PAGESIZE;
717 }
718
719 const void *
720 attr_retrieve_blob (Attrstore *s, unsigned int ofs, unsigned int len)
721 {
722   if (s->file)
723     {
724       /* Paging!  Yeah!  */
725       unsigned int pstart = ofs / BLOB_PAGESIZE;
726       unsigned int pend = (ofs + len - 1) / BLOB_PAGESIZE;
727       const void *m = load_page_range (s, pstart, pend);
728       return m + (ofs & (BLOB_PAGESIZE - 1));
729     }
730   if (!s->blob_store)
731     return 0;
732   if (ofs >= s->blob_next_free)
733     return 0;
734   return s->blob_store + ofs;
735 }
736
737 #define FLAT_ATTR_BLOCK 127
738 #define KEY_BLOCK 127
739 #define SCHEMA_BLOCK 127
740
741 #define add_elem(buf,ofs,val,block) do { \
742   if (((ofs) & (block)) == 0) \
743     buf = xrealloc (buf, ((ofs) + (block) + 1) * sizeof((buf)[0])); \
744   (buf)[(ofs)++] = val; \
745 } while (0)
746 #define add_u16(buf,ofs,val,block) do {\
747   typedef int __wrong_buf__[(1-sizeof((buf)[0])) * (sizeof((buf)[0])-1)];\
748   add_elem(buf,ofs,(val) & 0xFF,block); \
749   add_elem(buf,ofs,((val) >> 8) & 0xFF,block); \
750 } while (0)
751 #define add_num(buf,ofs,val,block) do {\
752   typedef int __wrong_buf__[(1-sizeof((buf)[0])) * (sizeof((buf)[0])-1)];\
753   if ((val) >= (1 << 14)) \
754     { \
755       if ((val) >= (1 << 28)) \
756         add_elem (buf,ofs,((val) >> 28) | 128, block); \
757       if ((val) >= (1 << 21)) \
758         add_elem (buf,ofs,((val) >> 21) | 128, block); \
759       add_elem (buf,ofs,((val) >> 14) | 128, block); \
760     } \
761   if ((val) >= (1 << 7)) \
762     add_elem (buf,ofs,((val) >> 7) | 128, block); \
763   add_elem (buf,ofs,(val) & 127, block); \
764 } while (0)
765
766 static int
767 longnv_cmp (const void *pa, const void *pb)
768 {
769   const LongNV *a = (const LongNV *)pa;
770   const LongNV *b = (const LongNV *)pb;
771   return a->key - b->key;
772 }
773
774 static Id
775 add_key (Attrstore *s, Id name, unsigned type, unsigned size)
776 {
777   unsigned i;
778   for (i = 0; i < s->nkeys; i++)
779     if (s->keys[i].name == name && s->keys[i].type == type)
780       break;
781   if (i < s->nkeys)
782     {
783       s->keys[i].size += size;
784       return i;
785     }
786   if ((s->nkeys & KEY_BLOCK) == 0)
787     s->keys = xrealloc (s->keys, (s->nkeys + KEY_BLOCK + 1) * sizeof (s->keys[0]));
788   s->keys[i].name = name;
789   s->keys[i].type = type;
790   s->keys[i].size = size;
791   return s->nkeys++;
792 }
793
794 void
795 attr_store_pack (Attrstore *s)
796 {
797   unsigned i;
798   unsigned int old_mem = 0;
799   if (s->packed)
800     return;
801   s->ent2attr = xcalloc (s->entries, sizeof (s->ent2attr[0]));
802   s->flat_attrs = 0;
803   s->attr_next_free = 0;
804   s->nschemata = 0;
805   s->szschemata = 0;
806   s->schemata = 0;
807   s->schemaofs = 0;
808
809   add_num (s->flat_attrs, s->attr_next_free, 0, FLAT_ATTR_BLOCK);
810   add_elem (s->schemata, s->szschemata, 0, SCHEMA_BLOCK);
811   add_elem (s->schemaofs, s->nschemata, 0, SCHEMA_BLOCK);
812
813   for (i = 0; i < s->entries; i++)
814     {
815       unsigned int num_attrs = 0, ofs;
816       LongNV *nv = s->attrs[i];
817       if (nv)
818         while (nv->key)
819           nv++, num_attrs++;
820       if (nv)
821         old_mem += (num_attrs + 1) * sizeof (LongNV);
822       if (!num_attrs)
823         continue;
824       nv = s->attrs[i];
825       qsort (s->attrs[i], num_attrs, sizeof (LongNV), longnv_cmp);
826       unsigned int this_schema;
827       for (this_schema = 0; this_schema < s->nschemata; this_schema++)
828         {
829           for (ofs = 0; ofs < num_attrs; ofs++)
830             {
831               Id key = nv[ofs].key;
832               assert (s->schemaofs[this_schema] + ofs < s->szschemata);
833               if (key != s->schemata[s->schemaofs[this_schema]+ofs])
834                 break;
835             }
836           if (ofs == num_attrs && !s->schemata[s->schemaofs[this_schema]+ofs])
837             break;
838         }
839       if (this_schema == s->nschemata)
840         {
841           /* This schema not found --> insert it.  */
842           add_elem (s->schemaofs, s->nschemata, s->szschemata, SCHEMA_BLOCK);
843           for (ofs = 0; ofs < num_attrs; ofs++)
844             {
845               Id key = nv[ofs].key;
846               add_elem (s->schemata, s->szschemata, key, SCHEMA_BLOCK);
847             }
848           add_elem (s->schemata, s->szschemata, 0, SCHEMA_BLOCK);
849         }
850       s->ent2attr[i] = s->attr_next_free;
851       add_num (s->flat_attrs, s->attr_next_free, this_schema, FLAT_ATTR_BLOCK);
852       for (ofs = 0; ofs < num_attrs; ofs++)
853         switch (s->keys[nv[ofs].key].type)
854           {
855             case TYPE_VOID:
856               break;
857             case TYPE_ATTR_INT:
858               {
859                 unsigned int i = nv[ofs].v.i[0];
860                 add_num (s->flat_attrs, s->attr_next_free, i, FLAT_ATTR_BLOCK);
861                 break;
862               }
863             case TYPE_ATTR_CHUNK:
864               {
865                 unsigned int i = nv[ofs].v.i[0];
866                 add_num (s->flat_attrs, s->attr_next_free, i, FLAT_ATTR_BLOCK);
867                 i = nv[ofs].v.i[1];
868                 add_num (s->flat_attrs, s->attr_next_free, i, FLAT_ATTR_BLOCK);
869                 break;
870               }
871             case TYPE_ATTR_STRING:
872               {
873                 const char *str = nv[ofs].v.str;
874                 for (; *str; str++)
875                   add_elem (s->flat_attrs, s->attr_next_free, *str, FLAT_ATTR_BLOCK);
876                 add_elem (s->flat_attrs, s->attr_next_free, 0, FLAT_ATTR_BLOCK);
877                 old_mem += strlen ((const char*)nv[ofs].v.str) + 1;
878                 xfree ((void*)nv[ofs].v.str);
879                 break;
880               }
881             case TYPE_ATTR_INTLIST:
882               {
883                 const int *il = nv[ofs].v.intlist;
884                 int i;
885                 for (; (i = *il) != 0; il++, old_mem += 4)
886                   add_num (s->flat_attrs, s->attr_next_free, i, FLAT_ATTR_BLOCK);
887                 add_num (s->flat_attrs, s->attr_next_free, 0, FLAT_ATTR_BLOCK);
888                 old_mem+=4;
889                 xfree (nv[ofs].v.intlist);
890                 break;
891               }
892             case TYPE_ATTR_LOCALIDS:
893               {
894                 const Id *il = nv[ofs].v.localids;
895                 Id i;
896                 for (; (i = *il) != 0; il++, old_mem += 4)
897                   add_num (s->flat_attrs, s->attr_next_free, i, FLAT_ATTR_BLOCK);
898                 add_num (s->flat_attrs, s->attr_next_free, 0, FLAT_ATTR_BLOCK);
899                 old_mem += 4;
900                 xfree (nv[ofs].v.localids);
901                 break;
902               }
903             default:
904               break;
905           }
906       xfree (nv);
907     }
908   old_mem += s->entries * sizeof (s->attrs[0]);
909   free (s->attrs);
910   s->attrs = 0;
911
912   /* Remove the hashtable too, it will be build on demand in str2localid
913      the next time we call it, which should not happen while in packed mode.  */
914   old_mem += (s->ss.stringhashmask + 1) * sizeof (s->ss.stringhashtbl[0]);
915   free (s->ss.stringhashtbl);
916   s->ss.stringhashtbl = 0;
917   s->ss.stringhashmask = 0;
918
919   fprintf (stderr, "%d\n", old_mem);
920   fprintf (stderr, "%zd\n", s->entries * sizeof(s->ent2attr[0]));
921   fprintf (stderr, "%d\n", s->attr_next_free);
922   fprintf (stderr, "%zd\n", s->nschemata * sizeof(s->schemaofs[0]));
923   fprintf (stderr, "%zd\n", s->szschemata * sizeof(s->schemata[0]));
924   fprintf (stderr, "pages %d\n", s->num_pages);
925   s->packed = 1;
926 }
927
928 /* Pages in all blob pages, and deactivates paging.  */
929 static void
930 pagein_all (Attrstore *s)
931 {
932   /* If we have no backing file everything is there already.  */
933   if (!s->file)
934     return;
935   /*fprintf (stderr, "Aieee!\n");
936   exit (1);*/
937 }
938
939 void
940 attr_store_unpack (Attrstore *s)
941 {
942   unsigned int i;
943   if (!s->packed)
944     return;
945
946   pagein_all (s);
947
948   /* Make the store writable right away, so we can use our adder functions.  */
949   s->packed = 0;
950   s->attrs = xcalloc (s->entries, sizeof (s->attrs[0]));
951
952   for (i = 0; i < s->entries; i++)
953     {
954       attr_iterator ai;
955       FOR_ATTRS (s, i, &ai)
956         {
957           switch (ai.type)
958             {
959             case TYPE_VOID:
960               add_attr_void (s, i, ai.name);
961               break;
962             case TYPE_ATTR_INT:
963               add_attr_int (s, i, ai.name, ai.as_int); 
964               break;
965             case TYPE_ATTR_CHUNK:
966               add_attr_chunk (s, i, ai.name, ai.as_chunk[0], ai.as_chunk[1]);
967               break;
968             case TYPE_ATTR_STRING:
969               add_attr_string (s, i, ai.name, ai.as_string);
970               break;
971             case TYPE_ATTR_INTLIST:
972               {
973                 while (1)
974                   {
975                     int val;
976                     get_num (ai.as_numlist, val);
977                     if (!val)
978                       break;
979                     add_attr_intlist_int (s, i, ai.name, val);
980                   }
981                 break;
982               }
983             case TYPE_ATTR_LOCALIDS:
984               {
985                 while (1)
986                   {
987                     Id val;
988                     get_num (ai.as_numlist, val);
989                     if (!val)
990                       break;
991                     add_attr_localids_id (s, i, ai.name, val);
992                   }
993                 break;
994               }
995             default:
996               if (ai.type >= TYPE_ATTR_SPECIAL_START
997                   && ai.type <= TYPE_ATTR_SPECIAL_END)
998                 add_attr_special_int (s, i, ai.name, ai.type - TYPE_ATTR_SPECIAL_START);
999               break;
1000             }
1001         }
1002     }
1003
1004   xfree (s->ent2attr);
1005   s->ent2attr = 0;
1006   xfree (s->flat_attrs);
1007   s->flat_attrs = 0;
1008   s->attr_next_free = 0;
1009   xfree (s->schemaofs);
1010   s->schemaofs = 0;
1011   s->nschemata = 0;
1012   xfree (s->schemata);
1013   s->schemata = 0;
1014   s->szschemata = 0;
1015 }
1016
1017 static void
1018 write_u8(FILE *fp, unsigned int x)
1019 {
1020   if (putc(x, fp) == EOF)
1021     {
1022       perror("write error");
1023       exit(1);
1024     }
1025 }
1026
1027 static void
1028 write_u32(FILE *fp, unsigned int x)
1029 {
1030   if (putc(x >> 24, fp) == EOF ||
1031       putc(x >> 16, fp) == EOF ||
1032       putc(x >> 8, fp) == EOF ||
1033       putc(x, fp) == EOF)
1034     {
1035       perror("write error");
1036       exit(1);
1037     }
1038 }
1039
1040 static void
1041 write_id(FILE *fp, Id x)
1042 {
1043   if (x >= (1 << 14))
1044     {
1045       if (x >= (1 << 28))
1046         putc((x >> 28) | 128, fp);
1047       if (x >= (1 << 21))
1048         putc((x >> 21) | 128, fp);
1049       putc((x >> 14) | 128, fp);
1050     }
1051   if (x >= (1 << 7))
1052     putc((x >> 7) | 128, fp);
1053   if (putc(x & 127, fp) == EOF)
1054     {
1055       perror("write error");
1056       exit(1);
1057     }
1058 }
1059
1060 static Id *
1061 write_idarray(FILE *fp, Id *ids)
1062 {
1063   Id id;
1064   if (!ids)
1065     return ids;
1066   if (!*ids)
1067     {
1068       write_u8(fp, 0);
1069       return ids + 1;
1070     }
1071   for (;;)
1072     {
1073       id = *ids++;
1074       if (id >= 64)
1075         id = (id & 63) | ((id & ~63) << 1);
1076       if (!*ids)
1077         {
1078           write_id(fp, id);
1079           return ids + 1;
1080         }
1081       write_id(fp, id | 64);
1082     }
1083 }
1084
1085 static void
1086 write_pages (FILE *fp, Attrstore *s)
1087 {
1088   unsigned int i;
1089   unsigned char buf[BLOB_PAGESIZE];
1090
1091   /* The compressed pages in the file have different sizes, so we need
1092      to store these sizes somewhere, either in front of all page data,
1093      interleaved with the page data (in front of each page), or after
1094      the page data.  At this point we don't yet know the final compressed
1095      sizes.  These are the pros and cons:
1096      * in front of all page data
1097        + when reading back we only have to read this header, and know
1098          where every page data is placed
1099        - we have to compress all pages first before starting to write them.
1100          Our output stream might be unseekable, so we can't simply
1101          reserve space for the header, write all pages and then update the
1102          header.  This needs memory for all compressed pages at once.
1103      * interleaved with page data
1104        + we can compress and write per page, low memory overhead
1105        - when reading back we have to read at least those numbers,
1106          thereby either having to read all page data, or at least seek
1107          over it.
1108      * after all page data
1109        + we can do streamed writing, remembering the sizes per page,
1110          and emitting the header (which is a footer then) at the end
1111        - reading back is hardest: before the page data we don't know
1112          how long it is overall, so we have to put that information
1113          also at the end, but it needs a determinate position, so can
1114          only be at a known offset from the end.  But that means that
1115          we must be able to seek when reading back.  We have this
1116          wish anyway in case we want to use on-demand paging then, but
1117          it's optional.
1118
1119      Of all these it seems the best good/bad ratio is with the interleaved
1120      storage.  No memory overhead at writing and no unreasonable limitations
1121      for read back.  */
1122   write_u32 (fp, s->blob_next_free);
1123   write_u32 (fp, BLOB_PAGESIZE);
1124   assert (((s->blob_next_free + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE) == s->num_pages);
1125   for (i = 0; i < s->num_pages; i++)
1126     {
1127       unsigned int in_len;
1128       unsigned int out_len;
1129       const void *in;
1130       if (i == s->num_pages - 1)
1131         in_len = s->blob_next_free & (BLOB_PAGESIZE - 1);
1132       else
1133         in_len = BLOB_PAGESIZE;
1134       if (in_len)
1135         {
1136           in = attr_retrieve_blob (s, i * BLOB_PAGESIZE, in_len);
1137           out_len = compress_buf (in, in_len, buf, in_len - 1);
1138           if (!out_len)
1139             {
1140               memcpy (buf, in, in_len);
1141               out_len = in_len;
1142             }
1143         }
1144       else
1145         out_len = 0;
1146 #ifdef DEBUG_PAGING
1147       fprintf (stderr, "page %d: %d -> %d\n", i, in_len, out_len);
1148 #endif
1149       write_u32 (fp, out_len * 2 + (out_len != in_len));
1150       if (out_len
1151           && fwrite (buf, out_len, 1, fp) != 1)
1152         {
1153           perror("write error");
1154           exit(1);
1155         }
1156     }
1157 }
1158
1159 void
1160 write_attr_store (FILE *fp, Attrstore *s)
1161 {
1162   unsigned i;
1163   unsigned local_ssize;
1164
1165   attr_store_pack (s);
1166
1167   /* Transform our attribute names (pool string IDs) into local IDs.  */
1168   for (i = 1; i < s->nkeys; i++)
1169     s->keys[i].name = str2localid (s, id2str (s->pool, s->keys[i].name), 1);
1170
1171   /* write file header */
1172   write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1173   write_u32(fp, SOLV_VERSION_2);
1174
1175   /* write counts */
1176   write_u32(fp, s->ss.nstrings);      // nstrings
1177   write_u32(fp, 0);                   // nrels
1178   write_u32(fp, s->entries);          // nsolvables
1179   write_u32(fp, s->nkeys);
1180   write_u32(fp, s->nschemata);
1181   write_u32(fp, 0);     /* no info block */
1182   unsigned solv_flags = 0;
1183   solv_flags |= SOLV_FLAG_PACKEDSIZES;
1184   //solv_flags |= SOLV_FLAG_PREFIX_POOL;
1185   write_u32(fp, solv_flags);
1186
1187   for (i = 1, local_ssize = 0; i < (unsigned)s->ss.nstrings; i++)
1188     local_ssize += strlen (localid2str (s, i)) + 1;
1189
1190   write_u32 (fp, local_ssize);
1191   for (i = 1; i < (unsigned)s->ss.nstrings; i++)
1192     {
1193       const char *str = localid2str (s, i);
1194       if (fwrite(str, strlen(str) + 1, 1, fp) != 1)
1195         {
1196           perror("write error");
1197           exit(1);
1198         }
1199     }
1200
1201   for (i = 1; i < s->nkeys; i++)
1202     {
1203       write_id (fp, s->keys[i].name);
1204       write_id (fp, s->keys[i].type);
1205       write_id (fp, s->keys[i].size);
1206
1207       /* Also transform back the names (now local IDs) into pool IDs,
1208          so we can use the pool also after writing.  */
1209       s->keys[i].name = str2id (s->pool, localid2str (s, s->keys[i].name), 0);
1210     }
1211
1212   write_id (fp, s->szschemata);
1213   Id *ids = s->schemata + 0;
1214   for (i = 0; i < s->nschemata; i++)
1215     ids = write_idarray (fp, ids);
1216   assert (ids == s->schemata + s->szschemata);
1217
1218   /* Convert our offsets into sizes.  */
1219   unsigned end = s->attr_next_free;
1220   for (i = s->entries; i > 0;)
1221     {
1222       i--;
1223       if (s->ent2attr[i])
1224         {
1225           s->ent2attr[i] = end - s->ent2attr[i];
1226           end = end - s->ent2attr[i];
1227         }
1228     }
1229   /* The first zero should not have been consumed, but everything else.  */
1230   assert (end == 1);
1231   /* Write the sizes and convert back to offsets.  */
1232   unsigned start = 1;
1233   for (i = 0; i < s->entries; i++)
1234     {
1235       write_id (fp, s->ent2attr[i]);
1236       if (s->ent2attr[i])
1237         s->ent2attr[i] += start, start = s->ent2attr[i];
1238     }
1239
1240   if (s->entries
1241       && fwrite (s->flat_attrs + 1, s->attr_next_free - 1, 1, fp) != 1)
1242     {
1243       perror ("write error");
1244       exit (1);
1245     }
1246
1247   write_pages (fp, s);
1248 }
1249
1250 static unsigned int
1251 read_u32(FILE *fp)
1252 {
1253   int c, i;
1254   unsigned int x = 0;
1255
1256   for (i = 0; i < 4; i++)
1257     {
1258       c = getc(fp);
1259       if (c == EOF)
1260         {
1261           fprintf(stderr, "unexpected EOF\n");
1262           exit(1);
1263         }
1264       x = (x << 8) | c;
1265     }
1266   return x;
1267 }
1268
1269 static Id
1270 read_id(FILE *fp, Id max)
1271 {
1272   unsigned int x = 0;
1273   int c, i;
1274
1275   for (i = 0; i < 5; i++)
1276     {
1277       c = getc(fp);
1278       if (c == EOF)
1279         {
1280           fprintf(stderr, "unexpected EOF\n");
1281           exit(1);
1282         }
1283       if (!(c & 128))
1284         {
1285           x = (x << 7) | c;
1286           if (max && x >= max)
1287             {
1288               fprintf(stderr, "read_id: id too large (%u/%u)\n", x, max);
1289               exit(1);
1290             }
1291           return x;
1292         }
1293       x = (x << 7) ^ c ^ 128;
1294     }
1295   fprintf(stderr, "read_id: id too long\n");
1296   exit(1);
1297 }
1298
1299 static Id *
1300 read_idarray(FILE *fp, Id max, Id *map, Id *store, Id *end, int relative)
1301 {
1302   unsigned int x = 0;
1303   int c;
1304   Id old = 0;
1305   for (;;)
1306     {
1307       c = getc(fp);
1308       if (c == EOF)
1309         {
1310           pool_debug(mypool, SAT_FATAL, "unexpected EOF\n");
1311           exit(1);
1312         }
1313       if ((c & 128) == 0)
1314         {
1315           x = (x << 6) | (c & 63);
1316           if (relative)
1317             {
1318               if (x == 0 && c == 0x40)
1319                 {
1320                   /* prereq hack */
1321                   if (store == end)
1322                     {
1323                       pool_debug(mypool, SAT_FATAL, "read_idarray: array overflow\n");
1324                       exit(1);
1325                     }
1326                   *store++ = SOLVABLE_PREREQMARKER;
1327                   old = 0;
1328                   x = 0;
1329                   continue;
1330                 }
1331               x = (x - 1) + old;
1332               old = x;
1333             }
1334           if (x >= max)
1335             {
1336               pool_debug(mypool, SAT_FATAL, "read_idarray: id too large (%u/%u)\n", x, max);
1337               exit(1);
1338             }
1339           if (map)
1340             x = map[x];
1341           if (store == end)
1342             {
1343               pool_debug(mypool, SAT_FATAL, "read_idarray: array overflow\n");
1344               exit(1);
1345             }
1346           *store++ = x;
1347           if ((c & 64) == 0)
1348             {
1349               if (x == 0)       /* already have trailing zero? */
1350                 return store;
1351               if (store == end)
1352                 {
1353                   pool_debug(mypool, SAT_FATAL, "read_idarray: array overflow\n");
1354                   exit(1);
1355                 }
1356               *store++ = 0;
1357               return store;
1358             }
1359           x = 0;
1360           continue;
1361         }
1362       x = (x << 7) ^ c ^ 128;
1363     }
1364 }
1365
1366 /* Try to either setup on-demand paging (using FP as backing
1367    file), or in case that doesn't work (FP not seekable) slurps in
1368    all pages and deactivates paging.  */
1369 void
1370 read_or_setup_pages (FILE *fp, Attrstore *s)
1371 {
1372   unsigned int blobsz;
1373   unsigned int pagesz;
1374   unsigned int npages;
1375   unsigned int i;
1376   unsigned int can_seek;
1377   long cur_file_ofs;
1378   unsigned char buf[BLOB_PAGESIZE];
1379   blobsz = read_u32 (fp);
1380   pagesz = read_u32 (fp);
1381   if (pagesz != BLOB_PAGESIZE)
1382     {
1383       /* We could handle this by slurping in everything.  */
1384       fprintf (stderr, "non matching page size\n");
1385       exit (1);
1386     }
1387   can_seek = 1;
1388   if ((cur_file_ofs = ftell (fp)) < 0)
1389     can_seek = 0;
1390   clearerr (fp);
1391   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1392   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1393
1394   s->num_pages = npages;
1395   s->pages = xmalloc (npages * sizeof (s->pages[0]));
1396
1397   /* If we can't seek on our input we have to slurp in everything.  */
1398   if (!can_seek)
1399     {
1400       s->blob_next_free = blobsz;
1401       s->blob_store = xrealloc (s->blob_store, (s->blob_next_free + BLOB_BLOCK) &~BLOB_BLOCK);
1402     }
1403   for (i = 0; i < npages; i++)
1404     {
1405       unsigned int in_len = read_u32 (fp);
1406       unsigned int compressed = in_len & 1;
1407       Attrblobpage *p = s->pages + i;
1408       in_len >>= 1;
1409 #ifdef DEBUG_PAGING
1410       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1411                i, in_len, compressed ? "" : "not ");
1412 #endif
1413       if (can_seek)
1414         {
1415           cur_file_ofs += 4;
1416           p->mapped_at = -1;
1417           p->file_offset = cur_file_ofs;
1418           p->file_size = in_len * 2 + compressed;
1419           if (fseek (fp, in_len, SEEK_CUR) < 0)
1420             {
1421               perror ("fseek");
1422               fprintf (stderr, "can't seek after we thought we can\n");
1423               /* We can't fall back to non-seeking behaviour as we already
1424                  read over some data pages without storing them away.  */
1425               exit (1);
1426             }
1427           cur_file_ofs += in_len;
1428         }
1429       else
1430         {
1431           unsigned int out_len;
1432           void *dest = s->blob_store + i * BLOB_PAGESIZE;
1433           p->mapped_at = i * BLOB_PAGESIZE;
1434           p->file_offset = 0;
1435           p->file_size = 0;
1436           /* We can't seek, so suck everything in.  */
1437           if (fread (compressed ? buf : dest, in_len, 1, fp) != 1)
1438             {
1439               perror ("fread");
1440               exit (1);
1441             }
1442           if (compressed)
1443             {
1444               out_len = unchecked_decompress_buf (buf, in_len,
1445                                                   dest, BLOB_PAGESIZE);
1446               if (out_len != BLOB_PAGESIZE
1447                   && i < npages - 1)
1448                 {
1449                   fprintf (stderr, "can't decompress\n");
1450                   exit (1);
1451                 }
1452             }
1453         }
1454     }
1455
1456   if (can_seek)
1457     {
1458       /* If we are here we were able to seek to all page
1459          positions, so activate paging by copying FP into our structure.
1460          We dup() the file, so that our callers can fclose() it and we
1461          still have it open.  But this means that we share file positions
1462          with the input filedesc.  So in case our caller reads it after us,
1463          and calls back into us we might change the file position unexpectedly
1464          to him.  */
1465       int fd = dup (fileno (fp));
1466       if (fd < 0)
1467         {
1468           /* Jeez!  What a bloody system, we can't dup() anymore.  */
1469           perror ("dup");
1470           exit (1);
1471         }
1472       /* XXX we don't close this yet anywhere.  */
1473       s->file = fdopen (fd, "r");
1474       if (!s->file)
1475         {
1476           /* My God!  What happened now?  */
1477           perror ("fdopen");
1478           exit (1);
1479         }
1480     }
1481 }
1482
1483 Attrstore *
1484 attr_store_read (FILE *fp, Pool *pool)
1485 {
1486   unsigned nentries;
1487   unsigned i;
1488   unsigned local_ssize;
1489   unsigned nstrings, nschemata;
1490   Attrstore *s = new_store (pool);
1491
1492   if (read_u32(fp) != ('S' << 24 | 'O' << 16 | 'L' << 8 | 'V'))
1493     {
1494       pool_debug(pool, SAT_FATAL, "not a SOLV file\n");
1495       exit(1);
1496     }
1497   unsigned solvversion = read_u32(fp);
1498   switch (solvversion)
1499     {
1500       case SOLV_VERSION_2:
1501         break;
1502       default:
1503         pool_debug(pool, SAT_FATAL, "unsupported SOLV version\n");
1504         exit(1);
1505     }
1506
1507   nstrings = read_u32(fp);
1508   read_u32(fp); //nrels
1509   nentries = read_u32(fp);
1510   s->nkeys = read_u32(fp);
1511   nschemata = read_u32(fp);
1512   read_u32(fp); //ninfo
1513   unsigned solvflags = read_u32(fp);
1514   if (!(solvflags & SOLV_FLAG_PACKEDSIZES))
1515     {
1516       pool_debug(pool, SAT_FATAL, "invalid attribute store\n");
1517       exit (1);
1518     }
1519
1520   /* Slightly hacky.  Our local string pool already contains "<NULL>" and
1521      "".  We write out the "" too, so we have to read over it.  We write it
1522      out to be compatible with the SOLV file and to not have to introduce
1523      merging and mapping the string IDs.  */
1524   local_ssize = read_u32 (fp) - 1;
1525   char *strsp = (char *)xrealloc(s->ss.stringspace, s->ss.sstrings + local_ssize + 1);
1526   Offset *str = (Offset *)xrealloc(s->ss.strings, (nstrings) * sizeof(Offset));
1527
1528   s->ss.stringspace = strsp;
1529   s->ss.strings = str;
1530   strsp += s->ss.sstrings;
1531
1532   unsigned char ignore_char = 1;
1533   if (fread(&ignore_char, 1, 1, fp) != 1
1534       || (local_ssize && fread(strsp, local_ssize, 1, fp) != 1)
1535       || ignore_char != 0)
1536     {
1537       perror ("read error while reading strings");
1538       exit(1);
1539     }
1540   strsp[local_ssize] = 0;
1541
1542   /* Don't build hashtable here, it will be built on demand by str2localid
1543      should we call that.  */
1544
1545   strsp = s->ss.stringspace;
1546   s->ss.nstrings = nstrings;
1547   for (i = 0; i < nstrings; i++)
1548     {
1549       str[i] = strsp - s->ss.stringspace;
1550       strsp += strlen (strsp) + 1;
1551     }
1552   s->ss.sstrings = strsp - s->ss.stringspace;
1553
1554   s->keys = xrealloc (s->keys, ((s->nkeys + KEY_BLOCK) & ~KEY_BLOCK) * sizeof (s->keys[0]));
1555   /* s->keys[0] is initialized in new_store.  */
1556   for (i = 1; i < s->nkeys; i++)
1557     {
1558       s->keys[i].name = read_id (fp, nstrings);
1559       s->keys[i].type = read_id (fp, TYPE_ATTR_TYPE_MAX + 1);
1560       s->keys[i].size = read_id (fp, 0);
1561
1562       /* Globalize the attribute names (they are local IDs right now).  */
1563       s->keys[i].name = str2id (s->pool, localid2str (s, s->keys[i].name), 1);
1564     }
1565
1566   s->szschemata = read_id (fp, 0);
1567   s->nschemata = 0;
1568   s->schemata = xmalloc (((s->szschemata + SCHEMA_BLOCK) & ~SCHEMA_BLOCK) * sizeof (s->schemata[0]));
1569   s->schemaofs = 0;
1570   Id *ids = s->schemata;
1571   //add_elem (s->schemaofs, s->nschemata, 0, SCHEMA_BLOCK);
1572   //*ids++ = 0;
1573   while (ids < s->schemata + s->szschemata)
1574     {
1575       add_elem (s->schemaofs, s->nschemata, ids - s->schemata, SCHEMA_BLOCK);
1576       ids = read_idarray (fp, s->nkeys, 0, ids, s->schemata + s->szschemata, 0);
1577     }
1578   assert (ids == s->schemata + s->szschemata);
1579   assert (nschemata == s->nschemata);
1580
1581   s->entries = nentries;
1582
1583   s->ent2attr = xmalloc (s->entries * sizeof (s->ent2attr[0]));
1584   int start = 1;
1585   for (i = 0; i < s->entries; i++)
1586     {
1587       int d = read_id (fp, 0);
1588       if (d)
1589         s->ent2attr[i] = start, start += d;
1590       else
1591         s->ent2attr[i] = 0;
1592     }
1593
1594   s->attr_next_free = start;
1595   s->flat_attrs = xmalloc (((s->attr_next_free + FLAT_ATTR_BLOCK) & ~FLAT_ATTR_BLOCK) * sizeof (s->flat_attrs[0]));
1596   s->flat_attrs[0] = 0;
1597   if (s->entries && fread (s->flat_attrs + 1, s->attr_next_free - 1, 1, fp) != 1)
1598     {
1599       perror ("read error");
1600       exit (1);
1601     }
1602
1603   read_or_setup_pages (fp, s);
1604
1605   s->packed = 1;
1606
1607   return s;
1608 }
1609
1610 void
1611 attr_store_search_s (Attrstore *s, const char *pattern, int flags, Id name, cb_attr_search_s cb)
1612 {
1613   unsigned int i;
1614   attr_iterator ai;
1615   regex_t regex;
1616   /* If we search for a glob, but we don't have a wildcard pattern, make this
1617      an exact string search.  */
1618   if ((flags & 7) == SEARCH_GLOB
1619       && !strpbrk (pattern, "?*["))
1620     flags = SEARCH_STRING | (flags & ~7);
1621   if ((flags & 7) == SEARCH_REGEX)
1622     {
1623       /* We feed multiple lines eventually (e.g. authors or descriptions),
1624          so set REG_NEWLINE.  */
1625       if (regcomp (&regex, pattern,
1626                    REG_EXTENDED | REG_NOSUB | REG_NEWLINE
1627                    | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0)) != 0)
1628         return;
1629     }
1630   for (i = 0; i < s->entries; i++)
1631     FOR_ATTRS (s, i, &ai)
1632       {
1633         const char *str;
1634         if (name && name != ai.name)
1635           continue;
1636         str = 0;
1637         switch (ai.type)
1638           {
1639           case TYPE_ATTR_INT:
1640           case TYPE_ATTR_INTLIST:
1641             continue;
1642           case TYPE_ATTR_CHUNK:
1643             if (!(flags & SEARCH_BLOBS))
1644               continue;
1645             str = attr_retrieve_blob (s, ai.as_chunk[0], ai.as_chunk[1]);
1646             break;
1647           case TYPE_ATTR_STRING:
1648             str = ai.as_string;
1649             break;
1650           case TYPE_ATTR_LOCALIDS:
1651             {
1652               Id val;
1653               get_num (ai.as_numlist, val);
1654               if (val)
1655                 str = localid2str (s, val);
1656               break;
1657             }
1658           default:
1659             break;
1660           }
1661         while (str)
1662           {
1663             unsigned int match = 0;
1664             switch (flags & 7)
1665             {
1666               case SEARCH_SUBSTRING:
1667                 if (flags & SEARCH_NOCASE)
1668                   match = !! strcasestr (str, pattern);
1669                 else
1670                   match = !! strstr (str, pattern);
1671                 break;
1672               case SEARCH_STRING:
1673                 if (flags & SEARCH_NOCASE)
1674                   match = ! strcasecmp (str, pattern);
1675                 else
1676                   match = ! strcmp (str, pattern);
1677                 break;
1678               case SEARCH_GLOB:
1679                 match = ! fnmatch (pattern, str,
1680                                    (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
1681                 break;
1682               case SEARCH_REGEX:
1683                 match = ! regexec (&regex, str, 0, NULL, 0);
1684                 break;
1685               default:
1686                 break;
1687             }
1688             if (match)
1689               cb (s, i, ai.name, str);
1690             if (ai.type != TYPE_ATTR_LOCALIDS)
1691               break;
1692             Id val;
1693             get_num (ai.as_numlist, val);
1694             if (!val)
1695               break;
1696             str = localid2str (s, val);
1697           }
1698       }
1699   if ((flags & 7) == SEARCH_REGEX)
1700     regfree (&regex);
1701 }
1702
1703 #ifdef MAIN
1704 int
1705 main (void)
1706 {
1707   Pool *pool = pool_create ();
1708   Attrstore *s = new_store (pool);
1709   unsigned int id1 = new_entry (s);
1710   unsigned int id2 = new_entry (s);
1711   unsigned int id3 = new_entry (s);
1712   unsigned int id4 = new_entry (s);
1713   add_attr_int (s, id1, str2id (s, "name1", 1), 42);
1714   add_attr_chunk (s, id1, str2id (s->pool, "name2", 1), 9876, 1024);
1715   add_attr_string (s, id1, str2id (s->pool, "name3", 1), "hallo");
1716   add_attr_int (s, id1, str2id (s->pool, "name1", 1), 43);
1717   add_attr_intlist_int (s, id1, str2id (s->pool, "intlist1", 1), 3);
1718   add_attr_intlist_int (s, id1, str2id (s->pool, "intlist1", 1), 14);
1719   add_attr_intlist_int (s, id1, str2id (s->pool, "intlist1", 1), 1);
1720   add_attr_intlist_int (s, id1, str2id (s->pool, "intlist1", 1), 59);
1721   add_attr_localids_id (s, id1, str2id (s->pool, "l_ids1", 1), str2localid (s, "one", 1));
1722   add_attr_localids_id (s, id1, str2id (s->pool, "l_ids1", 1), str2localid (s, "two", 1));
1723   add_attr_localids_id (s, id1, str2id (s->pool, "l_ids1", 1), str2localid (s, "three", 1));
1724   add_attr_localids_id (s, id1, str2id (s->pool, "l_ids2", 1), str2localid (s, "three", 1));
1725   add_attr_localids_id (s, id1, str2id (s->pool, "l_ids2", 1), str2localid (s, "two", 1));
1726   add_attr_localids_id (s, id1, str2id (s->pool, "l_ids2", 1), str2localid (s, "one", 1));
1727   write_attr_store (stdout, s);
1728   return 0;
1729 }
1730 #endif