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>
43 // Solaris requires curses.h to be included before term.h
44 #include "dselect-curses.h"
46 #if defined(HAVE_NCURSESW_TERM_H)
47 #include <ncursesw/term.h>
48 #elif defined(HAVE_NCURSES_TERM_H)
49 #include <ncurses/term.h>
54 #include <dpkg/i18n.h>
55 #include <dpkg/dpkg.h>
56 #include <dpkg/dpkg-db.h>
57 #include <dpkg/options.h>
63 static const char printforhelp
[] = N_("Type dselect --help for help.");
65 bool expertmode
= false;
67 static const char *admindir
= ADMINDIR
;
69 static keybindings
packagelistbindings(packagelist_kinterps
,packagelist_korgbindings
);
76 static const struct table_t colourtable
[]= {
77 {"black", COLOR_BLACK
},
79 {"green", COLOR_GREEN
},
80 {"yellow", COLOR_YELLOW
},
81 {"blue", COLOR_BLUE
},
82 {"magenta", COLOR_MAGENTA
},
83 {"cyan", COLOR_CYAN
},
84 {"white", COLOR_WHITE
},
88 static const struct table_t attrtable
[]= {
89 {"normal", A_NORMAL
},
90 {"standout", A_STANDOUT
},
91 {"underline", A_UNDERLINE
},
92 {"reverse", A_REVERSE
},
94 {"bright", A_BLINK
}, // on some terminals
100 /* A slightly confusing mapping from dselect's internal names to
101 * the user-visible names.*/
102 static const struct table_t screenparttable
[]= {
104 {"listsel", listsel
},
106 {"infohead", thisstate
},
107 {"pkgstate", selstate
},
108 {"pkgstatesel", selstatesel
},
109 {"listhead", colheads
},
111 {"info", info_body
},
112 {"infodesc", info_head
},
113 {"infofoot", whatinfo
},
114 {"helpscreen", helpscreen
},
118 /* Historical (patriotic?) colours. */
119 struct colordata color
[]= {
121 {COLOR_WHITE
, COLOR_BLACK
, 0 }, // default, not used
122 {COLOR_WHITE
, COLOR_BLACK
, 0 }, // list
123 {COLOR_WHITE
, COLOR_BLACK
, A_REVERSE
}, // listsel
124 {COLOR_WHITE
, COLOR_RED
, 0 }, // title
125 {COLOR_WHITE
, COLOR_BLUE
, 0 }, // thisstate
126 {COLOR_WHITE
, COLOR_BLACK
, A_BOLD
}, // selstate
127 {COLOR_WHITE
, COLOR_BLACK
, A_REVERSE
| A_BOLD
}, // selstatesel
128 {COLOR_WHITE
, COLOR_BLUE
, 0 }, // colheads
129 {COLOR_WHITE
, COLOR_RED
, 0 }, // query
130 {COLOR_WHITE
, COLOR_BLACK
, 0 }, // info_body
131 {COLOR_WHITE
, COLOR_BLACK
, A_BOLD
}, // info_head
132 {COLOR_WHITE
, COLOR_BLUE
, 0 }, // whatinfo
133 {COLOR_WHITE
, COLOR_BLACK
, 0 }, // help
144 static const menuentry menuentries
[]= {
145 { "access", N_("a"), N_("[A]ccess"), N_("Choose the access method to use."), &urq_setup
},
146 { "update", N_("u"), N_("[U]pdate"), N_("Update list of available packages, if possible."), &urq_update
},
147 { "select", N_("s"), N_("[S]elect"), N_("Request which packages you want on your system."), &urq_list
},
148 { "install", N_("i"), N_("[I]nstall"),N_("Install and upgrade wanted packages."), &urq_install
},
149 { "config", N_("c"), N_("[C]onfig"), N_("Configure any packages that are unconfigured."), &urq_config
},
150 { "remove", N_("r"), N_("[R]emove"), N_("Remove unwanted software."), &urq_remove
},
151 { "quit", N_("q"), N_("[Q]uit"), N_("Quit dselect."), &urq_quit
},
152 { nullptr, nullptr, N_("menu"), nullptr, &urq_menu
},
156 static const char programdesc
[]=
157 N_("Debian '%s' package handling frontend version %s.\n");
159 static const char licensestring
[]= N_(
160 "This is free software; see the GNU General Public License version 2 or\n"
161 "later for copying conditions. There is NO warranty.\n");
163 static void DPKG_ATTR_NORET
164 printversion(const struct cmdinfo
*ci
, const char *value
)
166 printf(gettext(programdesc
), DSELECT
, PACKAGE_RELEASE
);
167 printf("%s", gettext(licensestring
));
169 m_output(stdout
, _("<standard output>"));
174 static void DPKG_ATTR_NORET
175 usage(const struct cmdinfo
*ci
, const char *value
)
180 "Usage: %s [<option>...] [<command>...]\n"
183 printf(_("Commands:\n"));
184 for (i
= 0; menuentries
[i
].command
; i
++)
185 printf(" %-10s %s\n", menuentries
[i
].command
, menuentries
[i
].menuent
);
190 " --admindir <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"
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; colourtable
[i
].name
; i
++)
211 printf(" %s", colourtable
[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 *colours
, *attributes
, *attrib
, *colourname
;
271 int screenpart
, aval
;
273 s
= m_strdup(string
); // strtok modifies strings, keep string const
274 screenpart
= findintable(screenparttable
, strtok(s
, ":"), _("screen part"));
275 colours
= strtok(nullptr, ":");
276 attributes
= strtok(nullptr, ":");
278 if ((colours
== nullptr || ! strlen(colours
)) &&
279 (attributes
== nullptr || ! strlen(attributes
))) {
280 ohshit(_("null colour specification"));
283 if (colours
!= nullptr && strlen(colours
)) {
284 colourname
= strtok(colours
, ",");
285 if (colourname
!= nullptr && strlen(colourname
)) {
286 // normalize attributes to prevent confusion
287 color
[screenpart
].attr
= A_NORMAL
;
288 color
[screenpart
].fore
=findintable(colourtable
, colourname
, _("colour"));
290 colourname
= strtok(nullptr, ",");
291 if (colourname
!= nullptr && strlen(colourname
)) {
292 color
[screenpart
].attr
= A_NORMAL
;
293 color
[screenpart
].back
=findintable(colourtable
, colourname
, _("colour"));
297 if (attributes
!= nullptr && strlen(attributes
)) {
298 for (attrib
= strtok(attributes
, "+");
299 attrib
!= nullptr && strlen(attrib
);
300 attrib
= strtok(nullptr, "+")) {
301 aval
=findintable(attrtable
, attrib
, _("colour attribute"));
302 if (aval
== A_NORMAL
) // set to normal
303 color
[screenpart
].attr
= aval
;
304 else // add to existing attribs
305 color
[screenpart
].attr
= color
[screenpart
].attr
| aval
;
312 } /* End of extern "C" */
314 static const struct cmdinfo cmdinfos
[]= {
315 { "admindir", 0, 1, nullptr, &admindir
, nullptr },
316 { "debug", 'D', 1, nullptr, nullptr, set_debug
},
317 { "expert", 'E', 0, nullptr, nullptr, set_expert
},
318 { "help", '?', 0, nullptr, nullptr, usage
},
319 { "version", 0, 0, nullptr, nullptr, printversion
},
320 { "color", 0, 1, nullptr, nullptr, set_color
}, /* US spelling */
321 { "colour", 0, 1, nullptr, nullptr, set_color
}, /* UK spelling */
322 { nullptr, 0, 0, nullptr, nullptr, nullptr }
325 static bool cursesareon
= false;
328 const char *cup
, *smso
;
330 cup
= tigetstr("cup");
331 smso
= tigetstr("smso");
335 fputs(_("Terminal does not appear to support cursor addressing.\n"),stderr
);
337 fputs(_("Terminal does not appear to support highlighting.\n"),stderr
);
339 _("Set your TERM variable correctly, use a better terminal,\n"
340 "or make do with the per-package management tool %s.\n"),
342 ohshit(_("terminal lacks necessary features, giving up"));
357 urqresult
urq_list(void) {
358 modstatdb_open((modstatdb_rw
)(msdbrw_writeifposs
|
359 msdbrw_available_readonly
));
363 packagelist
*l
= new packagelist(&packagelistbindings
);
368 modstatdb_shutdown();
376 const menuentry
*me
= &menuentries
[i
];
379 buf
.fmt(" %c %d. %-11.11s %-80.80s ",
382 gettext(me
->menuent
));
384 int x
, y DPKG_ATTR_UNUSED
;
385 getmaxyx(stdscr
,y
,x
);
387 attrset(so ? A_REVERSE
: A_NORMAL
);
388 mvaddnstr(i
+ 2, 0, buf
.string(), x
- 1);
395 curseson(); cbreak(); noecho(); nonl(); keypad(stdscr
,TRUE
);
397 int x
, y DPKG_ATTR_UNUSED
;
398 getmaxyx(stdscr
,y
,x
);
401 buf
.fmt(gettext(programdesc
), DSELECT
, PACKAGE_RELEASE
);
405 mvaddnstr(0, 0, buf
.string(), x
- 1);
408 const struct menuentry
*mep
; int i
;
409 for (mep
=menuentries
, i
=0; mep
->option
&& mep
->menuent
; mep
++, i
++)
414 "Move around with ^P and ^N, cursor keys, initial letters, or digits;\n"
415 "Press <enter> to confirm selection. ^L redraws screen.\n\n"));
418 addstr(_("Copyright (C) 1994-1996 Ian Jackson.\n"
419 "Copyright (C) 2000,2001 Wichert Akkerman.\n"));
420 addstr(gettext(licensestring
));
423 if (!modstatdb_can_lock())
425 "Read-only access: only preview of selections is available!"));
431 urqresult
urq_menu(void) {
433 entries
= refreshmenu();
440 while (c
== ERR
&& errno
== EINTR
);
443 ohshite(_("failed to getch in main menu"));
445 clearok(stdscr
,TRUE
); clear(); refreshmenu(); dme(cursor
,1);
449 if (c
== CTRL('n') || c
== KEY_DOWN
|| c
== ' ' || c
== 'j') {
450 dme(cursor
,0); cursor
++; cursor
%= entries
; dme(cursor
,1);
451 } else if (c
== CTRL('p') || c
== KEY_UP
|| c
== CTRL('h') ||
452 c
==KEY_BACKSPACE
|| c
==KEY_DC
|| c
=='k') {
453 dme(cursor
,0); cursor
+= entries
-1; cursor
%= entries
; dme(cursor
,1);
454 } else if (c
=='\n' || c
=='\r' || c
==KEY_ENTER
) {
457 /* FIXME: trap errors in urq_... */
458 urqresult res
= menuentries
[cursor
].fn();
461 return urqr_quitmenu
;
463 cursor
++; cursor
%= entries
;
467 internerr("unknown menufn %d", res
);
469 refreshmenu(); dme(cursor
,1);
470 } else if (c
== CTRL('l')) {
471 clearok(stdscr
,TRUE
); clear(); refreshmenu(); dme(cursor
,1);
472 } else if (isdigit(c
)) {
473 char buf
[2]; buf
[0]=c
; buf
[1]=0; c
=atoi(buf
);
475 dme(cursor
,0); cursor
=c
; dme(cursor
,1);
479 } else if (isalpha(c
)) {
482 while (i
< entries
&& gettext(menuentries
[i
].key
)[0] != c
)
485 dme(cursor
,0); cursor
=i
; dme(cursor
,1);
495 urqresult
urq_quit(void) {
496 /* FIXME: check packages OK. */
497 return urqr_quitmenu
;
501 dselect_catch_fatal_error()
508 main(int, const char *const *argv
)
510 dpkg_locales_init(DSELECT
);
511 dpkg_set_progname(DSELECT
);
513 push_error_context_func(dselect_catch_fatal_error
, print_fatal_error
, nullptr);
515 dpkg_options_load(DSELECT
, cmdinfos
);
516 dpkg_options_parse(&argv
, cmdinfos
, printforhelp
);
518 admindir
= dpkg_db_set_dir(admindir
);
522 while ((a
= *argv
++) != nullptr) {
523 const menuentry
*me
= menuentries
;
524 while (me
->command
&& strcmp(me
->command
, a
))
527 badusage(_("unknown action string '%.50s'"), a
);