Commit | Line | Data |
---|---|---|
1479465f GJ |
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 | } |