chiark / gitweb /
lib/dpkg/tarfn.c: Kludge `tar_header_decode' to handle spurious `errno'.
[dpkg] / dselect / pkglist.cc
1 /*
2  * dselect - Debian package maintenance user interface
3  * pkglist.cc - package list administration
4  *
5  * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
6  * Copyright © 2001 Wichert Akkerman <wakkerma@debian.org>
7  * Copyright © 2008-2014 Guillem Jover <guillem@debian.org>
8  *
9  * This is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21  */
22
23 #include <config.h>
24 #include <compat.h>
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31
32 #include <dpkg/i18n.h>
33 #include <dpkg/dpkg.h>
34 #include <dpkg/dpkg-db.h>
35 #include <dpkg/string.h>
36
37 #include "dselect.h"
38 #include "bindings.h"
39
40 int packagelist::compareentries(const struct perpackagestate *a,
41                                 const struct perpackagestate *b) {
42   switch (statsortorder) {
43   case sso_avail:
44     if (a->ssavail != b->ssavail) return a->ssavail - b->ssavail;
45     break;
46   case sso_state:
47     if (a->ssstate != b->ssstate) return a->ssstate - b->ssstate;
48     break;
49   case sso_unsorted:
50     break;
51   default:
52     internerr("unknown statsortorder %d", statsortorder);
53   }
54
55   const char *asection= a->pkg->section;
56   if (!asection && a->pkg->set->name)
57     asection = "";
58   const char *bsection= b->pkg->section;
59   if (!bsection && b->pkg->set->name)
60     bsection = "";
61   int c_section=
62     !asection || !bsection ?
63       (!bsection) - (!asection) :
64     !*asection || !*bsection ?
65       (!*asection) - (!*bsection) :
66     strcasecmp(asection,bsection);
67   int c_priority=
68     a->pkg->priority - b->pkg->priority;
69   if (!c_priority && a->pkg->priority == PKG_PRIO_OTHER)
70     c_priority= strcasecmp(a->pkg->otherpriority, b->pkg->otherpriority);
71   int c_name=
72     a->pkg->set->name && b->pkg->set->name ?
73       strcasecmp(a->pkg->set->name, b->pkg->set->name) :
74     (!b->pkg->set->name) - (!a->pkg->set->name);
75
76   switch (sortorder) {
77   case so_section:
78     return c_section ? c_section : c_priority ? c_priority : c_name;
79   case so_priority:
80     return c_priority ? c_priority : c_section ? c_section : c_name;
81   case so_alpha:
82     return c_name;
83   case so_unsorted:
84   default:
85     internerr("unsorted or unknown sort %d", sortorder);
86   }
87   /* never reached, make gcc happy */
88   return 1;
89 }
90
91 void packagelist::discardheadings() {
92   int a,b;
93   for (a=0, b=0; a<nitems; a++) {
94     if (table[a]->pkg->set->name) {
95       table[b++]= table[a];
96     }
97   }
98   nitems= b;
99
100   struct perpackagestate *head, *next;
101   head= headings;
102   while (head) {
103     next= head->uprec;
104     delete head->pkg->set;
105     delete head;
106     head= next;
107   }
108   headings = nullptr;
109 }
110
111 void packagelist::addheading(enum ssavailval ssavail,
112                              enum ssstateval ssstate,
113                              pkgpriority priority,
114                              const char *otherpriority,
115                              const char *section) {
116   assert(nitems <= nallocated);
117   if (nitems == nallocated) {
118     nallocated += nallocated+50;
119     struct perpackagestate **newtable= new struct perpackagestate*[nallocated];
120     memcpy(newtable, table, nallocated * sizeof(struct perpackagestate *));
121     delete[] table;
122     table= newtable;
123   }
124
125   debug(dbg_general, "packagelist[%p]::addheading(%d,%d,%d,%s,%s)",
126         this, ssavail, ssstate, priority,
127         otherpriority ? otherpriority : "<null>",
128         section ? section : "<null>");
129
130   struct pkgset *newset = new pkgset;
131   newset->name = nullptr;
132   struct pkginfo *newhead = &newset->pkg;
133   newhead->set = newset;
134   newhead->priority= priority;
135   newhead->otherpriority= otherpriority;
136   newhead->section= section;
137
138   struct perpackagestate *newstate= new perpackagestate;
139   newstate->pkg= newhead;
140   newstate->uprec= headings;
141   headings= newstate;
142   newstate->ssavail= ssavail;
143   newstate->ssstate= ssstate;
144   newhead->clientdata= newstate;
145
146   table[nitems++]= newstate;
147 }
148
149 static packagelist *sortpackagelist;
150
151 int qsort_compareentries(const void *a, const void *b) {
152   return sortpackagelist->compareentries(*(const struct perpackagestate **)a,
153                                          *(const struct perpackagestate **)b);
154 }
155
156 void packagelist::sortinplace() {
157   sortpackagelist= this;
158
159   debug(dbg_general, "packagelist[%p]::sortinplace()", this);
160   qsort(table, nitems, sizeof(struct pkgbin *), qsort_compareentries);
161 }
162
163 void packagelist::ensurestatsortinfo() {
164   const struct dpkg_version *veri;
165   const struct dpkg_version *vera;
166   struct pkginfo *pkg;
167   int index;
168
169   debug(dbg_general,
170         "packagelist[%p]::ensurestatsortinfos() sortorder=%d nitems=%d",
171         this, statsortorder, nitems);
172
173   switch (statsortorder) {
174   case sso_unsorted:
175     debug(dbg_general, "packagelist[%p]::ensurestatsortinfos() unsorted", this);
176     return;
177   case sso_avail:
178     debug(dbg_general, "packagelist[%p]::ensurestatsortinfos() calcssadone=%d",
179           this, calcssadone);
180     if (calcssadone) return;
181     for (index=0; index < nitems; index++) {
182       debug(dbg_general, "packagelist[%p]::ensurestatsortinfos() i=%d pkg=%s",
183             this, index, pkg_name(table[index]->pkg, pnaw_always));
184       pkg= table[index]->pkg;
185       switch (pkg->status) {
186       case PKG_STAT_UNPACKED:
187       case PKG_STAT_HALFCONFIGURED:
188       case PKG_STAT_HALFINSTALLED:
189       case PKG_STAT_TRIGGERSAWAITED:
190       case PKG_STAT_TRIGGERSPENDING:
191         table[index]->ssavail= ssa_broken;
192         break;
193       case PKG_STAT_NOTINSTALLED:
194       case PKG_STAT_CONFIGFILES:
195         if (!dpkg_version_is_informative(&pkg->available.version)) {
196           table[index]->ssavail= ssa_notinst_gone;
197 // FIXME: Disable for now as a workaround, until dselect knows how to properly
198 //        store seen packages.
199 #if 0
200         } else if (table[index]->original == PKG_WANT_UNKNOWN) {
201           table[index]->ssavail= ssa_notinst_unseen;
202 #endif
203         } else {
204           table[index]->ssavail= ssa_notinst_seen;
205         }
206         break;
207       case PKG_STAT_INSTALLED:
208         veri= &table[index]->pkg->installed.version;
209         vera= &table[index]->pkg->available.version;
210         if (!dpkg_version_is_informative(vera)) {
211           table[index]->ssavail= ssa_installed_gone;
212         } else if (dpkg_version_compare(vera, veri) > 0) {
213           table[index]->ssavail= ssa_installed_newer;
214         } else {
215           table[index]->ssavail= ssa_installed_sameold;
216         }
217         break;
218       default:
219         internerr("unknown status %d on sso_avail", pkg->status);
220       }
221       debug(dbg_general,
222             "packagelist[%p]::ensurestatsortinfos() i=%d ssavail=%d",
223             this, index, table[index]->ssavail);
224     }
225     calcssadone = true;
226     break;
227   case sso_state:
228     debug(dbg_general, "packagelist[%p]::ensurestatsortinfos() calcsssdone=%d",
229           this, calcsssdone);
230     if (calcsssdone) return;
231     for (index=0; index < nitems; index++) {
232       debug(dbg_general, "packagelist[%p]::ensurestatsortinfos() i=%d pkg=%s",
233             this, index, pkg_name(table[index]->pkg, pnaw_always));
234       switch (table[index]->pkg->status) {
235       case PKG_STAT_UNPACKED:
236       case PKG_STAT_HALFCONFIGURED:
237       case PKG_STAT_HALFINSTALLED:
238       case PKG_STAT_TRIGGERSAWAITED:
239       case PKG_STAT_TRIGGERSPENDING:
240         table[index]->ssstate= sss_broken;
241         break;
242       case PKG_STAT_NOTINSTALLED:
243         table[index]->ssstate= sss_notinstalled;
244         break;
245       case PKG_STAT_CONFIGFILES:
246         table[index]->ssstate= sss_configfiles;
247         break;
248       case PKG_STAT_INSTALLED:
249         table[index]->ssstate= sss_installed;
250         break;
251       default:
252         internerr("unknown status %d on sso_state", table[index]->pkg->status);
253       }
254       debug(dbg_general,
255             "packagelist[%p]::ensurestatsortinfos() i=%d ssstate=%d",
256             this, index, table[index]->ssstate);
257     }
258     calcsssdone = true;
259     break;
260   default:
261     internerr("unknown statsortorder %d", statsortorder);
262   }
263 }
264
265 void packagelist::sortmakeheads() {
266   discardheadings();
267   ensurestatsortinfo();
268   sortinplace();
269   assert(nitems);
270
271   debug(dbg_general,
272         "packagelist[%p]::sortmakeheads() sortorder=%d statsortorder=%d",
273         this, sortorder, statsortorder);
274
275   int nrealitems= nitems;
276   addheading(ssa_none, sss_none, PKG_PRIO_UNSET, nullptr, nullptr);
277
278   assert(sortorder != so_unsorted);
279   if (sortorder == so_alpha && statsortorder == sso_unsorted) { sortinplace(); return; }
280
281   // Important: do not save pointers into table in this function, because
282   // addheading may need to reallocate table to make it larger !
283
284   struct pkginfo *lastpkg;
285   struct pkginfo *thispkg;
286   lastpkg = nullptr;
287   int a;
288   for (a=0; a<nrealitems; a++) {
289     thispkg= table[a]->pkg;
290     assert(thispkg->set->name);
291     int ssdiff= 0;
292     ssavailval ssavail= ssa_none;
293     ssstateval ssstate= sss_none;
294     switch (statsortorder) {
295     case sso_avail:
296       ssavail= thispkg->clientdata->ssavail;
297       ssdiff= (!lastpkg || ssavail != lastpkg->clientdata->ssavail);
298       break;
299     case sso_state:
300       ssstate= thispkg->clientdata->ssstate;
301       ssdiff= (!lastpkg || ssstate != lastpkg->clientdata->ssstate);
302       break;
303     case sso_unsorted:
304       break;
305     default:
306       internerr("unknown statsortorder %d", statsortorder);
307     }
308
309     int prioritydiff= (!lastpkg ||
310                        thispkg->priority != lastpkg->priority ||
311                        (thispkg->priority == PKG_PRIO_OTHER &&
312                         strcasecmp(thispkg->otherpriority,lastpkg->otherpriority)));
313     int sectiondiff= (!lastpkg ||
314                       strcasecmp(thispkg->section ? thispkg->section : "",
315                                  lastpkg->section ? lastpkg->section : ""));
316
317     debug(dbg_general,
318           "packagelist[%p]::sortmakeheads() pkg=%s  state=%d avail=%d %s  "
319           "priority=%d otherpriority=%s %s  section=%s %s",
320           this, pkg_name(thispkg, pnaw_always),
321           thispkg->clientdata->ssavail, thispkg->clientdata->ssstate,
322           ssdiff ? "*diff" : "same",
323           thispkg->priority,
324           thispkg->priority != PKG_PRIO_OTHER ? "<none>" :
325           thispkg->otherpriority ? thispkg->otherpriority : "<null>",
326           prioritydiff ? "*diff*" : "same",
327           thispkg->section ? thispkg->section : "<null>",
328           sectiondiff ? "*diff*" : "same");
329
330     if (ssdiff)
331       addheading(ssavail,ssstate,
332                  PKG_PRIO_UNSET, nullptr, nullptr);
333
334     if (sortorder == so_section && sectiondiff)
335       addheading(ssavail,ssstate,
336                  PKG_PRIO_UNSET, nullptr,
337                  thispkg->section ? thispkg->section : "");
338
339     if (sortorder == so_priority && prioritydiff)
340       addheading(ssavail,ssstate,
341                  thispkg->priority, thispkg->otherpriority, nullptr);
342
343     if (sortorder != so_alpha && (prioritydiff || sectiondiff))
344       addheading(ssavail,ssstate,
345                  thispkg->priority,thispkg->otherpriority,
346                  thispkg->section ? thispkg->section : "");
347
348     lastpkg= thispkg;
349   }
350
351   if (listpad) {
352     werase(listpad);
353   }
354
355   sortinplace();
356 }
357
358 void packagelist::initialsetup() {
359   debug(dbg_general, "packagelist[%p]::initialsetup()", this);
360
361   int allpackages = pkg_db_count_pkg();
362   datatable= new struct perpackagestate[allpackages];
363
364   nallocated= allpackages+150; // will realloc if necessary, so 150 not critical
365   table= new struct perpackagestate*[nallocated];
366
367   depsdone = nullptr;
368   unavdone = nullptr;
369   currentinfo = nullptr;
370   headings = nullptr;
371   verbose = false;
372   calcssadone = calcsssdone = false;
373   searchdescr = false;
374 }
375
376 void packagelist::finalsetup() {
377   setcursor(0);
378
379   debug(dbg_general, "packagelist[%p]::finalsetup done; recursive=%d nitems=%d",
380         this, recursive, nitems);
381 }
382
383 packagelist::packagelist(keybindings *kb) : baselist(kb) {
384   // nonrecursive
385   initialsetup();
386   struct pkgiterator *iter;
387   struct pkginfo *pkg;
388
389   nitems = 0;
390
391   iter = pkg_db_iter_new();
392   while ((pkg = pkg_db_iter_next_pkg(iter))) {
393     struct perpackagestate *state= &datatable[nitems];
394     state->pkg= pkg;
395     if (pkg->status == PKG_STAT_NOTINSTALLED &&
396         !pkg->files &&
397         pkg->want != PKG_WANT_INSTALL) {
398       pkg->clientdata = nullptr;
399       continue;
400     }
401     // treat all unknown packages as already seen
402     state->direct = state->original = (pkg->want == PKG_WANT_UNKNOWN ? PKG_WANT_PURGE : pkg->want);
403     if (modstatdb_get_status() == msdbrw_write &&
404         state->original == PKG_WANT_UNKNOWN) {
405       state->suggested=
406         pkg->status == PKG_STAT_INSTALLED ||
407           pkg->priority <= PKG_PRIO_STANDARD /* FIXME: configurable */
408             ? PKG_WANT_INSTALL : PKG_WANT_PURGE;
409       state->spriority= sp_inherit;
410     } else {
411       state->suggested= state->original;
412       state->spriority= sp_fixed;
413     }
414     state->dpriority= dp_must;
415     state->selected= state->suggested;
416     state->uprec = nullptr;
417     state->relations.init();
418     pkg->clientdata= state;
419     table[nitems]= state;
420     nitems++;
421   }
422   pkg_db_iter_free(iter);
423
424   if (!nitems)
425     ohshit(_("there are no packages"));
426   recursive = false;
427   sortorder= so_priority;
428   statsortorder= sso_avail;
429   archdisplayopt = ado_both;
430   versiondisplayopt= vdo_both;
431   sortmakeheads();
432   finalsetup();
433 }
434
435 packagelist::packagelist(keybindings *kb, pkginfo **pkgltab) : baselist(kb) {
436   // takes over responsibility for pkgltab (recursive)
437   initialsetup();
438
439   recursive = true;
440   nitems= 0;
441   if (pkgltab) {
442     add(pkgltab);
443     delete[] pkgltab;
444   }
445
446   sortorder= so_unsorted;
447   statsortorder= sso_unsorted;
448   archdisplayopt = ado_none;
449   versiondisplayopt= vdo_none;
450   finalsetup();
451 }
452
453 void
454 perpackagestate::free(bool recursive)
455 {
456   if (pkg->set->name) {
457     if (modstatdb_get_status() == msdbrw_write) {
458       if (uprec) {
459         assert(recursive);
460         uprec->selected= selected;
461         pkg->clientdata= uprec;
462       } else {
463         assert(!recursive);
464         if (pkg->want != selected &&
465             !(pkg->want == PKG_WANT_UNKNOWN && selected == PKG_WANT_PURGE)) {
466           pkg->want= selected;
467         }
468         pkg->clientdata = nullptr;
469       }
470     }
471     relations.destroy();
472   }
473 }
474
475 packagelist::~packagelist() {
476   debug(dbg_general, "packagelist[%p]::~packagelist()", this);
477
478   if (searchstring[0])
479     regfree(&searchfsm);
480
481   discardheadings();
482
483   int index;
484   for (index=0; index<nitems; index++) table[index]->free(recursive);
485   delete[] table;
486   delete[] datatable;
487   debug(dbg_general, "packagelist[%p]::~packagelist() tables freed", this);
488
489   doneent *search, *next;
490   for (search=depsdone; search; search=next) {
491     next= search->next;
492     delete search;
493   }
494
495   debug(dbg_general, "packagelist[%p]::~packagelist() done", this);
496 }
497
498 bool
499 packagelist::checksearch(char *rx)
500 {
501   int rc, opt = REG_NOSUB;
502   int pos;
503
504   if (str_is_unset(rx))
505     return false;
506
507   searchdescr = false;
508   if (searchstring[0]) {
509     regfree(&searchfsm);
510     searchstring[0]=0;
511   }
512
513   /* look for search options */
514   for (pos = strlen(rx) - 1; pos >= 0; pos--)
515     if ((rx[pos] == '/') && ((pos == 0) || (rx[pos - 1] != '\\')))
516       break;
517
518   if (pos >= 0) {
519     rx[pos++] = '\0';
520     if (strcspn(rx + pos, "di") != 0) {
521       displayerror(_("invalid search option given"));
522       return false;
523     }
524
525    while (rx[pos]) {
526      if (rx[pos] == 'i')
527        opt|=REG_ICASE;
528      else if (rx[pos] == 'd')
529        searchdescr = true;
530      pos++;
531    }
532   }
533
534   rc = regcomp(&searchfsm, rx, opt);
535   if (rc != 0) {
536     displayerror(_("error in regular expression"));
537     return false;
538   }
539
540   return true;
541 }
542
543 bool
544 packagelist::matchsearch(int index)
545 {
546   const char *name;
547
548   name = itemname(index);
549   if (!name)
550     return false;       /* Skip things without a name (separators) */
551
552   if (regexec(&searchfsm, name, 0, nullptr, 0) == 0)
553     return true;
554
555   if (searchdescr) {
556     const char *descr = table[index]->pkg->available.description;
557     if (str_is_unset(descr))
558       return false;
559
560     if (regexec(&searchfsm, descr, 0, nullptr, 0) == 0)
561       return true;
562   }
563
564   return false;
565 }
566
567 pkginfo **packagelist::display() {
568   // returns list of packages as null-terminated array, which becomes owned
569   // by the caller, if a recursive check is desired.
570   // returns 0 if no recursive check is desired.
571   int response, index;
572   const keybindings::interpretation *interp;
573   pkginfo **retl;
574
575   debug(dbg_general, "packagelist[%p]::display()", this);
576
577   setupsigwinch();
578   startdisplay();
579
580   if (!expertmode)
581   displayhelp(helpmenulist(),'i');
582
583   debug(dbg_general, "packagelist[%p]::display() entering loop", this);
584   for (;;) {
585     if (whatinfo_height) wcursyncup(whatinfowin);
586     if (doupdate() == ERR)
587       ohshite(_("doupdate failed"));
588     signallist= this;
589     sigwinch_mask(SIG_UNBLOCK);
590     do
591     response= getch();
592     while (response == ERR && errno == EINTR);
593     sigwinch_mask(SIG_BLOCK);
594     if (response == ERR)
595       ohshite(_("getch failed"));
596     interp= (*bindings)(response);
597     debug(dbg_general, "packagelist[%p]::display() response=%d interp=%s",
598           this, response, interp ? interp->action : "[none]");
599     if (!interp) { beep(); continue; }
600     (this->*(interp->pfn))();
601     if (interp->qa != qa_noquit) break;
602   }
603   pop_cleanup(ehflag_normaltidy); // unset the SIGWINCH handler
604   enddisplay();
605
606   if (interp->qa == qa_quitnochecksave ||
607       modstatdb_get_status() == msdbrw_readonly) {
608     debug(dbg_general, "packagelist[%p]::display() done - quitNOcheck", this);
609     return nullptr;
610   }
611
612   if (recursive) {
613     retl= new pkginfo*[nitems+1];
614     for (index=0; index<nitems; index++) retl[index]= table[index]->pkg;
615     retl[nitems] = nullptr;
616     debug(dbg_general, "packagelist[%p]::display() done, retl=%p", this, retl);
617     return retl;
618   } else {
619     packagelist *sub = new packagelist(bindings, nullptr);
620     for (index=0; index < nitems; index++)
621       if (table[index]->pkg->set->name)
622         sub->add(table[index]->pkg);
623     repeatedlydisplay(sub,dp_must);
624     debug(dbg_general,
625           "packagelist[%p]::display() done, not recursive no retl", this);
626     return nullptr;
627   }
628 }