po: Update German man pages translation
[dpkg.git] / dselect / main.cc
blob9726c52f8df3dc6084bc0d9897b9848a987aad72
1 /*
2 * dselect - Debian package maintenance user interface
3 * main.cc - main program
5 * Copyright © 1994-1996 Ian Jackson <ijackson@chiark.greenend.org.uk>
6 * Copyright © 2000,2001 Wichert Akkerman <wakkerma@debian.org>
7 * Copyright © 2006-2015 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 <sys/types.h>
27 #include <sys/wait.h>
29 #include <errno.h>
30 #include <limits.h>
31 #ifdef HAVE_LOCALE_H
32 #include <locale.h>
33 #endif
34 #include <ctype.h>
35 #include <string.h>
36 #include <fcntl.h>
37 #include <dirent.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <stdio.h>
42 // Solaris requires curses.h to be included before term.h
43 #include "dselect-curses.h"
45 #if defined(HAVE_NCURSESW_TERM_H)
46 #include <ncursesw/term.h>
47 #elif defined(HAVE_NCURSES_TERM_H)
48 #include <ncurses/term.h>
49 #else
50 #include <term.h>
51 #endif
53 #include <dpkg/i18n.h>
54 #include <dpkg/dpkg.h>
55 #include <dpkg/dpkg-db.h>
56 #include <dpkg/options.h>
57 #include <dpkg/fsys.h>
59 #include "dselect.h"
60 #include "bindings.h"
61 #include "pkglist.h"
63 static const char printforhelp[] = N_("Type dselect --help for help.");
65 bool expertmode = false;
67 static keybindings packagelistbindings(packagelist_kinterps,packagelist_korgbindings);
69 struct table_t {
70 const char *name;
71 const int num;
74 static const struct table_t colortable[] = {
75 {"black", COLOR_BLACK },
76 {"red", COLOR_RED },
77 {"green", COLOR_GREEN },
78 {"yellow", COLOR_YELLOW },
79 {"blue", COLOR_BLUE },
80 {"magenta", COLOR_MAGENTA },
81 {"cyan", COLOR_CYAN },
82 {"white", COLOR_WHITE },
83 {nullptr, 0},
86 static const struct table_t attrtable[]= {
87 {"normal", A_NORMAL },
88 {"standout", A_STANDOUT },
89 {"underline", A_UNDERLINE },
90 {"reverse", A_REVERSE },
91 {"blink", A_BLINK },
92 {"bright", A_BLINK }, // on some terminals
93 {"dim", A_DIM },
94 {"bold", A_BOLD },
95 {nullptr, 0},
98 /* A slightly confusing mapping from dselect's internal names to
99 * the user-visible names.*/
100 static const struct table_t screenparttable[]= {
101 {"list", list },
102 {"listsel", listsel },
103 {"title", title },
104 {"infohead", thisstate },
105 {"pkgstate", selstate },
106 {"pkgstatesel", selstatesel },
107 {"listhead", colheads },
108 {"query", query },
109 {"info", info_body },
110 {"infodesc", info_head },
111 {"infofoot", whatinfo },
112 {"helpscreen", helpscreen },
113 {nullptr, 0},
116 /* Original colors. */
117 struct colordata color[]= {
118 /* fore back attr */
119 {COLOR_WHITE, COLOR_BLACK, 0 }, // default, not used
120 {COLOR_WHITE, COLOR_BLACK, 0 }, // list
121 {COLOR_WHITE, COLOR_BLACK, A_REVERSE }, // listsel
122 {COLOR_WHITE, COLOR_RED, 0 }, // title
123 {COLOR_WHITE, COLOR_BLUE, 0 }, // thisstate
124 {COLOR_WHITE, COLOR_BLACK, A_BOLD }, // selstate
125 {COLOR_WHITE, COLOR_BLACK, A_REVERSE | A_BOLD }, // selstatesel
126 {COLOR_WHITE, COLOR_BLUE, 0 }, // colheads
127 {COLOR_WHITE, COLOR_RED, 0 }, // query
128 {COLOR_WHITE, COLOR_BLACK, 0 }, // info_body
129 {COLOR_WHITE, COLOR_BLACK, A_BOLD }, // info_head
130 {COLOR_WHITE, COLOR_BLUE, 0 }, // whatinfo
131 {COLOR_WHITE, COLOR_BLACK, 0 }, // help
134 struct menuentry {
135 const char *command;
136 const char *key;
137 const char *option;
138 const char *menuent;
139 urqfunction *fn;
142 static const menuentry menuentries[]= {
143 { "access", N_("a"), N_("[A]ccess"), N_("Choose the access method to use."), &urq_setup },
144 { "update", N_("u"), N_("[U]pdate"), N_("Update list of available packages, if possible."), &urq_update },
145 { "select", N_("s"), N_("[S]elect"), N_("Request which packages you want on your system."), &urq_list },
146 { "install", N_("i"), N_("[I]nstall"),N_("Install and upgrade wanted packages."), &urq_install },
147 { "config", N_("c"), N_("[C]onfig"), N_("Configure any packages that are unconfigured."), &urq_config },
148 { "remove", N_("r"), N_("[R]emove"), N_("Remove unwanted software."), &urq_remove },
149 { "quit", N_("q"), N_("[Q]uit"), N_("Quit dselect."), &urq_quit },
150 { nullptr, nullptr, N_("menu"), nullptr, &urq_menu },
151 { nullptr }
154 static const char programdesc[]=
155 N_("Debian '%s' package handling frontend version %s.\n");
157 static const char licensestring[]= N_(
158 "This is free software; see the GNU General Public License version 2 or\n"
159 "later for copying conditions. There is NO warranty.\n");
161 static void DPKG_ATTR_NORET
162 printversion(const struct cmdinfo *ci, const char *value)
164 printf(gettext(programdesc), DSELECT, PACKAGE_RELEASE);
165 printf("%s", gettext(licensestring));
167 m_output(stdout, _("<standard output>"));
169 exit(0);
172 static void DPKG_ATTR_NORET
173 usage(const struct cmdinfo *ci, const char *value)
175 int i;
177 printf(_(
178 "Usage: %s [<option>...] [<command>...]\n"
179 "\n"), DSELECT);
181 printf(_("Commands:\n"));
182 for (i = 0; menuentries[i].command; i++)
183 printf(" %-10s %s\n", menuentries[i].command, menuentries[i].menuent);
184 fputs("\n", stdout);
186 printf(_(
187 "Options:\n"
188 " --admindir <directory> Use <directory> instead of %s.\n"
189 " --instdir <directory> Use <directory> instead of %s.\n"
190 " --root <directory> Use <directory> instead of %s.\n"
191 " --expert Turn on expert mode.\n"
192 " -D, --debug <file> Turn on debugging, send output to <file>.\n"
193 " --color <color-spec> Configure screen colors.\n"
194 " --colour <color-spec> Ditto.\n"
195 ), ADMINDIR, "/", "/");
197 printf(_(
198 " -?, --help Show this help message.\n"
199 " --version Show the version.\n"
200 "\n"));
202 printf(_("<color-spec> is <screen-part>:[<foreground>],[<background>][:<attr>[+<attr>]...]\n"));
204 printf(_("<screen-part> is:"));
205 for (i=0; screenparttable[i].name; i++)
206 printf(" %s", screenparttable[i].name);
207 fputs("\n", stdout);
209 printf(_("<color> is:"));
210 for (i = 0; colortable[i].name; i++)
211 printf(" %s", colortable[i].name);
212 fputs("\n", stdout);
214 printf(_("<attr> is:"));
215 for (i=0; attrtable[i].name; i++)
216 printf(" %s", attrtable[i].name);
217 fputs("\n", stdout);
219 m_output(stdout, _("<standard output>"));
221 exit(0);
224 /* These are called by C code, so need to have C calling convention */
225 extern "C" {
227 static void
228 set_debug(const struct cmdinfo*, const char *v)
230 FILE *fp;
232 fp = fopen(v, "a");
233 if (!fp)
234 ohshite(_("couldn't open debug file '%.255s'\n"), v);
236 debug_set_output(fp, v);
237 debug_set_mask(dbg_general | dbg_depcon);
240 static void
241 set_expert(const struct cmdinfo*, const char *v)
243 expertmode = true;
246 static int
247 findintable(const struct table_t *table, const char *item, const char *tablename)
249 int i;
251 for (i = 0; item && (table[i].name != nullptr); i++)
252 if (strcasecmp(item, table[i].name) == 0)
253 return table[i].num;
255 ohshit(_("invalid %s '%s'"), tablename, item);
259 * The string's format is:
260 * screenpart:[forecolor][,backcolor][:[<attr>, ...]
261 * Examples: --color title:black,cyan:bright+underline
262 * --color list:red,yellow
263 * --color colheads:,green:bright
264 * --color selstate::reverse // doesn't work FIXME
266 static void
267 set_color(const struct cmdinfo*, const char *string)
269 char *s;
270 char *colors, *attributes;
271 int screenpart;
273 s = m_strdup(string); // strtok modifies strings, keep string const
274 screenpart= findintable(screenparttable, strtok(s, ":"), _("screen part"));
275 colors = strtok(nullptr, ":");
276 attributes = strtok(nullptr, ":");
278 if ((colors == nullptr || ! strlen(colors)) &&
279 (attributes == nullptr || ! strlen(attributes))) {
280 ohshit(_("missing color specification"));
283 if (colors != nullptr && strlen(colors)) {
284 char *colorname;
286 colorname = strtok(colors, ",");
287 if (colorname != nullptr && strlen(colorname)) {
288 // normalize attributes to prevent confusion
289 color[screenpart].attr= A_NORMAL;
290 color[screenpart].fore = findintable(colortable, colorname, _("color"));
292 colorname = strtok(nullptr, ",");
293 if (colorname != nullptr && strlen(colorname)) {
294 color[screenpart].attr= A_NORMAL;
295 color[screenpart].back = findintable(colortable, colorname, _("color"));
299 if (attributes != nullptr && strlen(attributes)) {
300 for (char *attrib = strtok(attributes, "+");
301 attrib != nullptr && strlen(attrib);
302 attrib = strtok(nullptr, "+")) {
303 int aval;
305 aval = findintable(attrtable, attrib, _("color attribute"));
306 if (aval == A_NORMAL) // set to normal
307 color[screenpart].attr= aval;
308 else // add to existing attribs
309 color[screenpart].attr= color[screenpart].attr | aval;
313 free(s);
316 } /* End of extern "C" */
318 static const struct cmdinfo cmdinfos[]= {
319 { "admindir", 0, 1, nullptr, nullptr, set_admindir, 1 },
320 { "instdir", 0, 1, nullptr, nullptr, set_instdir, 1 },
321 { "root", 0, 1, nullptr, nullptr, set_root, 1 },
322 { "debug", 'D', 1, nullptr, nullptr, set_debug },
323 { "expert", 'E', 0, nullptr, nullptr, set_expert },
324 { "help", '?', 0, nullptr, nullptr, usage },
325 { "version", 0, 0, nullptr, nullptr, printversion },
326 { "color", 0, 1, nullptr, nullptr, set_color }, /* US spelling */
327 { "colour", 0, 1, nullptr, nullptr, set_color }, /* UK spelling */
328 { nullptr, 0, 0, nullptr, nullptr, nullptr }
331 static bool cursesareon = false;
332 void curseson() {
333 if (!cursesareon) {
334 const char *cup, *smso;
335 initscr();
336 cup= tigetstr("cup");
337 smso= tigetstr("smso");
338 if (!cup || !smso) {
339 endwin();
340 if (!cup)
341 fputs(_("Terminal does not appear to support cursor addressing.\n"),stderr);
342 if (!smso)
343 fputs(_("Terminal does not appear to support highlighting.\n"),stderr);
344 fprintf(stderr,
345 _("Set your TERM variable correctly, use a better terminal,\n"
346 "or make do with the per-package management tool %s.\n"),
347 DPKG);
348 ohshit(_("terminal lacks necessary features, giving up"));
351 cursesareon = true;
354 void cursesoff() {
355 if (cursesareon) {
356 clear();
357 refresh();
358 endwin();
360 cursesareon = false;
363 urqresult urq_list(void) {
364 modstatdb_open(static_cast<modstatdb_rw>(msdbrw_writeifposs |
365 msdbrw_available_readonly));
367 curseson();
369 packagelist *l= new packagelist(&packagelistbindings);
370 l->resolvesuggest();
371 l->display();
372 delete l;
374 modstatdb_shutdown();
376 return urqr_normal;
379 static void
380 display_menu_entry(int i, int so)
382 const menuentry *me= &menuentries[i];
384 varbuf buf;
385 buf.fmt(" %c %d. %-11.11s %-80.80s ",
386 so ? '*' : ' ', i,
387 gettext(me->option),
388 gettext(me->menuent));
390 int x, y DPKG_ATTR_UNUSED;
391 getmaxyx(stdscr,y,x);
393 attrset(so ? A_REVERSE : A_NORMAL);
394 mvaddnstr(i + 2, 0, buf.string(), x - 1);
395 attrset(A_NORMAL);
398 static int
399 refreshmenu(void)
401 curseson(); cbreak(); noecho(); nonl(); keypad(stdscr,TRUE);
403 int x, y DPKG_ATTR_UNUSED;
404 getmaxyx(stdscr,y,x);
406 varbuf buf;
407 buf.fmt(gettext(programdesc), DSELECT, PACKAGE_RELEASE);
409 clear();
410 attrset(A_BOLD);
411 mvaddnstr(0, 0, buf.string(), x - 1);
413 attrset(A_NORMAL);
414 const struct menuentry *mep; int i;
415 for (mep=menuentries, i=0; mep->option && mep->menuent; mep++, i++)
416 display_menu_entry(i, 0);
418 attrset(A_BOLD);
419 addstr(_("\n\n"
420 "Move around with ^P and ^N, cursor keys, initial letters, or digits;\n"
421 "Press <enter> to confirm selection. ^L redraws screen.\n\n"));
423 attrset(A_NORMAL);
424 addstr(_("Copyright (C) 1994-1996 Ian Jackson.\n"
425 "Copyright (C) 2000,2001 Wichert Akkerman.\n"));
426 addstr(gettext(licensestring));
428 modstatdb_init();
429 if (!modstatdb_can_lock())
430 addstr(_("\n\n"
431 "Read-only access: only preview of selections is available!"));
432 modstatdb_done();
434 return i;
437 urqresult urq_menu(void) {
438 int entries, c;
439 entries= refreshmenu();
440 int cursor=0;
441 display_menu_entry(0, 1);
442 for (;;) {
443 refresh();
444 do {
445 c= getch();
446 if (c == KEY_RESIZE) {
447 refreshmenu();
448 display_menu_entry(cursor, 1);
449 continue;
451 } while (c == ERR && errno == EINTR);
452 if (c==ERR) {
453 if(errno != 0)
454 ohshite(_("failed to getch in main menu"));
455 else {
456 clearok(stdscr, TRUE);
457 clear();
458 refreshmenu();
459 display_menu_entry(cursor, 1);
463 if (c == CTRL('n') || c == KEY_DOWN || c == ' ' || c == 'j') {
464 display_menu_entry(cursor, 0);
465 cursor++;
466 cursor %= entries;
467 display_menu_entry(cursor, 1);
468 } else if (c == CTRL('p') || c == KEY_UP || c == CTRL('h') ||
469 c==KEY_BACKSPACE || c==KEY_DC || c=='k') {
470 display_menu_entry(cursor, 0);
471 cursor += entries - 1;
472 cursor %= entries;
473 display_menu_entry(cursor, 1);
474 } else if (c=='\n' || c=='\r' || c==KEY_ENTER) {
475 clear(); refresh();
477 /* FIXME: trap errors in urq_... */
478 urqresult res = menuentries[cursor].fn();
479 switch (res) {
480 case urqr_quitmenu:
481 return urqr_quitmenu;
482 case urqr_normal:
483 cursor++; cursor %= entries;
484 case urqr_fail:
485 break;
486 default:
487 internerr("unknown menufn %d", res);
489 refreshmenu();
490 display_menu_entry(cursor, 1);
491 } else if (c == CTRL('l')) {
492 clearok(stdscr, TRUE);
493 clear();
494 refreshmenu();
495 display_menu_entry(cursor, 1);
496 } else if (isdigit(c)) {
497 char buf[2]; buf[0]=c; buf[1]=0; c=atoi(buf);
498 if (c < entries) {
499 display_menu_entry(cursor, 0);
500 cursor = c;
501 display_menu_entry(cursor, 1);
502 } else {
503 beep();
505 } else if (isalpha(c)) {
506 c= tolower(c);
507 int i = 0;
508 while (i < entries && gettext(menuentries[i].key)[0] != c)
509 i++;
510 if (i < entries) {
511 display_menu_entry(cursor, 0);
512 cursor = i;
513 display_menu_entry(cursor, 1);
514 } else {
515 beep();
517 } else {
518 beep();
523 urqresult urq_quit(void) {
524 /* FIXME: check packages OK. */
525 return urqr_quitmenu;
528 static void
529 dselect_catch_fatal_error()
531 cursesoff();
532 catch_fatal_error();
536 main(int, const char *const *argv)
538 dpkg_locales_init(DSELECT);
539 dpkg_set_progname(DSELECT);
541 push_error_context_func(dselect_catch_fatal_error, print_fatal_error, nullptr);
543 dpkg_options_load(DSELECT, cmdinfos);
544 dpkg_options_parse(&argv, cmdinfos, printforhelp);
546 debug(dbg_general, "root=%s admindir=%s", dpkg_fsys_get_dir(), dpkg_db_get_dir());
548 if (*argv) {
549 const char *a;
550 while ((a = *argv++) != nullptr) {
551 const menuentry *me = menuentries;
552 while (me->command && strcmp(me->command, a))
553 me++;
554 if (!me->command)
555 badusage(_("unknown action string '%.50s'"), a);
556 me->fn();
558 } else {
559 urq_menu();
562 cursesoff();
563 dpkg_program_done();
564 dpkg_locales_done();
566 return(0);