build: Check whether HAVE_* macros for headers are defined
[dpkg.git] / dselect / pkglist.cc
blob8fb2bc6510f38a294e81988d64f1bd26d53f40af
1 /*
2 * dselect - Debian package maintenance user interface
3 * pkglist.cc - package list administration
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>
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.
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.
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/>.
23 #include <config.h>
24 #include <compat.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
31 #include <dpkg/i18n.h>
32 #include <dpkg/dpkg.h>
33 #include <dpkg/dpkg-db.h>
34 #include <dpkg/string.h>
36 #include "dselect.h"
37 #include "bindings.h"
39 int packagelist::compareentries(const struct perpackagestate *a,
40 const struct perpackagestate *b) {
41 switch (statsortorder) {
42 case sso_avail:
43 if (a->ssavail != b->ssavail) return a->ssavail - b->ssavail;
44 break;
45 case sso_state:
46 if (a->ssstate != b->ssstate) return a->ssstate - b->ssstate;
47 break;
48 case sso_unsorted:
49 break;
50 default:
51 internerr("unknown statsortorder %d", statsortorder);
54 const char *asection= a->pkg->section;
55 if (!asection && a->pkg->set->name)
56 asection = "";
57 const char *bsection= b->pkg->section;
58 if (!bsection && b->pkg->set->name)
59 bsection = "";
60 int c_section=
61 !asection || !bsection ?
62 (!bsection) - (!asection) :
63 !*asection || !*bsection ?
64 (!*asection) - (!*bsection) :
65 strcasecmp(asection,bsection);
66 int c_priority=
67 a->pkg->priority - b->pkg->priority;
68 if (!c_priority && a->pkg->priority == PKG_PRIO_OTHER)
69 c_priority= strcasecmp(a->pkg->otherpriority, b->pkg->otherpriority);
70 int c_name=
71 a->pkg->set->name && b->pkg->set->name ?
72 strcasecmp(a->pkg->set->name, b->pkg->set->name) :
73 (!b->pkg->set->name) - (!a->pkg->set->name);
75 switch (sortorder) {
76 case so_section:
77 return c_section ? c_section : c_priority ? c_priority : c_name;
78 case so_priority:
79 return c_priority ? c_priority : c_section ? c_section : c_name;
80 case so_alpha:
81 return c_name;
82 case so_unsorted:
83 default:
84 internerr("unsorted or unknown sort %d", sortorder);
86 /* never reached, make gcc happy */
87 return 1;
90 void packagelist::discardheadings() {
91 int a,b;
92 for (a=0, b=0; a<nitems; a++) {
93 if (table[a]->pkg->set->name) {
94 table[b++]= table[a];
97 nitems= b;
99 struct perpackagestate *head, *next;
100 head= headings;
101 while (head) {
102 next= head->uprec;
103 delete head->pkg->set;
104 delete head;
105 head= next;
107 headings = nullptr;
110 void packagelist::addheading(enum ssavailval ssavail,
111 enum ssstateval ssstate,
112 pkgpriority priority,
113 const char *otherpriority,
114 const char *section) {
115 if (nitems > nallocated)
116 internerr("inconsistent state: ntimes=%d > nallocated=%d",
117 nitems, nallocated);
119 if (nitems == nallocated) {
120 nallocated += nallocated+50;
121 struct perpackagestate **newtable= new struct perpackagestate*[nallocated];
122 memcpy(newtable, table, nallocated * sizeof(struct perpackagestate *));
123 delete[] table;
124 table= newtable;
127 debug(dbg_general, "packagelist[%p]::addheading(%d,%d,%d,%s,%s)",
128 this, ssavail, ssstate, priority,
129 otherpriority ? otherpriority : "<null>",
130 section ? section : "<null>");
132 struct pkgset *newset = new pkgset;
133 newset->name = nullptr;
134 struct pkginfo *newhead = &newset->pkg;
135 newhead->set = newset;
136 newhead->priority= priority;
137 newhead->otherpriority= otherpriority;
138 newhead->section= section;
140 struct perpackagestate *newstate= new perpackagestate;
141 newstate->pkg= newhead;
142 newstate->uprec= headings;
143 headings= newstate;
144 newstate->ssavail= ssavail;
145 newstate->ssstate= ssstate;
146 newhead->clientdata= newstate;
148 table[nitems++]= newstate;
151 static packagelist *sortpackagelist;
153 int qsort_compareentries(const void *a, const void *b) {
154 const struct perpackagestate *pa = *static_cast<const struct perpackagestate * const *>(a);
155 const struct perpackagestate *pb = *static_cast<const struct perpackagestate * const *>(b);
157 return sortpackagelist->compareentries(pa, pb);
160 void packagelist::sortinplace() {
161 sortpackagelist= this;
163 debug(dbg_general, "packagelist[%p]::sortinplace()", this);
164 qsort(table, nitems, sizeof(struct pkgbin *), qsort_compareentries);
167 void packagelist::ensurestatsortinfo() {
168 const struct dpkg_version *veri;
169 const struct dpkg_version *vera;
170 struct pkginfo *pkg;
171 int index;
173 debug(dbg_general,
174 "packagelist[%p]::ensurestatsortinfos() sortorder=%d nitems=%d",
175 this, statsortorder, nitems);
177 switch (statsortorder) {
178 case sso_unsorted:
179 debug(dbg_general, "packagelist[%p]::ensurestatsortinfos() unsorted", this);
180 return;
181 case sso_avail:
182 debug(dbg_general, "packagelist[%p]::ensurestatsortinfos() calcssadone=%d",
183 this, calcssadone);
184 if (calcssadone) return;
185 for (index=0; index < nitems; index++) {
186 debug(dbg_general, "packagelist[%p]::ensurestatsortinfos() i=%d pkg=%s",
187 this, index, pkg_name(table[index]->pkg, pnaw_always));
188 pkg= table[index]->pkg;
189 switch (pkg->status) {
190 case PKG_STAT_UNPACKED:
191 case PKG_STAT_HALFCONFIGURED:
192 case PKG_STAT_HALFINSTALLED:
193 case PKG_STAT_TRIGGERSAWAITED:
194 case PKG_STAT_TRIGGERSPENDING:
195 table[index]->ssavail= ssa_broken;
196 break;
197 case PKG_STAT_NOTINSTALLED:
198 case PKG_STAT_CONFIGFILES:
199 if (!dpkg_version_is_informative(&pkg->available.version)) {
200 table[index]->ssavail= ssa_notinst_gone;
201 // FIXME: Disable for now as a workaround, until dselect knows how to properly
202 // store seen packages.
203 #if 0
204 } else if (table[index]->original == PKG_WANT_UNKNOWN) {
205 table[index]->ssavail= ssa_notinst_unseen;
206 #endif
207 } else {
208 table[index]->ssavail= ssa_notinst_seen;
210 break;
211 case PKG_STAT_INSTALLED:
212 veri= &table[index]->pkg->installed.version;
213 vera= &table[index]->pkg->available.version;
214 if (!dpkg_version_is_informative(vera)) {
215 table[index]->ssavail= ssa_installed_gone;
216 } else if (dpkg_version_compare(vera, veri) > 0) {
217 table[index]->ssavail= ssa_installed_newer;
218 } else {
219 table[index]->ssavail= ssa_installed_sameold;
221 break;
222 default:
223 internerr("unknown status %d on sso_avail", pkg->status);
225 debug(dbg_general,
226 "packagelist[%p]::ensurestatsortinfos() i=%d ssavail=%d",
227 this, index, table[index]->ssavail);
229 calcssadone = true;
230 break;
231 case sso_state:
232 debug(dbg_general, "packagelist[%p]::ensurestatsortinfos() calcsssdone=%d",
233 this, calcsssdone);
234 if (calcsssdone) return;
235 for (index=0; index < nitems; index++) {
236 debug(dbg_general, "packagelist[%p]::ensurestatsortinfos() i=%d pkg=%s",
237 this, index, pkg_name(table[index]->pkg, pnaw_always));
238 switch (table[index]->pkg->status) {
239 case PKG_STAT_UNPACKED:
240 case PKG_STAT_HALFCONFIGURED:
241 case PKG_STAT_HALFINSTALLED:
242 case PKG_STAT_TRIGGERSAWAITED:
243 case PKG_STAT_TRIGGERSPENDING:
244 table[index]->ssstate= sss_broken;
245 break;
246 case PKG_STAT_NOTINSTALLED:
247 table[index]->ssstate= sss_notinstalled;
248 break;
249 case PKG_STAT_CONFIGFILES:
250 table[index]->ssstate= sss_configfiles;
251 break;
252 case PKG_STAT_INSTALLED:
253 table[index]->ssstate= sss_installed;
254 break;
255 default:
256 internerr("unknown status %d on sso_state", table[index]->pkg->status);
258 debug(dbg_general,
259 "packagelist[%p]::ensurestatsortinfos() i=%d ssstate=%d",
260 this, index, table[index]->ssstate);
262 calcsssdone = true;
263 break;
264 default:
265 internerr("unknown statsortorder %d", statsortorder);
269 void packagelist::sortmakeheads() {
270 discardheadings();
271 ensurestatsortinfo();
272 sortinplace();
274 if (nitems == 0)
275 internerr("cannot sort 0 items");
277 debug(dbg_general,
278 "packagelist[%p]::sortmakeheads() sortorder=%d statsortorder=%d",
279 this, sortorder, statsortorder);
281 int nrealitems= nitems;
282 addheading(ssa_none, sss_none, PKG_PRIO_UNSET, nullptr, nullptr);
284 if (sortorder == so_unsorted)
285 internerr("cannot sort unsorted order");
287 if (sortorder == so_alpha && statsortorder == sso_unsorted) { sortinplace(); return; }
289 // Important: do not save pointers into table in this function, because
290 // addheading may need to reallocate table to make it larger !
292 struct pkginfo *lastpkg;
293 struct pkginfo *thispkg;
294 lastpkg = nullptr;
295 int a;
296 for (a=0; a<nrealitems; a++) {
297 thispkg= table[a]->pkg;
298 if (thispkg->set->name == nullptr)
299 internerr("package set has no name at table index %d", a);
300 int ssdiff= 0;
301 ssavailval ssavail= ssa_none;
302 ssstateval ssstate= sss_none;
303 switch (statsortorder) {
304 case sso_avail:
305 ssavail= thispkg->clientdata->ssavail;
306 ssdiff= (!lastpkg || ssavail != lastpkg->clientdata->ssavail);
307 break;
308 case sso_state:
309 ssstate= thispkg->clientdata->ssstate;
310 ssdiff= (!lastpkg || ssstate != lastpkg->clientdata->ssstate);
311 break;
312 case sso_unsorted:
313 break;
314 default:
315 internerr("unknown statsortorder %d", statsortorder);
318 int prioritydiff= (!lastpkg ||
319 thispkg->priority != lastpkg->priority ||
320 (thispkg->priority == PKG_PRIO_OTHER &&
321 strcasecmp(thispkg->otherpriority,lastpkg->otherpriority)));
322 int sectiondiff= (!lastpkg ||
323 strcasecmp(thispkg->section ? thispkg->section : "",
324 lastpkg->section ? lastpkg->section : ""));
326 debug(dbg_general,
327 "packagelist[%p]::sortmakeheads() pkg=%s state=%d avail=%d %s "
328 "priority=%d otherpriority=%s %s section=%s %s",
329 this, pkg_name(thispkg, pnaw_always),
330 thispkg->clientdata->ssavail, thispkg->clientdata->ssstate,
331 ssdiff ? "*diff" : "same",
332 thispkg->priority,
333 thispkg->priority != PKG_PRIO_OTHER ? "<none>" :
334 thispkg->otherpriority ? thispkg->otherpriority : "<null>",
335 prioritydiff ? "*diff*" : "same",
336 thispkg->section ? thispkg->section : "<null>",
337 sectiondiff ? "*diff*" : "same");
339 if (ssdiff)
340 addheading(ssavail,ssstate,
341 PKG_PRIO_UNSET, nullptr, nullptr);
343 if (sortorder == so_section && sectiondiff)
344 addheading(ssavail,ssstate,
345 PKG_PRIO_UNSET, nullptr,
346 thispkg->section ? thispkg->section : "");
348 if (sortorder == so_priority && prioritydiff)
349 addheading(ssavail,ssstate,
350 thispkg->priority, thispkg->otherpriority, nullptr);
352 if (sortorder != so_alpha && (prioritydiff || sectiondiff))
353 addheading(ssavail,ssstate,
354 thispkg->priority,thispkg->otherpriority,
355 thispkg->section ? thispkg->section : "");
357 lastpkg= thispkg;
360 if (listpad) {
361 werase(listpad);
364 sortinplace();
367 void packagelist::initialsetup() {
368 debug(dbg_general, "packagelist[%p]::initialsetup()", this);
370 int allpackages = pkg_hash_count_pkg();
371 datatable= new struct perpackagestate[allpackages];
373 nallocated= allpackages+150; // will realloc if necessary, so 150 not critical
374 table= new struct perpackagestate*[nallocated];
376 depsdone = nullptr;
377 unavdone = nullptr;
378 currentinfo = nullptr;
379 headings = nullptr;
380 verbose = false;
381 calcssadone = calcsssdone = false;
382 searchdescr = false;
385 void packagelist::finalsetup() {
386 setcursor(0);
388 debug(dbg_general, "packagelist[%p]::finalsetup done; recursive=%d nitems=%d",
389 this, recursive, nitems);
392 packagelist::packagelist(keybindings *kb) : baselist(kb) {
393 // nonrecursive
394 initialsetup();
395 struct pkg_hash_iter *iter;
396 struct pkginfo *pkg;
398 nitems = 0;
400 iter = pkg_hash_iter_new();
401 while ((pkg = pkg_hash_iter_next_pkg(iter))) {
402 struct perpackagestate *state= &datatable[nitems];
403 state->pkg= pkg;
404 if (pkg->status == PKG_STAT_NOTINSTALLED &&
405 !pkg->archives &&
406 pkg->want != PKG_WANT_INSTALL) {
407 pkg->clientdata = nullptr;
408 continue;
410 // treat all unknown packages as already seen
411 state->direct = state->original = (pkg->want == PKG_WANT_UNKNOWN ? PKG_WANT_PURGE : pkg->want);
412 if (modstatdb_get_status() == msdbrw_write &&
413 state->original == PKG_WANT_UNKNOWN) {
414 state->suggested=
415 pkg->status == PKG_STAT_INSTALLED ||
416 pkg->priority <= PKG_PRIO_STANDARD /* FIXME: configurable */
417 ? PKG_WANT_INSTALL : PKG_WANT_PURGE;
418 state->spriority= sp_inherit;
419 } else {
420 state->suggested= state->original;
421 state->spriority= sp_fixed;
423 state->dpriority= dp_must;
424 state->selected= state->suggested;
425 state->uprec = nullptr;
426 state->relations.init();
427 pkg->clientdata= state;
428 table[nitems]= state;
429 nitems++;
431 pkg_hash_iter_free(iter);
433 if (!nitems)
434 ohshit(_("there are no packages"));
435 recursive = false;
436 sortorder= so_priority;
437 statsortorder= sso_avail;
438 archdisplayopt = ado_both;
439 versiondisplayopt= vdo_both;
440 sortmakeheads();
441 finalsetup();
444 packagelist::packagelist(keybindings *kb, pkginfo **pkgltab) : baselist(kb) {
445 // takes over responsibility for pkgltab (recursive)
446 initialsetup();
448 recursive = true;
449 nitems= 0;
450 if (pkgltab) {
451 add(pkgltab);
452 delete[] pkgltab;
455 sortorder= so_unsorted;
456 statsortorder= sso_unsorted;
457 archdisplayopt = ado_none;
458 versiondisplayopt= vdo_none;
459 finalsetup();
462 void
463 perpackagestate::free(bool recursive)
465 if (pkg->set->name) {
466 if (modstatdb_get_status() == msdbrw_write) {
467 if (uprec) {
468 if (!recursive)
469 internerr("unexpected non-recursive free requested");
470 uprec->selected= selected;
471 pkg->clientdata= uprec;
472 } else {
473 if (recursive)
474 internerr("unexpected recursive free requested");
475 if (pkg->want != selected &&
476 !(pkg->want == PKG_WANT_UNKNOWN && selected == PKG_WANT_PURGE)) {
477 pkg->want= selected;
479 pkg->clientdata = nullptr;
482 relations.destroy();
486 packagelist::~packagelist() {
487 debug(dbg_general, "packagelist[%p]::~packagelist()", this);
489 if (searchstring[0])
490 regfree(&searchfsm);
492 discardheadings();
494 int index;
495 for (index=0; index<nitems; index++) table[index]->free(recursive);
496 delete[] table;
497 delete[] datatable;
498 debug(dbg_general, "packagelist[%p]::~packagelist() tables freed", this);
500 doneent *search, *next;
501 for (search=depsdone; search; search=next) {
502 next= search->next;
503 delete search;
506 debug(dbg_general, "packagelist[%p]::~packagelist() done", this);
509 bool
510 packagelist::checksearch(char *rx)
512 int rc, opt = REG_NOSUB;
513 int pos;
515 if (str_is_unset(rx))
516 return false;
518 searchdescr = false;
519 if (searchstring[0]) {
520 regfree(&searchfsm);
521 searchstring[0]=0;
524 /* look for search options */
525 for (pos = strlen(rx) - 1; pos >= 0; pos--)
526 if ((rx[pos] == '/') && ((pos == 0) || (rx[pos - 1] != '\\')))
527 break;
529 if (pos >= 0) {
530 rx[pos++] = '\0';
531 if (strcspn(rx + pos, "di") != 0) {
532 displayerror(_("invalid search option given"));
533 return false;
536 while (rx[pos]) {
537 if (rx[pos] == 'i')
538 opt|=REG_ICASE;
539 else if (rx[pos] == 'd')
540 searchdescr = true;
541 pos++;
545 rc = regcomp(&searchfsm, rx, opt);
546 if (rc != 0) {
547 displayerror(_("error in regular expression"));
548 return false;
551 return true;
554 bool
555 packagelist::matchsearch(int index)
557 const char *name;
559 name = itemname(index);
560 if (!name)
561 return false; /* Skip things without a name (separators) */
563 if (regexec(&searchfsm, name, 0, nullptr, 0) == 0)
564 return true;
566 if (searchdescr) {
567 const char *descr = table[index]->pkg->available.description;
568 if (str_is_unset(descr))
569 return false;
571 if (regexec(&searchfsm, descr, 0, nullptr, 0) == 0)
572 return true;
575 return false;
578 pkginfo **packagelist::display() {
579 // returns list of packages as null-terminated array, which becomes owned
580 // by the caller, if a recursive check is desired.
581 // returns 0 if no recursive check is desired.
582 int response, index;
583 const keybindings::interpretation *interp;
585 debug(dbg_general, "packagelist[%p]::display()", this);
587 startdisplay();
589 if (!expertmode)
590 displayhelp(helpmenulist(),'i');
592 debug(dbg_general, "packagelist[%p]::display() entering loop", this);
593 for (;;) {
594 if (whatinfo_height) wcursyncup(whatinfowin);
595 if (doupdate() == ERR)
596 ohshite(_("doupdate failed"));
597 do {
598 response = getch();
599 if (response == KEY_RESIZE) {
600 resize_window();
601 continue;
603 } while (response == ERR && errno == EINTR);
604 if (response == ERR)
605 ohshite(_("getch failed"));
606 interp= (*bindings)(response);
607 debug(dbg_general, "packagelist[%p]::display() response=%d interp=%s",
608 this, response, interp ? interp->action : "[none]");
609 if (!interp)
610 continue;
611 (this->*(interp->pfn))();
612 if (interp->qa != qa_noquit) break;
614 enddisplay();
616 if (interp->qa == qa_quitnochecksave ||
617 modstatdb_get_status() == msdbrw_readonly) {
618 debug(dbg_general, "packagelist[%p]::display() done - quitNOcheck", this);
619 return nullptr;
622 if (recursive) {
623 pkginfo **retl = new pkginfo*[nitems + 1];
624 for (index=0; index<nitems; index++) retl[index]= table[index]->pkg;
625 retl[nitems] = nullptr;
626 debug(dbg_general, "packagelist[%p]::display() done, retl=%p", this, retl);
627 return retl;
628 } else {
629 packagelist *sub = new packagelist(bindings, nullptr);
630 for (index=0; index < nitems; index++)
631 if (table[index]->pkg->set->name)
632 sub->add(table[index]->pkg);
633 repeatedlydisplay(sub,dp_must);
634 debug(dbg_general,
635 "packagelist[%p]::display() done, not recursive no retl", this);
636 return nullptr;