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/>.
31 #include <dpkg/i18n.h>
32 #include <dpkg/dpkg.h>
33 #include <dpkg/dpkg-db.h>
34 #include <dpkg/string.h>
39 int packagelist::compareentries(const struct perpackagestate
*a
,
40 const struct perpackagestate
*b
) {
41 switch (statsortorder
) {
43 if (a
->ssavail
!= b
->ssavail
) return a
->ssavail
- b
->ssavail
;
46 if (a
->ssstate
!= b
->ssstate
) return a
->ssstate
- b
->ssstate
;
51 internerr("unknown statsortorder %d", statsortorder
);
54 const char *asection
= a
->pkg
->section
;
55 if (!asection
&& a
->pkg
->set
->name
)
57 const char *bsection
= b
->pkg
->section
;
58 if (!bsection
&& b
->pkg
->set
->name
)
61 !asection
|| !bsection
?
62 (!bsection
) - (!asection
) :
63 !*asection
|| !*bsection
?
64 (!*asection
) - (!*bsection
) :
65 strcasecmp(asection
,bsection
);
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
);
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
);
77 return c_section
? c_section
: c_priority
? c_priority
: c_name
;
79 return c_priority
? c_priority
: c_section
? c_section
: c_name
;
84 internerr("unsorted or unknown sort %d", sortorder
);
86 /* never reached, make gcc happy */
90 void packagelist::discardheadings() {
92 for (a
=0, b
=0; a
<nitems
; a
++) {
93 if (table
[a
]->pkg
->set
->name
) {
99 struct perpackagestate
*head
, *next
;
103 delete head
->pkg
->set
;
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",
119 if (nitems
== nallocated
) {
120 nallocated
+= nallocated
+50;
121 struct perpackagestate
**newtable
= new struct perpackagestate
*[nallocated
];
122 memcpy(newtable
, table
, nallocated
* sizeof(struct perpackagestate
*));
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
;
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
;
174 "packagelist[%p]::ensurestatsortinfos() sortorder=%d nitems=%d",
175 this, statsortorder
, nitems
);
177 switch (statsortorder
) {
179 debug(dbg_general
, "packagelist[%p]::ensurestatsortinfos() unsorted", this);
182 debug(dbg_general
, "packagelist[%p]::ensurestatsortinfos() calcssadone=%d",
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
;
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.
204 } else if (table
[index
]->original
== PKG_WANT_UNKNOWN
) {
205 table
[index
]->ssavail
= ssa_notinst_unseen
;
208 table
[index
]->ssavail
= ssa_notinst_seen
;
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
;
219 table
[index
]->ssavail
= ssa_installed_sameold
;
223 internerr("unknown status %d on sso_avail", pkg
->status
);
226 "packagelist[%p]::ensurestatsortinfos() i=%d ssavail=%d",
227 this, index
, table
[index
]->ssavail
);
232 debug(dbg_general
, "packagelist[%p]::ensurestatsortinfos() calcsssdone=%d",
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
;
246 case PKG_STAT_NOTINSTALLED
:
247 table
[index
]->ssstate
= sss_notinstalled
;
249 case PKG_STAT_CONFIGFILES
:
250 table
[index
]->ssstate
= sss_configfiles
;
252 case PKG_STAT_INSTALLED
:
253 table
[index
]->ssstate
= sss_installed
;
256 internerr("unknown status %d on sso_state", table
[index
]->pkg
->status
);
259 "packagelist[%p]::ensurestatsortinfos() i=%d ssstate=%d",
260 this, index
, table
[index
]->ssstate
);
265 internerr("unknown statsortorder %d", statsortorder
);
269 void packagelist::sortmakeheads() {
271 ensurestatsortinfo();
275 internerr("cannot sort 0 items");
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
;
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
);
301 ssavailval ssavail
= ssa_none
;
302 ssstateval ssstate
= sss_none
;
303 switch (statsortorder
) {
305 ssavail
= thispkg
->clientdata
->ssavail
;
306 ssdiff
= (!lastpkg
|| ssavail
!= lastpkg
->clientdata
->ssavail
);
309 ssstate
= thispkg
->clientdata
->ssstate
;
310 ssdiff
= (!lastpkg
|| ssstate
!= lastpkg
->clientdata
->ssstate
);
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
: ""));
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",
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");
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
: "");
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
];
378 currentinfo
= nullptr;
381 calcssadone
= calcsssdone
= false;
385 void packagelist::finalsetup() {
388 debug(dbg_general
, "packagelist[%p]::finalsetup done; recursive=%d nitems=%d",
389 this, recursive
, nitems
);
392 packagelist::packagelist(keybindings
*kb
) : baselist(kb
) {
395 struct pkg_hash_iter
*iter
;
400 iter
= pkg_hash_iter_new();
401 while ((pkg
= pkg_hash_iter_next_pkg(iter
))) {
402 struct perpackagestate
*state
= &datatable
[nitems
];
404 if (pkg
->status
== PKG_STAT_NOTINSTALLED
&&
406 pkg
->want
!= PKG_WANT_INSTALL
) {
407 pkg
->clientdata
= nullptr;
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
) {
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
;
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
;
431 pkg_hash_iter_free(iter
);
434 ohshit(_("there are no packages"));
436 sortorder
= so_priority
;
437 statsortorder
= sso_avail
;
438 archdisplayopt
= ado_both
;
439 versiondisplayopt
= vdo_both
;
444 packagelist::packagelist(keybindings
*kb
, pkginfo
**pkgltab
) : baselist(kb
) {
445 // takes over responsibility for pkgltab (recursive)
455 sortorder
= so_unsorted
;
456 statsortorder
= sso_unsorted
;
457 archdisplayopt
= ado_none
;
458 versiondisplayopt
= vdo_none
;
463 perpackagestate::free(bool recursive
)
465 if (pkg
->set
->name
) {
466 if (modstatdb_get_status() == msdbrw_write
) {
469 internerr("unexpected non-recursive free requested");
470 uprec
->selected
= selected
;
471 pkg
->clientdata
= uprec
;
474 internerr("unexpected recursive free requested");
475 if (pkg
->want
!= selected
&&
476 !(pkg
->want
== PKG_WANT_UNKNOWN
&& selected
== PKG_WANT_PURGE
)) {
479 pkg
->clientdata
= nullptr;
486 packagelist::~packagelist() {
487 debug(dbg_general
, "packagelist[%p]::~packagelist()", this);
495 for (index
=0; index
<nitems
; index
++) table
[index
]->free(recursive
);
498 debug(dbg_general
, "packagelist[%p]::~packagelist() tables freed", this);
500 doneent
*search
, *next
;
501 for (search
=depsdone
; search
; search
=next
) {
506 debug(dbg_general
, "packagelist[%p]::~packagelist() done", this);
510 packagelist::checksearch(char *rx
)
512 int rc
, opt
= REG_NOSUB
;
515 if (str_is_unset(rx
))
519 if (searchstring
[0]) {
524 /* look for search options */
525 for (pos
= strlen(rx
) - 1; pos
>= 0; pos
--)
526 if ((rx
[pos
] == '/') && ((pos
== 0) || (rx
[pos
- 1] != '\\')))
531 if (strcspn(rx
+ pos
, "di") != 0) {
532 displayerror(_("invalid search option given"));
539 else if (rx
[pos
] == 'd')
545 rc
= regcomp(&searchfsm
, rx
, opt
);
547 displayerror(_("error in regular expression"));
555 packagelist::matchsearch(int index
)
559 name
= itemname(index
);
561 return false; /* Skip things without a name (separators) */
563 if (regexec(&searchfsm
, name
, 0, nullptr, 0) == 0)
567 const char *descr
= table
[index
]->pkg
->available
.description
;
568 if (str_is_unset(descr
))
571 if (regexec(&searchfsm
, descr
, 0, nullptr, 0) == 0)
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.
583 const keybindings::interpretation
*interp
;
585 debug(dbg_general
, "packagelist[%p]::display()", this);
590 displayhelp(helpmenulist(),'i');
592 debug(dbg_general
, "packagelist[%p]::display() entering loop", this);
594 if (whatinfo_height
) wcursyncup(whatinfowin
);
595 if (doupdate() == ERR
)
596 ohshite(_("doupdate failed"));
599 if (response
== KEY_RESIZE
) {
603 } while (response
== ERR
&& errno
== EINTR
);
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]");
611 (this->*(interp
->pfn
))();
612 if (interp
->qa
!= qa_noquit
) break;
616 if (interp
->qa
== qa_quitnochecksave
||
617 modstatdb_get_status() == msdbrw_readonly
) {
618 debug(dbg_general
, "packagelist[%p]::display() done - quitNOcheck", this);
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
);
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
);
635 "packagelist[%p]::display() done, not recursive no retl", this);