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/>.
26 #include <sys/types.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>
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>
63 static const char printforhelp
[] = N_("Type dselect --help for help.");
65 bool expertmode
= false;
67 static keybindings
packagelistbindings(packagelist_kinterps
,packagelist_korgbindings
);
74 static const struct table_t colortable
[] = {
75 {"black", COLOR_BLACK
},
77 {"green", COLOR_GREEN
},
78 {"yellow", COLOR_YELLOW
},
79 {"blue", COLOR_BLUE
},
80 {"magenta", COLOR_MAGENTA
},
81 {"cyan", COLOR_CYAN
},
82 {"white", COLOR_WHITE
},
86 static const struct table_t attrtable
[]= {
87 {"normal", A_NORMAL
},
88 {"standout", A_STANDOUT
},
89 {"underline", A_UNDERLINE
},
90 {"reverse", A_REVERSE
},
92 {"bright", A_BLINK
}, // on some terminals
98 /* A slightly confusing mapping from dselect's internal names to
99 * the user-visible names.*/
100 static const struct table_t screenparttable
[]= {
102 {"listsel", listsel
},
104 {"infohead", thisstate
},
105 {"pkgstate", selstate
},
106 {"pkgstatesel", selstatesel
},
107 {"listhead", colheads
},
109 {"info", info_body
},
110 {"infodesc", info_head
},
111 {"infofoot", whatinfo
},
112 {"helpscreen", helpscreen
},
116 /* Original colors. */
117 struct colordata color
[]= {
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
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
},
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>"));
172 static void DPKG_ATTR_NORET
173 usage(const struct cmdinfo
*ci
, const char *value
)
178 "Usage: %s [<option>...] [<command>...]\n"
181 printf(_("Commands:\n"));
182 for (i
= 0; menuentries
[i
].command
; i
++)
183 printf(" %-10s %s\n", menuentries
[i
].command
, menuentries
[i
].menuent
);
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
, "/", "/");
198 " -?, --help Show this help message.\n"
199 " --version Show the version.\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
);
209 printf(_("<color> is:"));
210 for (i
= 0; colortable
[i
].name
; i
++)
211 printf(" %s", colortable
[i
].name
);
214 printf(_("<attr> is:"));
215 for (i
=0; attrtable
[i
].name
; i
++)
216 printf(" %s", attrtable
[i
].name
);
219 m_output(stdout
, _("<standard output>"));
224 /* These are called by C code, so need to have C calling convention */
228 set_debug(const struct cmdinfo
*, const char *v
)
234 ohshite(_("couldn't open debug file '%.255s'\n"), v
);
236 debug_set_output(fp
, v
);
237 debug_set_mask(dbg_general
| dbg_depcon
);
241 set_expert(const struct cmdinfo
*, const char *v
)
247 findintable(const struct table_t
*table
, const char *item
, const char *tablename
)
251 for (i
= 0; item
&& (table
[i
].name
!= nullptr); i
++)
252 if (strcasecmp(item
, table
[i
].name
) == 0)
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
267 set_color(const struct cmdinfo
*, const char *string
)
270 char *colors
, *attributes
;
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
)) {
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, "+")) {
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
;
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;
334 const char *cup
, *smso
;
336 cup
= tigetstr("cup");
337 smso
= tigetstr("smso");
341 fputs(_("Terminal does not appear to support cursor addressing.\n"),stderr
);
343 fputs(_("Terminal does not appear to support highlighting.\n"),stderr
);
345 _("Set your TERM variable correctly, use a better terminal,\n"
346 "or make do with the per-package management tool %s.\n"),
348 ohshit(_("terminal lacks necessary features, giving up"));
363 urqresult
urq_list(void) {
364 modstatdb_open(static_cast<modstatdb_rw
>(msdbrw_writeifposs
|
365 msdbrw_available_readonly
));
369 packagelist
*l
= new packagelist(&packagelistbindings
);
374 modstatdb_shutdown();
380 display_menu_entry(int i
, int so
)
382 const menuentry
*me
= &menuentries
[i
];
385 buf
.fmt(" %c %d. %-11.11s %-80.80s ",
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);
401 curseson(); cbreak(); noecho(); nonl(); keypad(stdscr
,TRUE
);
403 int x
, y DPKG_ATTR_UNUSED
;
404 getmaxyx(stdscr
,y
,x
);
407 buf
.fmt(gettext(programdesc
), DSELECT
, PACKAGE_RELEASE
);
411 mvaddnstr(0, 0, buf
.string(), x
- 1);
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);
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"));
424 addstr(_("Copyright (C) 1994-1996 Ian Jackson.\n"
425 "Copyright (C) 2000,2001 Wichert Akkerman.\n"));
426 addstr(gettext(licensestring
));
429 if (!modstatdb_can_lock())
431 "Read-only access: only preview of selections is available!"));
437 urqresult
urq_menu(void) {
439 entries
= refreshmenu();
441 display_menu_entry(0, 1);
446 if (c
== KEY_RESIZE
) {
448 display_menu_entry(cursor
, 1);
451 } while (c
== ERR
&& errno
== EINTR
);
454 ohshite(_("failed to getch in main menu"));
456 clearok(stdscr
, TRUE
);
459 display_menu_entry(cursor
, 1);
463 if (c
== CTRL('n') || c
== KEY_DOWN
|| c
== ' ' || c
== 'j') {
464 display_menu_entry(cursor
, 0);
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;
473 display_menu_entry(cursor
, 1);
474 } else if (c
=='\n' || c
=='\r' || c
==KEY_ENTER
) {
477 /* FIXME: trap errors in urq_... */
478 urqresult res
= menuentries
[cursor
].fn();
481 return urqr_quitmenu
;
483 cursor
++; cursor
%= entries
;
487 internerr("unknown menufn %d", res
);
490 display_menu_entry(cursor
, 1);
491 } else if (c
== CTRL('l')) {
492 clearok(stdscr
, TRUE
);
495 display_menu_entry(cursor
, 1);
496 } else if (isdigit(c
)) {
497 char buf
[2]; buf
[0]=c
; buf
[1]=0; c
=atoi(buf
);
499 display_menu_entry(cursor
, 0);
501 display_menu_entry(cursor
, 1);
505 } else if (isalpha(c
)) {
508 while (i
< entries
&& gettext(menuentries
[i
].key
)[0] != c
)
511 display_menu_entry(cursor
, 0);
513 display_menu_entry(cursor
, 1);
523 urqresult
urq_quit(void) {
524 /* FIXME: check packages OK. */
525 return urqr_quitmenu
;
529 dselect_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());
550 while ((a
= *argv
++) != nullptr) {
551 const menuentry
*me
= menuentries
;
552 while (me
->command
&& strcmp(me
->command
, a
))
555 badusage(_("unknown action string '%.50s'"), a
);