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/>.
32 #include <dpkg/i18n.h>
33 #include <dpkg/dpkg.h>
34 #include <dpkg/dpkg-db.h>
35 #include <dpkg/string.h>
40 int packagelist
::compareentries(const struct perpackagestate
*a
,
41 const struct perpackagestate
*b
) {
42 switch (statsortorder
) {
44 if (a
->ssavail
!= b
->ssavail
) return a
->ssavail
- b
->ssavail
;
47 if (a
->ssstate
!= b
->ssstate
) return a
->ssstate
- b
->ssstate
;
52 internerr("unknown statsortorder %d", statsortorder
);
55 const char *asection
= a
->pkg
->section
;
56 if (!asection
&& a
->pkg
->set
->name
)
58 const char *bsection
= b
->pkg
->section
;
59 if (!bsection
&& b
->pkg
->set
->name
)
62 !asection
|| !bsection ?
63 (!bsection
) - (!asection
) :
64 !*asection
|| !*bsection ?
65 (!*asection
) - (!*bsection
) :
66 strcasecmp(asection
,bsection
);
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
);
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
);
78 return c_section ? c_section
: c_priority ? c_priority
: c_name
;
80 return c_priority ? c_priority
: c_section ? c_section
: c_name
;
85 internerr("unsorted or unknown sort %d", sortorder
);
87 /* never reached, make gcc happy */
91 void packagelist
::discardheadings() {
93 for (a
=0, b
=0; a
<nitems
; a
++) {
94 if (table
[a
]->pkg
->set
->name
) {
100 struct perpackagestate
*head
, *next
;
104 delete head
->pkg
->set
;
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
*));
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>");
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
;
138 struct perpackagestate
*newstate
= new perpackagestate
;
139 newstate
->pkg
= newhead
;
140 newstate
->uprec
= headings
;
142 newstate
->ssavail
= ssavail
;
143 newstate
->ssstate
= ssstate
;
144 newhead
->clientdata
= newstate
;
146 table
[nitems
++]= newstate
;
149 static packagelist
*sortpackagelist
;
151 int qsort_compareentries(const void *a
, const void *b
) {
152 return sortpackagelist
->compareentries(*(const struct perpackagestate
**)a
,
153 *(const struct perpackagestate
**)b
);
156 void packagelist
::sortinplace() {
157 sortpackagelist
= this;
159 debug(dbg_general
, "packagelist[%p]::sortinplace()", this);
160 qsort(table
, nitems
, sizeof(struct pkgbin
*), qsort_compareentries
);
163 void packagelist
::ensurestatsortinfo() {
164 const struct dpkg_version
*veri
;
165 const struct dpkg_version
*vera
;
170 "packagelist[%p]::ensurestatsortinfos() sortorder=%d nitems=%d",
171 this, statsortorder
, nitems
);
173 switch (statsortorder
) {
175 debug(dbg_general
, "packagelist[%p]::ensurestatsortinfos() unsorted", this);
178 debug(dbg_general
, "packagelist[%p]::ensurestatsortinfos() calcssadone=%d",
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
;
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.
200 } else if (table
[index
]->original
== PKG_WANT_UNKNOWN
) {
201 table
[index
]->ssavail
= ssa_notinst_unseen
;
204 table
[index
]->ssavail
= ssa_notinst_seen
;
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
;
215 table
[index
]->ssavail
= ssa_installed_sameold
;
219 internerr("unknown status %d on sso_avail", pkg
->status
);
222 "packagelist[%p]::ensurestatsortinfos() i=%d ssavail=%d",
223 this, index
, table
[index
]->ssavail
);
228 debug(dbg_general
, "packagelist[%p]::ensurestatsortinfos() calcsssdone=%d",
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
;
242 case PKG_STAT_NOTINSTALLED
:
243 table
[index
]->ssstate
= sss_notinstalled
;
245 case PKG_STAT_CONFIGFILES
:
246 table
[index
]->ssstate
= sss_configfiles
;
248 case PKG_STAT_INSTALLED
:
249 table
[index
]->ssstate
= sss_installed
;
252 internerr("unknown status %d on sso_state", table
[index
]->pkg
->status
);
255 "packagelist[%p]::ensurestatsortinfos() i=%d ssstate=%d",
256 this, index
, table
[index
]->ssstate
);
261 internerr("unknown statsortorder %d", statsortorder
);
265 void packagelist
::sortmakeheads() {
267 ensurestatsortinfo();
272 "packagelist[%p]::sortmakeheads() sortorder=%d statsortorder=%d",
273 this, sortorder
, statsortorder
);
275 int nrealitems
= nitems
;
276 addheading(ssa_none
, sss_none
, PKG_PRIO_UNSET
, nullptr, nullptr);
278 assert(sortorder
!= so_unsorted
);
279 if (sortorder
== so_alpha
&& statsortorder
== sso_unsorted
) { sortinplace(); return; }
281 // Important: do not save pointers into table in this function, because
282 // addheading may need to reallocate table to make it larger !
284 struct pkginfo
*lastpkg
;
285 struct pkginfo
*thispkg
;
288 for (a
=0; a
<nrealitems
; a
++) {
289 thispkg
= table
[a
]->pkg
;
290 assert(thispkg
->set
->name
);
292 ssavailval ssavail
= ssa_none
;
293 ssstateval ssstate
= sss_none
;
294 switch (statsortorder
) {
296 ssavail
= thispkg
->clientdata
->ssavail
;
297 ssdiff
= (!lastpkg
|| ssavail
!= lastpkg
->clientdata
->ssavail
);
300 ssstate
= thispkg
->clientdata
->ssstate
;
301 ssdiff
= (!lastpkg
|| ssstate
!= lastpkg
->clientdata
->ssstate
);
306 internerr("unknown statsortorder %d", statsortorder
);
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
: ""));
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",
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");
331 addheading(ssavail
,ssstate
,
332 PKG_PRIO_UNSET
, nullptr, nullptr);
334 if (sortorder
== so_section
&& sectiondiff
)
335 addheading(ssavail
,ssstate
,
336 PKG_PRIO_UNSET
, nullptr,
337 thispkg
->section ? thispkg
->section
: "");
339 if (sortorder
== so_priority
&& prioritydiff
)
340 addheading(ssavail
,ssstate
,
341 thispkg
->priority
, thispkg
->otherpriority
, nullptr);
343 if (sortorder
!= so_alpha
&& (prioritydiff
|| sectiondiff
))
344 addheading(ssavail
,ssstate
,
345 thispkg
->priority
,thispkg
->otherpriority
,
346 thispkg
->section ? thispkg
->section
: "");
358 void packagelist
::initialsetup() {
359 debug(dbg_general
, "packagelist[%p]::initialsetup()", this);
361 int allpackages
= pkg_db_count_pkg();
362 datatable
= new struct perpackagestate
[allpackages
];
364 nallocated
= allpackages
+150; // will realloc if necessary, so 150 not critical
365 table
= new struct perpackagestate
*[nallocated
];
369 currentinfo
= nullptr;
372 calcssadone
= calcsssdone
= false;
376 void packagelist
::finalsetup() {
379 debug(dbg_general
, "packagelist[%p]::finalsetup done; recursive=%d nitems=%d",
380 this, recursive
, nitems
);
383 packagelist
::packagelist(keybindings
*kb
) : baselist(kb
) {
386 struct pkgiterator
*iter
;
391 iter
= pkg_db_iter_new();
392 while ((pkg
= pkg_db_iter_next_pkg(iter
))) {
393 struct perpackagestate
*state
= &datatable
[nitems
];
395 if (pkg
->status
== PKG_STAT_NOTINSTALLED
&&
397 pkg
->want
!= PKG_WANT_INSTALL
) {
398 pkg
->clientdata
= nullptr;
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
) {
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
;
411 state
->suggested
= state
->original
;
412 state
->spriority
= sp_fixed
;
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
;
422 pkg_db_iter_free(iter
);
425 ohshit(_("there are no packages"));
427 sortorder
= so_priority
;
428 statsortorder
= sso_avail
;
429 archdisplayopt
= ado_both
;
430 versiondisplayopt
= vdo_both
;
435 packagelist
::packagelist(keybindings
*kb
, pkginfo
**pkgltab
) : baselist(kb
) {
436 // takes over responsibility for pkgltab (recursive)
446 sortorder
= so_unsorted
;
447 statsortorder
= sso_unsorted
;
448 archdisplayopt
= ado_none
;
449 versiondisplayopt
= vdo_none
;
454 perpackagestate
::free(bool recursive
)
456 if (pkg
->set
->name
) {
457 if (modstatdb_get_status() == msdbrw_write
) {
460 uprec
->selected
= selected
;
461 pkg
->clientdata
= uprec
;
464 if (pkg
->want
!= selected
&&
465 !(pkg
->want
== PKG_WANT_UNKNOWN
&& selected
== PKG_WANT_PURGE
)) {
468 pkg
->clientdata
= nullptr;
475 packagelist
::~packagelist() {
476 debug(dbg_general
, "packagelist[%p]::~packagelist()", this);
484 for (index
=0; index
<nitems
; index
++) table
[index
]->free(recursive
);
487 debug(dbg_general
, "packagelist[%p]::~packagelist() tables freed", this);
489 doneent
*search
, *next
;
490 for (search
=depsdone
; search
; search
=next
) {
495 debug(dbg_general
, "packagelist[%p]::~packagelist() done", this);
499 packagelist
::checksearch(char *rx
)
501 int rc
, opt
= REG_NOSUB
;
504 if (str_is_unset(rx
))
508 if (searchstring
[0]) {
513 /* look for search options */
514 for (pos
= strlen(rx
) - 1; pos
>= 0; pos
--)
515 if ((rx
[pos
] == '/') && ((pos
== 0) || (rx
[pos
- 1] != '\\')))
520 if (strcspn(rx
+ pos
, "di") != 0) {
521 displayerror(_("invalid search option given"));
528 else if (rx
[pos
] == 'd')
534 rc
= regcomp(&searchfsm
, rx
, opt
);
536 displayerror(_("error in regular expression"));
544 packagelist
::matchsearch(int index
)
548 name
= itemname(index
);
550 return false; /* Skip things without a name (separators) */
552 if (regexec(&searchfsm
, name
, 0, nullptr, 0) == 0)
556 const char *descr
= table
[index
]->pkg
->available
.description
;
557 if (str_is_unset(descr
))
560 if (regexec(&searchfsm
, descr
, 0, nullptr, 0) == 0)
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.
572 const keybindings
::interpretation
*interp
;
575 debug(dbg_general
, "packagelist[%p]::display()", this);
581 displayhelp(helpmenulist(),'i');
583 debug(dbg_general
, "packagelist[%p]::display() entering loop", this);
585 if (whatinfo_height
) wcursyncup(whatinfowin
);
586 if (doupdate() == ERR
)
587 ohshite(_("doupdate failed"));
589 sigwinch_mask(SIG_UNBLOCK
);
592 while (response
== ERR
&& errno
== EINTR
);
593 sigwinch_mask(SIG_BLOCK
);
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;
603 pop_cleanup(ehflag_normaltidy
); // unset the SIGWINCH handler
606 if (interp
->qa
== qa_quitnochecksave
||
607 modstatdb_get_status() == msdbrw_readonly
) {
608 debug(dbg_general
, "packagelist[%p]::display() done - quitNOcheck", this);
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
);
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
);
625 "packagelist[%p]::display() done, not recursive no retl", this);