summaryrefslogtreecommitdiffstats
path: root/lib/dpkg/parse.c
blob: 9ff3b686f41797231e534994e50225e46ff2651b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
/*
 * libdpkg - Debian packaging suite library routines
 * parse.c - database file parsing, main package/field loop
 *
 * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
 * Copyright © 2006, 2008-2015 Guillem Jover <guillem@debian.org>
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <config.h>
#include <compat.h>

#include <sys/types.h>
#include <sys/stat.h>
#ifdef USE_MMAP
#include <sys/mman.h>
#endif

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>

#include <dpkg/macros.h>
#include <dpkg/i18n.h>
#include <dpkg/c-ctype.h>
#include <dpkg/dpkg.h>
#include <dpkg/dpkg-db.h>
#include <dpkg/string.h>
#include <dpkg/pkg.h>
#include <dpkg/parsedump.h>
#include <dpkg/fdio.h>
#include <dpkg/buffer.h>

/**
 * Fields information.
 */
const struct fieldinfo fieldinfos[]= {
  /* Note: Capitalization of field name strings is important. */
  { FIELD("Package"),          f_name,            w_name                                     },
  { FIELD("Essential"),        f_boolean,         w_booleandefno,   PKGIFPOFF(essential)     },
  { FIELD("Protected"),        f_boolean,         w_booleandefno,   PKGIFPOFF(is_protected)  },
  { FIELD("Status"),           f_status,          w_status                                   },
  { FIELD("Priority"),         f_priority,        w_priority                                 },
  { FIELD("Section"),          f_section,         w_section                                  },
  { FIELD("Installed-Size"),   f_charfield,       w_charfield,      PKGIFPOFF(installedsize) },
  { FIELD("Origin"),           f_charfield,       w_charfield,      PKGIFPOFF(origin)        },
  { FIELD("Maintainer"),       f_charfield,       w_charfield,      PKGIFPOFF(maintainer)    },
  { FIELD("Bugs"),             f_charfield,       w_charfield,      PKGIFPOFF(bugs)          },
  { FIELD("Architecture"),     f_architecture,    w_architecture                             },
  { FIELD("Multi-Arch"),       f_multiarch,       w_multiarch,      PKGIFPOFF(multiarch)     },
  { FIELD("Source"),           f_charfield,       w_charfield,      PKGIFPOFF(source)        },
  { FIELD("Version"),          f_version,         w_version,        PKGIFPOFF(version)       },
  { FIELD("Config-Version"),   f_configversion,   w_configversion                            },
  { FIELD("Replaces"),         f_dependency,      w_dependency,     dep_replaces             },
  { FIELD("Provides"),         f_dependency,      w_dependency,     dep_provides             },
  { FIELD("Depends"),          f_dependency,      w_dependency,     dep_depends              },
  { FIELD("Pre-Depends"),      f_dependency,      w_dependency,     dep_predepends           },
  { FIELD("Recommends"),       f_dependency,      w_dependency,     dep_recommends           },
  { FIELD("Suggests"),         f_dependency,      w_dependency,     dep_suggests             },
  { FIELD("Breaks"),           f_dependency,      w_dependency,     dep_breaks               },
  { FIELD("Conflicts"),        f_dependency,      w_dependency,     dep_conflicts            },
  { FIELD("Enhances"),         f_dependency,      w_dependency,     dep_enhances             },
  { FIELD("Conffiles"),        f_conffiles,       w_conffiles                                },
  { FIELD("Filename"),         f_archives,        w_archives,       ARCHIVEFOFF(name)        },
  { FIELD("Size"),             f_archives,        w_archives,       ARCHIVEFOFF(size)        },
  { FIELD("MD5sum"),           f_archives,        w_archives,       ARCHIVEFOFF(md5sum)      },
  { FIELD("MSDOS-Filename"),   f_archives,        w_archives,       ARCHIVEFOFF(msdosname)   },
  { FIELD("Description"),      f_charfield,       w_charfield,      PKGIFPOFF(description)   },
  { FIELD("Triggers-Pending"), f_trigpend,        w_trigpend                                 },
  { FIELD("Triggers-Awaited"), f_trigaw,          w_trigaw                                   },

  /* The following are the obsolete fields that get remapped to their
   * modern forms, while emitting an obsolescence warning. */
  { FIELD("Recommended"),      f_obs_dependency,  w_null,           dep_recommends           },
  { FIELD("Optional"),         f_obs_dependency,  w_null,           dep_suggests             },
  { FIELD("Class"),            f_obs_class,       w_null                                     },
  { FIELD("Revision"),         f_obs_revision,    w_null                                     },
  { FIELD("Package-Revision"), f_obs_revision,    w_null                                     },
  { FIELD("Package_Revision"), f_obs_revision,    w_null                                     },
  { NULL                                                                                     }
};

/**
 * Package object being parsed.
 *
 * Structure used to hold the parsed data for the package being constructed,
 * before it gets properly inserted into the package database.
 */
struct pkg_parse_object {
  struct pkginfo *pkg;
  struct pkgbin *pkgbin;
};

/**
 * Parse the field and value into the package being constructed.
 */
static void
pkg_parse_field(struct parsedb_state *ps, struct field_state *fs,
                void *parse_obj)
{
  struct pkg_parse_object *pkg_obj = parse_obj;
  const struct fieldinfo *fip;
  int *ip;

  for (fip = fieldinfos, ip = fs->fieldencountered; fip->name; fip++, ip++)
    if (fip->namelen == (size_t)fs->fieldlen &&
        strncasecmp(fip->name, fs->fieldstart, fs->fieldlen) == 0)
      break;
  if (fip->name) {
    if ((*ip)++)
      parse_error(ps,
                  _("duplicate value for '%s' field"), fip->name);

    varbuf_reset(&fs->value);
    varbuf_add_buf(&fs->value, fs->valuestart, fs->valuelen);
    varbuf_end_str(&fs->value);

    fip->rcall(pkg_obj->pkg, pkg_obj->pkgbin, ps, fs->value.buf, fip);
  } else {
    struct arbitraryfield *arp, **larpp;

    if (fs->fieldlen < 2)
      parse_error(ps,
                  _("user-defined field name '%.*s' too short"),
                  fs->fieldlen, fs->fieldstart);
    larpp = &pkg_obj->pkgbin->arbs;
    while ((arp = *larpp) != NULL) {
      if (strncasecmp(arp->name, fs->fieldstart, fs->fieldlen) == 0 &&
          strlen(arp->name) == (size_t)fs->fieldlen)
        parse_error(ps,
                   _("duplicate value for user-defined field '%.*s'"),
                   fs->fieldlen, fs->fieldstart);
      larpp = &arp->next;
    }
    arp = nfmalloc(sizeof(*arp));
    arp->name = nfstrnsave(fs->fieldstart, fs->fieldlen);
    arp->value = nfstrnsave(fs->valuestart, fs->valuelen);
    arp->next = NULL;
    *larpp = arp;
  }
}

/**
 * Verify and fixup the package structure being constructed.
 */
static void
pkg_parse_verify(struct parsedb_state *ps,
                 struct pkginfo *pkg, struct pkgbin *pkgbin)
{
  struct dependency *dep;
  struct deppossi *dop;

  parse_must_have_field(ps, pkg->set->name, "Package");

  /* XXX: We need to check for status != PKG_STAT_HALFINSTALLED as while
   * unpacking an unselected package, it will not have yet all data in
   * place. But we cannot check for > PKG_STAT_HALFINSTALLED as
   * PKG_STAT_CONFIGFILES always should have those fields. */
  if ((ps->flags & pdb_recordavailable) ||
      (pkg->status != PKG_STAT_NOTINSTALLED &&
       pkg->status != PKG_STAT_HALFINSTALLED)) {
    parse_ensure_have_field(ps, &pkgbin->description, "Description");
    parse_ensure_have_field(ps, &pkgbin->maintainer, "Maintainer");
    parse_must_have_field(ps, pkgbin->version.version, "Version");
  }

  /* XXX: Versions before dpkg 1.10.19 did not preserve the Architecture
   * field in the status file. So there's still live systems with packages
   * in PKG_STAT_CONFIGFILES, ignore those too for now. */
  if ((ps->flags & pdb_recordavailable) ||
      pkg->status > PKG_STAT_HALFINSTALLED) {
    /* We always want usable architecture information (as long as the package
     * is in such a state that it makes sense), so that it can be used safely
     * on string comparisons and the like. */
    if (pkgbin->arch->type == DPKG_ARCH_NONE)
      parse_warn(ps, _("missing '%s' field"), "Architecture");
    else if (pkgbin->arch->type == DPKG_ARCH_EMPTY)
      parse_warn(ps, _("empty value for '%s' field"), "Architecture");
  }
  /* Mark missing architectures as empty, to distinguish these from
   * unused slots in the db. */
  if (pkgbin->arch->type == DPKG_ARCH_NONE)
    pkgbin->arch = dpkg_arch_get(DPKG_ARCH_EMPTY);

  if (pkgbin->arch->type == DPKG_ARCH_EMPTY &&
      pkgbin->multiarch == PKG_MULTIARCH_SAME)
    parse_error(ps, _("package has '%s' field but is missing architecture"),
                "Multi-Arch: same");
  if (pkgbin->arch->type == DPKG_ARCH_ALL &&
      pkgbin->multiarch == PKG_MULTIARCH_SAME)
    parse_error(ps, _("package has '%s' field but is architecture '%s'"),
                "Multi-Arch: same", "all");

  /* Generate the cached fully qualified package name representation. */
  pkgbin->pkgname_archqual = pkgbin_name_archqual(pkg, pkgbin);

  /* Initialize deps to be arch-specific unless stated otherwise. */
  for (dep = pkgbin->depends; dep; dep = dep->next)
    for (dop = dep->list; dop; dop = dop->next)
      if (!dop->arch)
        dop->arch = pkgbin->arch;

  /*
   * Check the Config-Version information:
   *
   * If there is a Config-Version it is definitely to be used, but there
   * should not be one if the package is ‘installed’ or ‘triggers-pending’
   * (in which case the Version will be copied) or if the package is
   * ‘not-installed’ (in which case there is no Config-Version).
   */
  if (!(ps->flags & pdb_recordavailable)) {
    if (pkg->configversion.version) {
      if (pkg->status == PKG_STAT_INSTALLED ||
          pkg->status == PKG_STAT_NOTINSTALLED ||
          pkg->status == PKG_STAT_TRIGGERSPENDING)
        parse_error(ps,
                    _("'%s' field present for package with inappropriate '%s' field"),
                    "Config-Version", "Status");
    } else {
      if (pkg->status == PKG_STAT_INSTALLED ||
          pkg->status == PKG_STAT_TRIGGERSPENDING)
        pkg->configversion = pkgbin->version;
    }
  }

  if (pkg->trigaw.head &&
      (pkg->status <= PKG_STAT_CONFIGFILES ||
       pkg->status >= PKG_STAT_TRIGGERSPENDING))
    parse_error(ps,
                _("package has status %s but triggers are awaited"),
                pkg_status_name(pkg));
  else if (pkg->status == PKG_STAT_TRIGGERSAWAITED && !pkg->trigaw.head)
    parse_error(ps,
                _("package has status %s but no triggers awaited"),
                pkg_status_name(pkg));

  if (pkg->trigpend_head &&
      !(pkg->status == PKG_STAT_TRIGGERSPENDING ||
        pkg->status == PKG_STAT_TRIGGERSAWAITED))
    parse_error(ps,
                _("package has status %s but triggers are pending"),
                pkg_status_name(pkg));
  else if (pkg->status == PKG_STAT_TRIGGERSPENDING && !pkg->trigpend_head)
    parse_error(ps,
                _("package has status %s but no triggers pending"),
                pkg_status_name(pkg));

  /* Note: There was a bug that could make a not-installed package have
   * conffiles, so we check for them here and remove them (rather than
   * calling it an error, which will do at some point). */
  if (!(ps->flags & pdb_recordavailable) &&
      pkg->status == PKG_STAT_NOTINSTALLED &&
      pkgbin->conffiles) {
    parse_warn(ps,
               _("package has status %s and has conffiles, forgetting them"),
               pkg_status_name(pkg));
    pkgbin->conffiles = NULL;
  }

  /* Note: Mark not-installed leftover packages for automatic removal on
   * next database dump. */
  if (!(ps->flags & pdb_recordavailable) &&
      pkg->status == PKG_STAT_NOTINSTALLED &&
      pkg->eflag == PKG_EFLAG_OK &&
      (pkg->want == PKG_WANT_PURGE ||
       pkg->want == PKG_WANT_DEINSTALL)) {
    pkg_set_want(pkg, PKG_WANT_UNKNOWN);
  }

  /* Note: Mark not-installed non-arch-qualified selections for automatic
   * removal, as they do not make sense in a multiarch enabled world, and
   * might cause those selections to be unreferencable from command-line
   * interfaces when there's other more specific selections. */
  if (ps->type == pdb_file_status &&
      pkg->status == PKG_STAT_NOTINSTALLED &&
      pkg->eflag == PKG_EFLAG_OK &&
      pkg->want == PKG_WANT_INSTALL &&
      pkgbin->arch->type == DPKG_ARCH_EMPTY)
    pkg_set_want(pkg, PKG_WANT_UNKNOWN);

  /* Note: Versions before dpkg 1.13.10 did not blank the Origin and Bugs
   * fields, so there can be packages that should be garbage collected but
   * are lingering around. Blank them to make sure we will forget all about
   * them on the next database dump. */
  if (!(ps->flags & pdb_recordavailable) &&
      pkg->status == PKG_STAT_NOTINSTALLED &&
      pkg->eflag == PKG_EFLAG_OK &&
      pkg->want == PKG_WANT_UNKNOWN) {
    pkgbin_blank(pkgbin);
  }
}

struct pkgcount {
  int single;
  int multi;
  int total;
};

static void
parse_count_pkg_instance(struct pkgcount *count,
                         struct pkginfo *pkg, struct pkgbin *pkgbin)
{
  if (pkg->status == PKG_STAT_NOTINSTALLED)
     return;

  if (pkgbin->multiarch == PKG_MULTIARCH_SAME)
    count->multi++;
  else
    count->single++;

  count->total++;
}

/**
 * Lookup the package set slot for the parsed package.
 *
 * Perform various checks, to make sure the database is always in a sane
 * state, and to not allow breaking it.
 */
static struct pkgset *
parse_find_set_slot(struct parsedb_state *ps,
                    struct pkginfo *new_pkg, struct pkgbin *new_pkgbin)
{
  struct pkgcount count = { .single = 0, .multi = 0, .total = 0 };
  struct pkgset *set;
  struct pkginfo *pkg;

  set = pkg_hash_find_set(new_pkg->set->name);

  /* Sanity checks: verify that the db is in a consistent state. */

  if (ps->type == pdb_file_status)
    parse_count_pkg_instance(&count, new_pkg, new_pkgbin);

  count.total = 0;

  for (pkg = &set->pkg; pkg; pkg = pkg->arch_next)
    parse_count_pkg_instance(&count, pkg, &pkg->installed);

  if (count.single > 1)
    parse_error(ps, _("multiple non-coinstallable package instances present; "
                      "most probably due to an upgrade from an unofficial dpkg"));

  if (count.single > 0 && count.multi > 0)
    parse_error(ps, _("mixed non-coinstallable and coinstallable package "
                      "instances present; most probably due to an upgrade "
                      "from an unofficial dpkg"));

  if (pkgset_installed_instances(set) != count.total)
    internerr("in-core pkgset '%s' with inconsistent number of instances",
              set->name);

  return set;
}

/**
 * Lookup the package slot for the parsed package.
 *
 * Cross-grading (i.e. switching arch) is only possible when parsing an
 * update entry or when installing a new package.
 *
 * Most of the time each pkginfo in a pkgset has the same architecture for
 * both the installed and available pkgbin members. But when cross-grading
 * there's going to be a temporary discrepancy, because we reuse the single
 * instance and fill the available pkgbin with the candidate pkgbin, until
 * that is copied over the installed pkgbin.
 *
 * If there's 0 or > 1 package instances, then we match against the pkginfo
 * slot architecture, because cross-grading is just not possible.
 *
 * If there's 1 instance, we are cross-grading and both installed and
 * candidate are not PKG_MULTIARCH_SAME, we have to reuse the existing single
 * slot regardless of the arch differing between the two. If we are not
 * cross-grading, then we use the entry with the matching arch.
 */
static struct pkginfo *
parse_find_pkg_slot(struct parsedb_state *ps,
                    struct pkginfo *new_pkg, struct pkgbin *new_pkgbin)
{
  struct pkgset *db_set;
  struct pkginfo *db_pkg;

  db_set = parse_find_set_slot(ps, new_pkg, new_pkgbin);

  if (ps->type == pdb_file_available) {
    /* If there's a single package installed and the new package is not
     * “Multi-Arch: same”, then we preserve the previous behaviour of
     * possible architecture switch, for example from native to all. */
    if (pkgset_installed_instances(db_set) == 1 &&
        new_pkgbin->multiarch != PKG_MULTIARCH_SAME)
      return pkg_hash_get_singleton(db_set);
    else
      return pkg_hash_get_pkg(db_set, new_pkgbin->arch);
  } else {
    bool selection = false;

    /* If the package is part of the status file, and it's not installed
     * then this means it's just a selection. */
    if (ps->type == pdb_file_status && new_pkg->status == PKG_STAT_NOTINSTALLED)
      selection = true;

    /* Verify we don't allow something that will mess up the db. */
    if (pkgset_installed_instances(db_set) > 1 &&
        !selection && new_pkgbin->multiarch != PKG_MULTIARCH_SAME)
      ohshit(_("package %s (%s) with field '%s: %s' is not co-installable "
               "with %s which has multiple installed instances"),
             pkgbin_name(new_pkg, new_pkgbin, pnaw_always),
             versiondescribe(&new_pkgbin->version, vdew_nonambig),
             "Multi-Arch", multiarchinfos[new_pkgbin->multiarch].name,
             db_set->name);

    /* If we are parsing the status file, use a slot per arch. */
    if (ps->type == pdb_file_status)
      return pkg_hash_get_pkg(db_set, new_pkgbin->arch);

    /* If we are doing an update, from the log or a new package, then
     * handle cross-grades. */
    if (pkgset_installed_instances(db_set) == 1) {
      db_pkg = pkg_hash_get_singleton(db_set);

      if (db_pkg->installed.multiarch == PKG_MULTIARCH_SAME &&
          new_pkgbin->multiarch == PKG_MULTIARCH_SAME)
        return pkg_hash_get_pkg(db_set, new_pkgbin->arch);
      else
        return db_pkg;
    } else {
      return pkg_hash_get_pkg(db_set, new_pkgbin->arch);
    }
  }
}

/**
 * Copy into the in-core database the package being constructed.
 */
static void
pkg_parse_copy(struct parsedb_state *ps,
               struct pkginfo *dst_pkg, struct pkgbin *dst_pkgbin,
               struct pkginfo *src_pkg, struct pkgbin *src_pkgbin)
{
  /* Copy the priority and section across, but don't overwrite existing
   * values if the pdb_weakclassification flag is set. */
  if (str_is_set(src_pkg->section) &&
      !((ps->flags & pdb_weakclassification) &&
        str_is_set(dst_pkg->section)))
    dst_pkg->section = src_pkg->section;
  if (src_pkg->priority != PKG_PRIO_UNKNOWN &&
      !((ps->flags & pdb_weakclassification) &&
        dst_pkg->priority != PKG_PRIO_UNKNOWN)) {
    dst_pkg->priority = src_pkg->priority;
    if (src_pkg->priority == PKG_PRIO_OTHER)
      dst_pkg->otherpriority = src_pkg->otherpriority;
  }

  /* Sort out the dependency mess. */
  copy_dependency_links(dst_pkg, &dst_pkgbin->depends, src_pkgbin->depends,
                        (ps->flags & pdb_recordavailable) ? true : false);

  /* Copy across data. */
  memcpy(dst_pkgbin, src_pkgbin, sizeof(struct pkgbin));
  if (!(ps->flags & pdb_recordavailable)) {
    struct trigaw *ta;

    pkg_set_want(dst_pkg, src_pkg->want);
    pkg_copy_eflags(dst_pkg, src_pkg);
    pkg_set_status(dst_pkg, src_pkg->status);
    dst_pkg->configversion = src_pkg->configversion;
    dst_pkg->archives = NULL;

    dst_pkg->trigpend_head = src_pkg->trigpend_head;
    dst_pkg->trigaw = src_pkg->trigaw;
    for (ta = dst_pkg->trigaw.head; ta; ta = ta->sameaw.next) {
      if (ta->aw != src_pkg)
        internerr("trigger awaited package %s and origin package %s not linked properly",
                  pkg_name(ta->aw, pnaw_always),
                  pkgbin_name(src_pkg, src_pkgbin, pnaw_always));
      ta->aw = dst_pkg;
      /* ->othertrigaw_head is updated by trig_note_aw in *(pkg_hash_find())
       * rather than in dst_pkg. */
    }
  } else if (!(ps->flags & pdb_ignore_archives)) {
    dst_pkg->archives = src_pkg->archives;
  }
}

/**
 * Return a descriptive parser type.
 */
static enum parsedbtype
parse_get_type(struct parsedb_state *ps, enum parsedbflags flags)
{
  if (flags & pdb_recordavailable) {
    if (flags & pdb_single_stanza)
      return pdb_file_control;
    else
      return pdb_file_available;
  } else {
    if (flags & pdb_single_stanza)
      return pdb_file_update;
    else
      return pdb_file_status;
  }
}

/**
 * Create a new deb822 parser context.
 */
struct parsedb_state *
parsedb_new(const char *filename, int fd, enum parsedbflags flags)
{
  struct parsedb_state *ps;

  ps = m_malloc(sizeof(*ps));
  ps->err = DPKG_ERROR_OBJECT;
  ps->errmsg = VARBUF_OBJECT;
  ps->filename = filename;
  ps->type = parse_get_type(ps, flags);
  ps->flags = flags;
  ps->fd = fd;
  ps->lno = 0;
  ps->data = NULL;
  ps->dataptr = NULL;
  ps->endptr = NULL;
  ps->pkg = NULL;
  ps->pkgbin = NULL;

  return ps;
}

/**
 * Open a file for deb822 parsing.
 */
struct parsedb_state *
parsedb_open(const char *filename, enum parsedbflags flags)
{
  struct parsedb_state *ps;
  int fd;

  /* Special case stdin handling. */
  if (flags & pdb_dash_is_stdin && strcmp(filename, "-") == 0)
    return parsedb_new(filename, STDIN_FILENO, flags);

  fd = open(filename, O_RDONLY);
  if (fd == -1 && !(errno == ENOENT && (flags & pdb_allow_empty)))
    ohshite(_("failed to open package info file '%.255s' for reading"),
            filename);

  ps = parsedb_new(filename, fd, flags | pdb_close_fd);

  push_cleanup(cu_closefd, ~ehflag_normaltidy, 1, &ps->fd);

  return ps;
}

/**
 * Load data for package deb822 style parsing.
 */
void
parsedb_load(struct parsedb_state *ps)
{
  struct stat st;

  if (ps->fd < 0 && (ps->flags & pdb_allow_empty))
      return;

  if (fstat(ps->fd, &st) == -1)
    ohshite(_("can't stat package info file '%.255s'"), ps->filename);

  if (S_ISFIFO(st.st_mode)) {
    struct varbuf buf = VARBUF_INIT;
    struct dpkg_error err;
    off_t size;

    size = fd_vbuf_copy(ps->fd, &buf, -1, &err);
    if (size < 0)
      ohshit(_("reading package info file '%s': %s"), ps->filename, err.str);

    varbuf_end_str(&buf);

    ps->dataptr = varbuf_detach(&buf);
    ps->endptr = ps->dataptr + size;
  } else if (st.st_size > 0) {
#ifdef USE_MMAP
    ps->dataptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, ps->fd, 0);
    if (ps->dataptr == MAP_FAILED)
      ohshite(_("can't mmap package info file '%.255s'"), ps->filename);
#else
    ps->dataptr = m_malloc(st.st_size);

    if (fd_read(ps->fd, ps->dataptr, st.st_size) < 0)
      ohshite(_("reading package info file '%.255s'"), ps->filename);
#endif
    ps->endptr = ps->dataptr + st.st_size;
  } else {
    ps->dataptr = ps->endptr = NULL;
  }
  ps->data = ps->dataptr;
}

/**
 * Parse an RFC-822 style stanza.
 */
bool
parse_stanza(struct parsedb_state *ps, struct field_state *fs,
             parse_field_func *parse_field, void *parse_obj)
{
  int c;

  /* Skip adjacent new lines. */
  while (!parse_at_eof(ps)) {
    c = parse_getc(ps);
    if (c != '\n' && c != MSDOS_EOF_CHAR)
      break;
    ps->lno++;
  }

  /* Nothing relevant parsed, bail out. */
  if (parse_at_eof(ps))
    return false;

  /* Loop per field. */
  for (;;) {
    bool blank_line;

    /* Scan field name. */
    fs->fieldstart = ps->dataptr - 1;
    while (!parse_at_eof(ps) && !c_isspace(c) && c != ':' && c != MSDOS_EOF_CHAR)
      c = parse_getc(ps);
    fs->fieldlen = ps->dataptr - fs->fieldstart - 1;
    if (fs->fieldlen == 0)
      parse_error(ps,  _("empty field name"));
    if (fs->fieldstart[0] == '-')
      parse_error(ps,  _("field name '%.*s' cannot start with hyphen"),
                  fs->fieldlen, fs->fieldstart);

    /* Skip spaces before ‘:’. */
    while (!parse_at_eof(ps) && c != '\n' && c_isspace(c))
      c = parse_getc(ps);

    /* Validate ‘:’. */
    if (parse_at_eof(ps))
      parse_error(ps, _("end of file after field name '%.*s'"),
                  fs->fieldlen, fs->fieldstart);
    if (c == '\n')
      parse_error(ps,
                  _("newline in field name '%.*s'"), fs->fieldlen, fs->fieldstart);
    if (c == MSDOS_EOF_CHAR)
      parse_error(ps, _("MSDOS end of file (^Z) in field name '%.*s'"),
                  fs->fieldlen, fs->fieldstart);
    if (c != ':')
      parse_error(ps,
                  _("field name '%.*s' must be followed by colon"),
                  fs->fieldlen, fs->fieldstart);

    /* Skip space after ‘:’ but before value and EOL. */
    while (!parse_at_eof(ps)) {
      c = parse_getc(ps);
      if (c == '\n' || !c_isspace(c))
        break;
    }
    if (parse_at_eof(ps))
      parse_error(ps, _("end of file before value of field '%.*s' (missing final newline)"),
                  fs->fieldlen, fs->fieldstart);
    if (c == MSDOS_EOF_CHAR)
      parse_error(ps, _("MSDOS end of file (^Z) in value of field '%.*s' (missing newline?)"),
                  fs->fieldlen, fs->fieldstart);

    blank_line = false;

    /* Scan field value. */
    fs->valuestart = ps->dataptr - 1;
    for (;;) {
      if (c == '\n' || c == MSDOS_EOF_CHAR) {
        if (blank_line) {
          if (ps->flags & pdb_lax_stanza_parser)
            parse_warn(ps, _("blank line in value of field '%.*s'"),
                       fs->fieldlen, fs->fieldstart);
          else
            parse_error(ps, _("blank line in value of field '%.*s'"),
                        fs->fieldlen, fs->fieldstart);
        }
        ps->lno++;

        if (parse_at_eof(ps))
          break;
        c = parse_getc(ps);

        /* Found double EOL, or start of new field. */
        if (parse_at_eof(ps) || c == '\n' || !c_isspace(c))
          break;

        parse_ungetc(c, ps);
        blank_line = true;
      } else if (blank_line && !c_isspace(c)) {
        blank_line = false;
      }

      if (parse_at_eof(ps))
        parse_error(ps, _("end of file during value of field '%.*s' (missing final newline)"),
                    fs->fieldlen, fs->fieldstart);

      c = parse_getc(ps);
    }
    fs->valuelen = ps->dataptr - fs->valuestart - 1;

    /* Trim ending space on value. */
    while (fs->valuelen && c_isspace(*(fs->valuestart + fs->valuelen - 1)))
      fs->valuelen--;

    parse_field(ps, fs, parse_obj);

    if (parse_at_eof(ps) || c == '\n' || c == MSDOS_EOF_CHAR)
      break;
  } /* Loop per field. */

  if (c == '\n')
    ps->lno++;

  return true;
}

/**
 * Teardown a package deb822 parser context.
 */
void
parsedb_close(struct parsedb_state *ps)
{
  if (ps->flags & pdb_close_fd) {
    pop_cleanup(ehflag_normaltidy);

    if (ps->fd >= 0 && close(ps->fd) < 0)
      ohshite(_("failed to close after read: '%.255s'"), ps->filename);
  }

  if (ps->data != NULL) {
#ifdef USE_MMAP
    munmap(ps->data, ps->endptr - ps->data);
#else
    free(ps->data);
#endif
  }
  dpkg_error_destroy(&ps->err);
  varbuf_destroy(&ps->errmsg);
  free(ps);
}

/**
 * Parse deb822 style package data from a buffer.
 *
 * donep may be NULL.
 * If donep is not NULL only one package's information is expected.
 */
int
parsedb_parse(struct parsedb_state *ps, struct pkginfo **donep)
{
  struct pkgset tmp_set;
  struct pkginfo *new_pkg, *db_pkg;
  struct pkgbin *new_pkgbin, *db_pkgbin;
  struct pkg_parse_object pkg_obj;
  int fieldencountered[array_count(fieldinfos)];
  int pdone;
  struct field_state fs;

  if (ps->data == NULL && (ps->flags & pdb_allow_empty))
    return 0;

  memset(&fs, 0, sizeof(fs));
  fs.fieldencountered = fieldencountered;

  new_pkg = &tmp_set.pkg;
  if (ps->flags & pdb_recordavailable)
    new_pkgbin = &new_pkg->available;
  else
    new_pkgbin = &new_pkg->installed;

  ps->pkg = new_pkg;
  ps->pkgbin = new_pkgbin;

  pkg_obj.pkg = new_pkg;
  pkg_obj.pkgbin = new_pkgbin;

  pdone= 0;

  /* Loop per package. */
  for (;;) {
    memset(fieldencountered, 0, sizeof(fieldencountered));
    pkgset_blank(&tmp_set);

    if (!parse_stanza(ps, &fs, pkg_parse_field, &pkg_obj))
      break;

    if (pdone && donep)
      parse_error(ps,
                  _("several package info entries found, only one allowed"));

    pkg_parse_verify(ps, new_pkg, new_pkgbin);

    db_pkg = parse_find_pkg_slot(ps, new_pkg, new_pkgbin);
    if (ps->flags & pdb_recordavailable)
      db_pkgbin = &db_pkg->available;
    else
      db_pkgbin = &db_pkg->installed;

    if (((ps->flags & pdb_ignoreolder) || ps->type == pdb_file_available) &&
        dpkg_version_is_informative(&db_pkgbin->version) &&
        dpkg_version_compare(&new_pkgbin->version, &db_pkgbin->version) < 0)
      continue;

    pkg_parse_copy(ps, db_pkg, db_pkgbin, new_pkg, new_pkgbin);

    if (donep)
      *donep = db_pkg;
    pdone++;
    if (parse_at_eof(ps))
      break;
  }

  varbuf_destroy(&fs.value);
  if (donep && !pdone)
    ohshit(_("no package information in '%.255s'"), ps->filename);

  return pdone;
}

/**
 * Parse a deb822 style file.
 *
 * donep may be NULL.
 * If donep is not NULL only one package's information is expected.
 */
int
parsedb(const char *filename, enum parsedbflags flags, struct pkginfo **pkgp)
{
  struct parsedb_state *ps;
  int count;

  ps = parsedb_open(filename, flags);
  parsedb_load(ps);
  count = parsedb_parse(ps, pkgp);
  parsedb_close(ps);

  return count;
}

/**
 * Copy dependency links structures.
 *
 * This routine is used to update the ‘reverse’ dependency pointers when
 * new ‘forwards’ information has been constructed. It first removes all
 * the links based on the old information. The old information starts in
 * *updateme; after much brou-ha-ha the reverse structures are created
 * and *updateme is set to the value from newdepends.
 *
 * @param pkg The package we're doing this for. This is used to construct
 *        correct uplinks.
 * @param updateme The forwards dependency pointer that we are to update.
 *        This starts out containing the old forwards info, which we use to
 *        unthread the old reverse links. After we're done it is updated.
 * @param newdepends The value that we ultimately want to have in updateme.
 * @param available The pkgbin to modify, available or installed.
 *
 * It is likely that the backward pointer for the package in question
 * (‘depended’) will be updated by this routine, but this will happen by
 * the routine traversing the dependency data structures. It doesn't need
 * to be told where to update that; just mentioned here as something that
 * one should be cautious about.
 */
void copy_dependency_links(struct pkginfo *pkg,
                           struct dependency **updateme,
                           struct dependency *newdepends,
                           bool available)
{
  struct dependency *dyp;
  struct deppossi *dop, **revdeps;

  /* Delete ‘backward’ (‘depended’) links from other packages to
   * dependencies listed in old version of this one. We do this by
   * going through all the dependencies in the old version of this
   * one and following them down to find which deppossi nodes to
   * remove. */
  for (dyp= *updateme; dyp; dyp= dyp->next) {
    for (dop= dyp->list; dop; dop= dop->next) {
      if (dop->rev_prev)
        dop->rev_prev->rev_next = dop->rev_next;
      else
        if (available)
          dop->ed->depended.available = dop->rev_next;
        else
          dop->ed->depended.installed = dop->rev_next;
      if (dop->rev_next)
        dop->rev_next->rev_prev = dop->rev_prev;
    }
  }

  /* Now fill in new ‘ed’ links from other packages to dependencies
   * listed in new version of this one, and set our uplinks correctly. */
  for (dyp= newdepends; dyp; dyp= dyp->next) {
    dyp->up= pkg;
    for (dop= dyp->list; dop; dop= dop->next) {
      revdeps = available ? &dop->ed->depended.available :
                            &dop->ed->depended.installed;
      dop->rev_next = *revdeps;
      dop->rev_prev = NULL;
      if (*revdeps)
        (*revdeps)->rev_prev = dop;
      *revdeps = dop;
    }
  }

  /* Finally, we fill in the new value. */
  *updateme= newdepends;
}