
commit
88ff0ffa41
19 changed files with 1102 additions and 0 deletions
@ -0,0 +1,29 @@ |
|||
PROGRAM = tasksel |
|||
VERSION=\"0.1\" |
|||
CC = gcc |
|||
CFLAGS = -g -Wall |
|||
DEFS = -DVERSION=$(VERSION) -DPACKAGE=\"$(PROGRAM)\" -DLOCALEDIR=\"/usr/share/locale\" #-DDEBUG |
|||
LIBS = -lslang |
|||
OBJS = tasksel.o slangui.o data.o util.o strutl.o |
|||
|
|||
COMPILE = $(CC) $(CFLAGS) $(DEFS) -c |
|||
LINK = $(CC) $(CFLAGS) $(DEFS) -o |
|||
|
|||
all: $(PROGRAM) |
|||
|
|||
%.o: %.c |
|||
$(COMPILE) $< |
|||
|
|||
po/build_stamp: |
|||
$(MAKE) -C po |
|||
|
|||
$(PROGRAM): $(OBJS) po/build_stamp |
|||
$(LINK) $(PROGRAM) $(OBJS) $(LIBS) |
|||
|
|||
test: |
|||
$(MAKE) -C scratch |
|||
|
|||
clean: |
|||
rm -f $(PROGRAM) *.o *~ |
|||
$(MAKE) -C po clean |
|||
|
@ -0,0 +1,31 @@ |
|||
$Id: README,v 1.1 1999/11/21 22:01:04 tausq Exp $ |
|||
Task Selection UI v0.1 |
|||
Nov 20, 1999 |
|||
Randolph Chung <tausq@debian.org> |
|||
|
|||
Here's a first cut at a task selection user interface. |
|||
|
|||
The interface GUI is based on libslang. It has no other special library |
|||
dependencies. |
|||
|
|||
On startup, the tasksel program will read /var/lib/dpkg/available to |
|||
identify task packages (matching "task-*"). These packages will be |
|||
presented in a simple list selection screen with their short descriptions. |
|||
Users can drill down into the task packages to see detailed descriptions and |
|||
some information about the packages in the task. |
|||
|
|||
On exit, tasksel executes the appropriate apt-get command to install the |
|||
selected packages. If the -t option is given, then tasksel prints out the |
|||
command line to use to stdout instead. All other messages are printed to |
|||
stderr. |
|||
|
|||
The appropriate display strings in the source have been marked with gettext |
|||
macros, though I have never done any other i18n work and am not sure if it |
|||
is done correctly at the moment. |
|||
|
|||
A word on building.... use make CFLAGS="-Os" to get a smaller binary. The |
|||
default build rule produces binaries with debugging symbols. You can also |
|||
turn on -DDEBUG to get extra debugging info. |
|||
|
|||
Comments and suggestions are most welcome. |
|||
|
@ -0,0 +1,2 @@ |
|||
$Id: TODO,v 1.1 1999/11/21 22:01:04 tausq Exp $ |
|||
- fix screen resize code |
@ -0,0 +1,248 @@ |
|||
/* $Id: data.c,v 1.1 1999/11/21 22:01:04 tausq Exp $ */ |
|||
/* data.c - encapsulates functions for reading a package listing like dpkg's available file
|
|||
* Internally, packages are stored in a binary tree format to faciliate search operations |
|||
*/ |
|||
#include "data.h" |
|||
|
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <search.h> |
|||
#include <string.h> |
|||
#include <ctype.h> |
|||
|
|||
#include "util.h" |
|||
#include "macros.h" |
|||
|
|||
#define PACKAGEFIELD "Package: " |
|||
#define DEPENDSFIELD "Depends: " |
|||
#define RECOMMENDSFIELD "Recommends: " |
|||
#define SUGGESTSFIELD "Suggests: " |
|||
#define DESCRIPTIONFIELD "Description: " |
|||
#define AVAILABLEFILE "/var/lib/dpkg/available" |
|||
#define BUF_SIZE 1024 |
|||
#define MATCHFIELD(buf, s) (strncmp(buf, s, strlen(s)) == 0) |
|||
#define FIELDDATA(buf, s) (buf + strlen(s)) |
|||
#define CHOMP(s) if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = 0 |
|||
|
|||
/* module variables */ |
|||
static struct package_t **_packages_enumbuf = NULL; |
|||
static int _packages_enumcount = 0; |
|||
static void *_packages_root = NULL; |
|||
|
|||
/* private functions */ |
|||
static int packagecompare(const void *p1, const void *p2) |
|||
{ |
|||
/* compares two packages by name; used for binary tree routines */ |
|||
char *s1, *s2; |
|||
s1 = ((struct package_t *)p1)->name; |
|||
s2 = ((struct package_t *)p2)->name; |
|||
return strcmp(s1, s2); |
|||
} |
|||
|
|||
static void packages_walk_enumerate(const void *nodep, const VISIT order, const int depth) |
|||
{ |
|||
/* adds nodep to list of nodes if we haven't visited this node before */ |
|||
struct package_t *datap = *(struct package_t **)nodep; |
|||
if (order == leaf || order == endorder) { |
|||
_packages_enumcount++; |
|||
_packages_enumbuf[_packages_enumcount - 1] = datap; |
|||
} |
|||
} |
|||
|
|||
static void packages_walk_delete(const void *nodep, const VISIT order, const int depth) |
|||
{ |
|||
/* deletes memory associated with nodep */ |
|||
struct package_t *datap = *(struct package_t **)nodep; |
|||
int i; |
|||
|
|||
if (order == leaf || order == endorder) { |
|||
tdelete((void *)datap, &_packages_root, packagecompare); |
|||
if (datap->name) FREE(datap->name); |
|||
if (datap->shortdesc) FREE(datap->shortdesc); |
|||
if (datap->longdesc) FREE(datap->longdesc); |
|||
if (datap->depends) { |
|||
for (i = 0; i < datap->dependscount; i++) FREE(datap->depends[i]); |
|||
FREE(datap->depends); |
|||
} |
|||
if (datap->suggests) { |
|||
for (i = 0; i < datap->suggestscount; i++) FREE(datap->suggests[i]); |
|||
FREE(datap->suggests); |
|||
} |
|||
if (datap->recommends) { |
|||
for (i = 0; i < datap->recommendscount; i++) FREE(datap->recommends[i]); |
|||
FREE(datap->recommends); |
|||
} |
|||
FREE(datap); |
|||
} |
|||
} |
|||
|
|||
int splitlinkdesc(const char *desc, char ***array) |
|||
{ |
|||
/* given a comma separate list of names, returns an array with the names split into elts of the array */ |
|||
char *p; |
|||
char *token; |
|||
int i = 0, elts = 1; |
|||
|
|||
VERIFY(array != NULL); |
|||
*array = NULL; |
|||
if (desc == NULL) return 0; |
|||
|
|||
p = (char *)desc; |
|||
while (*p != 0) if (*p++ == ',') elts++; |
|||
|
|||
*array = MALLOC(sizeof(char *) * elts); |
|||
memset(*array, 0, sizeof(char *) * elts); |
|||
|
|||
p = (char *)desc; |
|||
while ((token = strsep(&p, ","))) { |
|||
while (isspace(*token)) token++; |
|||
(*array)[i++] = STRDUP(token); |
|||
} |
|||
|
|||
return elts; |
|||
} |
|||
|
|||
static void addpackage(struct packages_t *pkgs, |
|||
const char *name, const char *dependsdesc, const char *recommendsdesc, |
|||
const char *suggestsdesc, const char *shortdesc, const char *longdesc) |
|||
{ |
|||
/* Adds package to the package list binary tree */ |
|||
struct package_t *node = NEW(struct package_t); |
|||
void *p; |
|||
|
|||
VERIFY(name != NULL); |
|||
|
|||
/* DPRINTF("Adding package %s to list\n", name); */ |
|||
memset(node, 0, sizeof(struct package_t)); |
|||
node->name = STRDUP(name); |
|||
node->shortdesc = STRDUP(shortdesc); |
|||
node->longdesc = STRDUP(longdesc); |
|||
|
|||
if (dependsdesc) node->dependscount = splitlinkdesc(dependsdesc, &node->depends); |
|||
if (recommendsdesc) node->recommendscount = splitlinkdesc(recommendsdesc, &node->recommends); |
|||
if (suggestsdesc) node->suggestscount = splitlinkdesc(suggestsdesc, &node->suggests); |
|||
|
|||
p = tsearch((void *)node, &pkgs->packages, packagecompare); |
|||
VERIFY(p != NULL); |
|||
pkgs->count++; |
|||
} |
|||
|
|||
/* public functions */ |
|||
struct package_t *packages_find(const struct packages_t *pkgs, const char *name) |
|||
{ |
|||
/* Given a package name, returns a pointer to the appropriate package_t structure
|
|||
* or NULL if none is found */ |
|||
struct package_t pkg; |
|||
void *result; |
|||
|
|||
pkg.name = (char *)name; |
|||
result = tfind((void *)&pkg, &pkgs->packages, packagecompare); |
|||
|
|||
if (result == NULL) |
|||
return NULL; |
|||
else |
|||
return *(struct package_t **)result; |
|||
} |
|||
|
|||
struct package_t **packages_enumerate(const struct packages_t *packages) |
|||
{ |
|||
/* Converts the packages binary tree into a array. Do not modify the returned array! It
|
|||
* is a static variable managed by this module */ |
|||
|
|||
if (_packages_enumbuf != NULL) { |
|||
FREE(_packages_enumbuf); |
|||
} |
|||
|
|||
_packages_enumbuf = MALLOC(sizeof(struct package_t *) * packages->count); |
|||
if (_packages_enumbuf == NULL) |
|||
DIE("Cannot allocate memory for enumeration buffer"); |
|||
memset(_packages_enumbuf, 0, sizeof(struct package_t *) * packages->count); |
|||
_packages_enumcount = 0; |
|||
twalk((void *)packages->packages, packages_walk_enumerate); |
|||
ASSERT(_packages_enumcount == packages->count); |
|||
return _packages_enumbuf; |
|||
} |
|||
|
|||
void packages_readlist(struct packages_t *taskpkgs, struct packages_t *pkgs) |
|||
{ |
|||
/* Populates internal data structures with information for an available file */ |
|||
FILE *f; |
|||
char buf[BUF_SIZE]; |
|||
char *name, *shortdesc, *longdesc; |
|||
char *dependsdesc, *recommendsdesc, *suggestsdesc; |
|||
|
|||
VERIFY(taskpkgs != NULL); VERIFY(pkgs != NULL); |
|||
|
|||
/* Initialization */ |
|||
memset(taskpkgs, 0, sizeof(struct packages_t)); |
|||
memset(pkgs, 0, sizeof(struct packages_t)); |
|||
|
|||
if ((f = fopen(AVAILABLEFILE, "r")) == NULL) PERROR(AVAILABLEFILE); |
|||
while (!feof(f)) { |
|||
fgets(buf, BUF_SIZE, f); |
|||
CHOMP(buf); |
|||
if (MATCHFIELD(buf, PACKAGEFIELD)) { |
|||
/*DPRINTF("Package = %s\n", FIELDDATA(buf, PACKAGEFIELD)); */ |
|||
name = shortdesc = longdesc = dependsdesc = recommendsdesc = suggestsdesc = NULL; |
|||
|
|||
name = STRDUP(FIELDDATA(buf, PACKAGEFIELD)); |
|||
VERIFY(name != NULL); |
|||
|
|||
/* look for depends/suggests and shotdesc/longdesc */ |
|||
while (!feof(f)) { |
|||
fgets(buf, BUF_SIZE, f); |
|||
CHOMP(buf); |
|||
if (buf[0] == 0) break; |
|||
|
|||
if (MATCHFIELD(buf, DEPENDSFIELD)) { |
|||
dependsdesc = STRDUP(FIELDDATA(buf, DEPENDSFIELD)); |
|||
VERIFY(dependsdesc != NULL); |
|||
} else if (MATCHFIELD(buf, RECOMMENDSFIELD)) { |
|||
recommendsdesc = STRDUP(FIELDDATA(buf, RECOMMENDSFIELD)); |
|||
VERIFY(recommendsdesc != NULL); |
|||
} else if (MATCHFIELD(buf, SUGGESTSFIELD)) { |
|||
suggestsdesc = STRDUP(FIELDDATA(buf, SUGGESTSFIELD)); |
|||
VERIFY(suggestsdesc != NULL); |
|||
} else if (MATCHFIELD(buf, DESCRIPTIONFIELD)) { |
|||
shortdesc = STRDUP(FIELDDATA(buf, DESCRIPTIONFIELD)); |
|||
VERIFY(shortdesc != NULL); |
|||
do { |
|||
fgets(buf, BUF_SIZE, f); |
|||
if (buf[0] != ' ') break; |
|||
if (buf[1] == '.') buf[1] = ' '; |
|||
if (longdesc == NULL) { |
|||
longdesc = (char *)MALLOC(strlen(buf) + 1); |
|||
strcpy(longdesc, buf + 1); |
|||
} else { |
|||
longdesc = realloc(longdesc, strlen(longdesc) + strlen(buf) + 1); |
|||
strcat(longdesc, buf + 1); |
|||
} |
|||
} while (buf[0] != '\n' && !feof(f)); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
addpackage(pkgs, name, NULL, NULL, NULL, shortdesc, NULL); |
|||
if (strncmp(name, "task-", 5) == 0) |
|||
addpackage(taskpkgs, name, dependsdesc, recommendsdesc, suggestsdesc, shortdesc, longdesc); |
|||
|
|||
if (name != NULL) FREE(name); |
|||
if (dependsdesc != NULL) FREE(dependsdesc); |
|||
if (recommendsdesc != NULL) FREE(recommendsdesc); |
|||
if (suggestsdesc != NULL) FREE(suggestsdesc); |
|||
if (shortdesc != NULL) FREE(shortdesc); |
|||
if (longdesc != NULL) FREE(longdesc); |
|||
} |
|||
}; |
|||
fclose(f); |
|||
} |
|||
|
|||
void packages_free(struct packages_t *taskpkgs, struct packages_t *pkgs) |
|||
{ |
|||
/* Frees up memory allocated by packages_readlist */ |
|||
_packages_root = taskpkgs->packages; |
|||
twalk(taskpkgs->packages, packages_walk_delete); |
|||
_packages_root = pkgs->packages; |
|||
twalk(pkgs->packages, packages_walk_delete); |
|||
} |
|||
|
@ -0,0 +1,31 @@ |
|||
/* $Id: data.h,v 1.1 1999/11/21 22:01:04 tausq Exp $ */ |
|||
#ifndef _DATA_H |
|||
#define _DATA_H |
|||
|
|||
struct package_t { |
|||
char *name; |
|||
char *shortdesc; |
|||
char *longdesc; |
|||
int dependscount; |
|||
char **depends; |
|||
int recommendscount; |
|||
char **recommends; |
|||
int suggestscount; |
|||
char **suggests; |
|||
int selected; |
|||
}; |
|||
|
|||
struct packages_t { |
|||
int count; |
|||
void *packages; |
|||
}; |
|||
|
|||
/* Reads in a list of package and package descriptions */ |
|||
void packages_readlist(struct packages_t *taskpackages, struct packages_t *packages); |
|||
/* free memory allocated to store packages */ |
|||
void packages_free(struct packages_t *taskpackages, struct packages_t *packages); |
|||
|
|||
struct package_t *packages_find(const struct packages_t *packages, const char *name); |
|||
struct package_t **packages_enumerate(const struct packages_t *packages); |
|||
|
|||
#endif |
@ -0,0 +1,14 @@ |
|||
/* $Id: help.h,v 1.1 1999/11/21 22:01:04 tausq Exp $ */ |
|||
#ifndef _HELP_H |
|||
#define _HELP_H |
|||
|
|||
#define HELPTXT _("Task packages are \"metapackages\" that allow you to quickly install a selection of " \ |
|||
"packages that performs a given task.\n\nThe main chooser list shows a list of " \ |
|||
"tasks that you can choose to install. The arrow keys moves the cursor. Pressing ENTER " \ |
|||
"or the SPACEBAR toggles the selection of the package at the cursor. You can also press " \ |
|||
"A to select all packages, or N to deselect all packages. Pressing Q will exit this " \ |
|||
"program and begin installation of your selected tasks.\n\n\nThank you for using Debian." \ |
|||
"\n\nPress enter to return to the task selection screen" \ |
|||
) |
|||
|
|||
#endif |
@ -0,0 +1,54 @@ |
|||
/* $Id: macros.h,v 1.1 1999/11/21 22:01:04 tausq Exp $ */ |
|||
#ifndef _MACROS_H |
|||
#define _MACROS_H |
|||
|
|||
#include <stdio.h> |
|||
#include <errno.h> |
|||
|
|||
#ifdef DEBUG |
|||
#define DPRINTF(fmt, arg...) \ |
|||
do { \ |
|||
fprintf(stderr, "%s:%d ", __FILE__, __LINE__); \ |
|||
fprintf(stderr, fmt, ##arg); \ |
|||
fprintf(stderr, "\r\n"); \ |
|||
} while (0); |
|||
#define ASSERT(cond) \ |
|||
if (!(cond)) { \ |
|||
fprintf(stderr, "ASSERTION FAILED at %s:%d! (%s)\n", __FILE__, __LINE__, #cond); \ |
|||
exit(255); \ |
|||
} |
|||
#define VERIFY(cond) ASSERT(cond) |
|||
#define ABORT abort() |
|||
#define STRDUP(s) safe_strdup(s) |
|||
#define MALLOC(sz) safe_malloc(sz) |
|||
#define FREE(p) safe_free((void **)&p); |
|||
#else |
|||
#define DPRINTF(fmt, arg...) |
|||
#define ASSERT(cond) |
|||
#define VERIFY(cond) |
|||
#define ABORT exit(255) |
|||
#define STRDUP(s) (s ? strdup(s) : NULL) |
|||
#define MALLOC(sz) malloc(sz) |
|||
#define FREE(p) if (p) free(p) |
|||
#endif |
|||
|
|||
/* Do you see a perl influence? :-) */ |
|||
#define DIE(fmt, arg...) \ |
|||
do { \ |
|||
fprintf(stderr, "Fatal error encountered at %s:%d\r\n\t", __FILE__, __LINE__); \ |
|||
fprintf(stderr, fmt, ##arg); \ |
|||
fprintf(stderr, "\r\n"); \ |
|||
ABORT; \ |
|||
} while (0); |
|||
|
|||
#define PERROR(ctx) \ |
|||
do { \ |
|||
fprintf(stderr, "I/O error at %s:%d\r\n\t", __FILE__, __LINE__); \ |
|||
fprintf(stderr, "%s: %s\r\n", ctx, strerror(errno)); \ |
|||
ABORT; \ |
|||
} while (0); |
|||
|
|||
#define NEW(S) (S *)MALLOC(sizeof(S)) |
|||
#define _(s) gettext(s) |
|||
|
|||
#endif |
@ -0,0 +1,11 @@ |
|||
XGETTEXT = xgettext --keyword=_ |
|||
|
|||
%.po: |
|||
$(XGETTEXT) ../*.c |
|||
|
|||
all: messages.po |
|||
touch build_stamp |
|||
|
|||
clean: |
|||
-rm -f messages.po build_stamp |
|||
|
@ -0,0 +1,25 @@ |
|||
CC = gcc |
|||
CFLAGS = -g -Wall -I.. |
|||
DEFS = -DVERSION=$(VERSION) -DPACKAGE=\"$(PROGRAM)\" -DLOCALEDIR=\"/usr/share/locale\" -DDEBUG |
|||
LIBS = -lslang |
|||
|
|||
COMPILE = $(CC) $(CFLAGS) $(DEFS) -c |
|||
LINK = $(CC) $(CFLAGS) $(DEFS) -o |
|||
|
|||
all: testdata flowtest |
|||
|
|||
../%.o: ../%.c |
|||
$(COMPILE) $< -o $@ |
|||
|
|||
%.o: %.c |
|||
$(COMPILE) $< |
|||
|
|||
testdata: ../data.o ../util.o testdata.o |
|||
$(LINK) testdata $^ |
|||
|
|||
flowtest: ../strutl.o ../util.o flowtest.o |
|||
$(LINK) flowtest $^ |
|||
|
|||
clean: |
|||
rm -f *.o *~ testdata flowtest |
|||
|
@ -0,0 +1,20 @@ |
|||
#include <stdio.h> |
|||
#include "strutl.h" |
|||
#include <stdlib.h> |
|||
|
|||
int main(int argc, char**argv) |
|||
{ |
|||
char *buf; |
|||
char *S1 = "99 bottles of beer on the wall, 99 bottles of beer, take one down, pass it around"; |
|||
char *S2 = "99bottlesofbeeronthewall,99bottlesofbeer,takeonedown,passitaround"; |
|||
|
|||
buf = reflowtext(30, S1); |
|||
printf("Original text:\n%s\n---\nReflowed text:\n%s\n\n", S1, buf); |
|||
free(buf); |
|||
|
|||
buf = reflowtext(30, S2); |
|||
printf("Original text:\n%s\n---\nReflowed text:\n%s\n\n", S2, buf); |
|||
free(buf); |
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,35 @@ |
|||
#include <stdio.h> |
|||
#include "data.h" |
|||
#include "util.h" |
|||
|
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
struct packages_t taskpkgs, pkgs; |
|||
struct package_t **taskpackages, *package; |
|||
char *pkgname; |
|||
int i, j; |
|||
|
|||
#ifdef DEBUG |
|||
atexit(memleak_check); |
|||
#endif |
|||
|
|||
packages_readlist(&taskpkgs, &pkgs); |
|||
taskpackages = packages_enumerate(&taskpkgs); |
|||
|
|||
for (i = 0; i < taskpkgs.count; i++) { |
|||
printf("Task package %d: %s\n", i+1, taskpackages[i]->name); |
|||
if (taskpackages[i]->dependscount > 0) { |
|||
printf("\tDepends:\n"); |
|||
for (j = 0; j < taskpackages[i]->dependscount; j++) |
|||
pkgname = taskpackages[i]->depends[j]; |
|||
package = packages_find(&pkgs, pkgname); |
|||
printf("\t\t%s: %s\n", pkgname, (package ? package->shortdesc : "(no description available)")); |
|||
} |
|||
printf("\tDescription: %s\n%s\n\n", |
|||
taskpackages[i]->shortdesc, taskpackages[i]->longdesc); |
|||
/* ... */ |
|||
} |
|||
|
|||
packages_free(&taskpkgs, &pkgs); |
|||
return 0; |
|||
} |
@ -0,0 +1,348 @@ |
|||
/* $Id: slangui.c,v 1.1 1999/11/21 22:01:04 tausq Exp $ */ |
|||
/* slangui.c - SLang user interface routines */ |
|||
/* TODO: the redraw code is a bit broken */ |
|||
#include "slangui.h" |
|||
#include <slang.h> |
|||
#include <libintl.h> |
|||
#include <string.h> |
|||
#include <ctype.h> |
|||
|
|||
#include "data.h" |
|||
#include "strutl.h" |
|||
#include "macros.h" |
|||
#include "help.h" |
|||
|
|||
/* Slang object number mapping */ |
|||
#define DEFAULTOBJ 0 |
|||
#define CHOOSEROBJ 1 |
|||
#define DESCOBJ 2 |
|||
#define STATUSOBJ 3 |
|||
#define DIALOGOBJ 4 |
|||
#define POINTEROBJ 5 |
|||
#define BUTTONOBJ 6 |
|||
|
|||
#define ROWS SLtt_Screen_Rows |
|||
#define COLUMNS SLtt_Screen_Cols |
|||
|
|||
/* Module private variables */ |
|||
static int _chooser_coloffset = 5; |
|||
static int _chooser_rowoffset = 3; |
|||
static int _chooser_height = 0; |
|||
static int _chooser_width = 0; |
|||
static int _chooser_topindex = 0; |
|||
static int _resized = 0; |
|||
static struct packages_t *_packages = NULL; |
|||
static struct packages_t *_taskpackages = NULL; |
|||
static struct package_t **_taskpackagesary = NULL; |
|||
|
|||
static void write_centered_str(int row, int coloffset, int width, char *txt) |
|||
{ |
|||
int col; |
|||
int len = strlen(txt); |
|||
|
|||
if (txt == NULL) return; |
|||
|
|||
SLsmg_fill_region(row, coloffset, 1, width, ' '); |
|||
|
|||
if (coloffset + len >= width) col = 0; |
|||
else col = (width - len) / 2; |
|||
|
|||
SLsmg_gotorc(row, coloffset + col); |
|||
SLsmg_write_nstring(txt, width); |
|||
} |
|||
|
|||
|
|||
void ui_init(int argc, char * const argv[], struct packages_t *taskpkgs, struct packages_t *pkgs) |
|||
{ |
|||
_taskpackages = taskpkgs; |
|||
_taskpackagesary = packages_enumerate(taskpkgs); |
|||
_packages = pkgs; |
|||
|
|||
SLang_set_abort_signal(NULL); |
|||
|
|||
/* assign attributes to objects */ |
|||
SLtt_set_color(DEFAULTOBJ, NULL, "white", "black"); |
|||
SLtt_set_color(CHOOSEROBJ, NULL, "black", "cyan"); |
|||
SLtt_set_color(POINTEROBJ, NULL, "brightblue", "cyan"); |
|||
SLtt_set_color(DESCOBJ, NULL, "black", "cyan"); |
|||
SLtt_set_color(STATUSOBJ, NULL, "yellow", "blue"); |
|||
SLtt_set_color(DIALOGOBJ, NULL, "black", "white"); |
|||
SLtt_set_color(BUTTONOBJ, NULL, "white", "red"); |
|||
|
|||
ui_resize(); |
|||
} |
|||
|
|||
int ui_shutdown(void) |
|||
{ |
|||
SLsmg_reset_smg(); |
|||
SLang_reset_tty(); |
|||
return 0; |
|||
} |
|||
|
|||
void ui_resize(void) |
|||
{ |
|||
/* SIGWINCH handler */ |
|||
if (-1 == SLang_init_tty(-1, 0, 1)) DIE(_("Unable to initialize the terminal")); |
|||
SLtt_get_terminfo(); |
|||
if (-1 == SLsmg_init_smg()) DIE(_("Unable to initialize screen output")); |
|||
if (-1 == SLkp_init()) DIE(_("Unable to initialize keyboard interface")); |
|||
|
|||
if (SLtt_Screen_Rows < 20 || SLtt_Screen_Cols < 70) { |
|||
DIE(_("Sorry, tasksel needs a terminal with at least 70 columns and 20 rows")); |
|||
} |
|||
|
|||
_chooser_height = ROWS - 2 * _chooser_rowoffset; |
|||
_chooser_width = COLUMNS - 2 * _chooser_coloffset; |
|||
ui_drawscreen(); |
|||
_resized = 1; |
|||
} |
|||
|
|||
int ui_eventloop(void) |
|||
{ |
|||
int done = 0; |
|||
int ret = 0; |
|||
int pos = 0; |
|||
int c, i; |
|||
|
|||
_chooser_topindex = 0; |
|||
ui_redrawcursor(0); |
|||
|
|||
while (!done) { |
|||
|
|||
c = SLkp_getkey(); |
|||
switch (c) { |
|||
case SL_KEY_UP: |
|||
case SL_KEY_LEFT: |
|||
if (pos > 0) pos--; else pos = _taskpackages->count - 1; |
|||
ui_redrawcursor(pos); |
|||
break; |
|||
|
|||
case SL_KEY_DOWN: |
|||
case SL_KEY_RIGHT: |
|||
if (pos < _taskpackages->count - 1) pos++; else pos = 0; |
|||
ui_redrawcursor(pos); |
|||
break; |
|||
|
|||
case SL_KEY_PPAGE: |
|||
pos -= _chooser_height; |
|||
if (pos < 0) pos = 0; |
|||
ui_redrawcursor(pos); |
|||
break; |
|||
|
|||
case SL_KEY_NPAGE: |
|||
pos += _chooser_height; |
|||
if (pos >= _taskpackages->count - 1) pos = _taskpackages->count - 1; |
|||
ui_redrawcursor(pos); |
|||
break; |
|||
|
|||
case SL_KEY_ENTER: case '\r': case '\n': |
|||
case ' ': ui_toggleselection(pos); break; |
|||
|
|||
case 'A': case 'a': |
|||
for (i = 0; i < _taskpackages->count; i++) _taskpackagesary[i]->selected = 1; |
|||
ui_drawscreen(); |
|||
break; |
|||
|
|||
case 'N': case 'n': |
|||
for (i = 0; i < _taskpackages->count; i++) _taskpackagesary[i]->selected = 0; |
|||
ui_drawscreen(); |
|||
break; |
|||
|
|||
case 'H': case 'h': case SL_KEY_F(1): ui_showhelp(); break; |
|||
case 'I': case 'i': ui_showpackageinfo(pos); break; |
|||
case 'Q': case 'q': done = 1; break; |
|||
} |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
int ui_drawbox(int obj, int r, int c, unsigned int dr, unsigned int dc) |
|||
{ |
|||
SLsmg_set_color(obj); |
|||
SLsmg_draw_box(r, c, dr, dc); |
|||
return 0; |
|||
} |
|||
|
|||
int ui_drawscreen(void) |
|||
{ |
|||
int i; |
|||
char buf[160]; |
|||
|
|||
SLsmg_cls(); |
|||
|
|||
/* Show version and status lines */ |
|||
SLsmg_set_color(STATUSOBJ); |
|||
|
|||
snprintf(buf, 160, "%s v%s - %s", |
|||
_(" Debian Installation Task Selector"), VERSION, |
|||
_("(c) 1999 SPI and others ")); |
|||
|
|||
write_centered_str(0, 0, COLUMNS, buf); |
|||
|
|||
write_centered_str(ROWS-1, 0, COLUMNS, |
|||
_(" h - Help SPACE - Toggle selection i - Task info q - Exit")); |
|||
|
|||
/* Draw the chooser screen */ |
|||
SLsmg_set_color(DEFAULTOBJ); |
|||
write_centered_str(1, 0, COLUMNS, |
|||
_("Select the task package(s) appropriate for your system:")); |
|||
ui_drawbox(CHOOSEROBJ, _chooser_rowoffset - 1, _chooser_coloffset - 1, _chooser_height + 2, _chooser_width + 2); |
|||
|
|||
for (i = 0; i < _taskpackages->count && i < _chooser_height; i++) |
|||
ui_drawchooseritem(i); |
|||
|
|||
ui_redrawcursor(0); |
|||
|
|||
SLsmg_refresh(); |
|||
_resized = 0; |
|||
return 0; |
|||
} |
|||
|
|||
void ui_button(int row, int col, char *txt) |
|||
{ |
|||
SLsmg_set_color(BUTTONOBJ); |
|||
SLsmg_gotorc(row, col); |
|||
SLsmg_write_char(' '); |
|||
SLsmg_write_string(txt); |
|||
SLsmg_write_char(' '); |
|||
} |
|||
|
|||
void ui_dialog(int row, int col, int height, int width, char *title, char *msg) |
|||
{ |
|||
char *reflowbuf; |
|||
int ri, c, pos; |
|||
char *line, *txt; |
|||
|
|||
reflowbuf = reflowtext(width - 4, msg); |
|||
|
|||
SLsmg_set_color(DIALOGOBJ); |
|||
|
|||
ui_drawbox(DIALOGOBJ, row, col, height, width); |
|||
SLsmg_fill_region(row+1, col+1, height-2, width-2, ' '); |
|||
|
|||
if (title) { |
|||
pos = col + (width - strlen(title))/2; |
|||
SLsmg_gotorc(row, pos); |
|||
SLsmg_write_char(' '); |
|||
SLsmg_write_string(title); |
|||
SLsmg_write_char(' '); |
|||
} |
|||
|
|||
if (reflowbuf != NULL) { |
|||
txt = reflowbuf; |
|||
ri = 0; |
|||
while ((line = strsep(&txt, "\n")) && (ri < height - 3)) { |
|||
SLsmg_gotorc(row + 1 + ri, col + 1); |
|||
SLsmg_write_nstring(line, width - 4); |
|||
ri++; |
|||
} |
|||
} |
|||
|
|||
ui_button(row+height-2, col+(width-4)/2, " OK "); |
|||
|
|||
SLsmg_refresh(); |
|||
do { |
|||
c = SLkp_getkey(); |
|||
} while (!(c == '\n' || c == '\r' || c == SL_KEY_ENTER || isspace(c)) && (_resized == 0)); |
|||
} |
|||
|
|||
void ui_drawchooseritem(int index) |
|||
{ |
|||
char buf[1024]; |
|||
ASSERT(_taskpackages != NULL); |
|||
if (index >= _taskpackages->count) DIE("Index out of bounds: %d >= %d", index, _taskpackages->count); |
|||
if (index < _chooser_topindex) return; |
|||
|
|||
SLsmg_set_color(CHOOSEROBJ); |
|||
SLsmg_gotorc(_chooser_rowoffset + index - _chooser_topindex, _chooser_coloffset); |
|||
|
|||
snprintf(buf, 1024, " (%c) %s: %s", |
|||
(_taskpackagesary[index]->selected == 0 ? ' ' : '*'), |
|||
_taskpackagesary[index]->name, _taskpackagesary[index]->shortdesc); |
|||
SLsmg_write_nstring(buf, _chooser_width); |
|||
} |
|||
|
|||
void ui_toggleselection(int index) |
|||
{ |
|||
ASSERT(_taskpackages != NULL); |
|||
if (index >= _taskpackages->count) DIE("Index out of bounds: %d >= %d", index, _taskpackages->count); |
|||
|
|||
if (_taskpackagesary[index]->selected == 0) |
|||
_taskpackagesary[index]->selected = 1; |
|||
else |
|||
_taskpackagesary[index]->selected = 0; |
|||
|
|||
SLsmg_set_color(CHOOSEROBJ); |
|||
SLsmg_gotorc(_chooser_rowoffset + index - _chooser_topindex, _chooser_coloffset + 3); |
|||
|
|||
if (_taskpackagesary[index]->selected == 0) |
|||
SLsmg_write_string("( )"); |
|||
else |
|||
SLsmg_write_string("(*)"); |
|||
SLsmg_refresh(); |
|||
} |
|||
|
|||
void ui_showhelp(void) |
|||
{ |
|||
ui_dialog(3, 3, ROWS-6, COLUMNS-6, "Help", HELPTXT); |
|||
ui_drawscreen(); |
|||
} |
|||
|
|||
void ui_redrawchooser(int index) |
|||
{ |
|||
int i; |
|||
for (i = _chooser_topindex; i < _chooser_topindex + _chooser_height; i++) |
|||
if (i < _taskpackages->count) ui_drawchooseritem(i); |
|||
} |
|||
|
|||
void ui_redrawcursor(int index) |
|||
{ |
|||
|
|||
/* Check to see if we have to scroll the selection */ |
|||
if (index + 1 - _chooser_height > _chooser_topindex) { |
|||
_chooser_topindex = index + 1 - _chooser_height; |
|||
ui_redrawchooser(index); |
|||
} else if (index < _chooser_topindex) { |
|||
_chooser_topindex = 0; |
|||
ui_redrawchooser(index); |
|||
} |
|||
|
|||
SLsmg_set_color(POINTEROBJ); |
|||
SLsmg_fill_region(_chooser_rowoffset, _chooser_coloffset, _chooser_height, 3, ' '); |
|||
SLsmg_gotorc(_chooser_rowoffset + index - _chooser_topindex, _chooser_coloffset); |
|||
SLsmg_write_string("->"); |
|||
SLsmg_refresh(); |
|||
} |
|||
|
|||
void ui_showpackageinfo(int index) |
|||
{ |
|||
struct package_t *pkg, *deppkg; |
|||
int i; |
|||
int width; |
|||
char buf[4096]; |
|||
char shortbuf[256]; |
|||
int bufleft; |
|||
|
|||
ASSERT(_taskpackages != NULL); |
|||
if (index >= _taskpackages->count) DIE("Index out of bounds: %d >= %d", index, _taskpackages->count); |
|||
|
|||
pkg = _taskpackagesary[index]; |
|||
|
|||
/* pack buf with package info */ |
|||
snprintf(buf, sizeof(buf), "Name: %s\nDescription:\n%s\n\nDependent packages:\n", pkg->name, pkg->longdesc); |
|||
bufleft = sizeof(buf) - strlen(buf) - 1; |
|||
|
|||
width = COLUMNS - 5; |
|||
ASSERT(width < sizeof(shortbuf)); |
|||
for (i = 0; i < pkg->dependscount; i++) { |
|||
deppkg = packages_find(_packages, pkg->depends[i]); |
|||
snprintf(shortbuf, width, "%s - %s\n", pkg->depends[i], |
|||
(deppkg && deppkg->shortdesc ? deppkg->shortdesc : "(no description available)")); |
|||
strncat(buf, shortbuf, bufleft); |
|||
bufleft = sizeof(buf) - strlen(buf) - 1; |
|||
} |
|||
|
|||
ui_dialog(2, 2, ROWS-4, COLUMNS-4, pkg->name, buf); |
|||
ui_drawscreen(); |
|||
} |
|||
|
@ -0,0 +1,24 @@ |
|||
/* $Id */ |
|||
#ifndef _SLANGUI_H |
|||
#define _SLANGUI_H |
|||
|
|||
struct packages_t; |
|||
|
|||
void ui_init(int argc, char * const argv[], struct packages_t *taskpkgs, struct packages_t *packages); |
|||
int ui_shutdown(void); |
|||
void ui_resize(void); |
|||
int ui_eventloop(void); |
|||
|
|||
int ui_drawbox(int obj, int x, int y, unsigned int dx, unsigned int dy); |
|||
void ui_button(int row, int col, char *txt); |
|||
void ui_dialog(int row, int col, int height, int width, char *title, char *msg); |
|||
|
|||
int ui_drawscreen(void); |
|||
void ui_drawchooseritem(int index); |
|||
void ui_toggleselection(int index); |
|||
void ui_showhelp(void); |
|||
void ui_showpackageinfo(int index); |
|||
void ui_redrawchooser(int index); |
|||
void ui_redrawcursor(int index); |
|||
|
|||
#endif |
@ -0,0 +1,57 @@ |
|||
/* $Id: strutl.c,v 1.1 1999/11/21 22:01:04 tausq Exp $ */ |
|||
#include "strutl.h" |
|||
|
|||
#include <string.h> |
|||
#include <stdlib.h> |
|||
|
|||
#include "macros.h" |
|||
|
|||
char *reflowtext(int width, char *txt) |
|||
{ |
|||
/* A simple greedy text formatting algorithm. Tries to put as many characters as possible
|
|||
* on a line without going over width |
|||
* |
|||
* Returns a malloc'ed string buffer that should be freed by the caller |
|||
*/ |
|||
char *buf; |
|||
char *begin, *end; |
|||
|
|||
if (txt == NULL) return NULL; |
|||
buf = MALLOC(strlen(txt) + strlen(txt) / width + 2); |
|||
buf[0] = 0; |
|||
|
|||
begin = txt; |
|||
while (*begin != 0) { |
|||
end = begin; |
|||
while (*end != 0 && *end != '\n' && end - begin < width) end++; |
|||
|
|||
if (end - begin < width) { |
|||
/* don't need to wrap -- saw a newline or EOS */ |
|||
if (*end == 0) { |
|||
strncat(buf, begin, end - begin); |
|||
break; |
|||
} else { |
|||
strncat(buf, begin, end - begin); |
|||
strcat(buf, "\n"); |
|||
begin = end + 1; |
|||
} |
|||
} else { |
|||
/* wrap the text */ |
|||
end--; |
|||
while (*end != ' ' && end > begin) end--; |
|||
if (end != begin) { |
|||
strncat(buf, begin, end - begin); |
|||
strcat(buf, "\n"); |
|||
begin = end + 1; |
|||
} else { |
|||
/* this is where it gets gross.. nowhere to break the line */ |
|||
end = begin + width - 1; |
|||
strncat(buf, begin, end - begin); |
|||
strcat(buf, "\n"); |
|||
begin = end; |
|||
} |
|||
} |
|||
} |
|||
return buf; |
|||
} |
|||
|
@ -0,0 +1,7 @@ |
|||
/* $Id: strutl.h,v 1.1 1999/11/21 22:01:04 tausq Rel $ */ |
|||
#ifndef _STRUTL_H |
|||
#define _STRUTL_H |
|||
|
|||
char *reflowtext(int width, char *txt); |
|||
|
|||
#endif |
@ -0,0 +1,92 @@ |
|||
/* $Id: tasksel.c,v 1.1 1999/11/21 22:01:04 tausq Exp $ */ |
|||
#include "tasksel.h" |
|||
|
|||
#include <string.h> |
|||
#include <signal.h> |
|||
#include <libintl.h> |
|||
#include <locale.h> |
|||
#include <stdlib.h> |
|||
#include <unistd.h> |
|||
|
|||
#include "slangui.h" |
|||
#include "data.h" |
|||
#include "macros.h" |
|||
|
|||
static void signalhandler(int sig) |
|||
{ |
|||
switch (sig) { |
|||
case SIGWINCH: |
|||
ui_resize(); |
|||
break; |
|||
default: |
|||
DPRINTF("%s\n", _("Unknown signal seen")); |
|||
} |
|||
|
|||
} |
|||
|
|||
void help(void) |
|||
{ |
|||
fprintf(stderr, "tasksel [-t]\n\t"); |
|||
fprintf(stderr, "%s\n\n", _("-t -- test mode; don't actually run apt-get on exit")); |
|||
exit(0); |
|||
} |
|||
|
|||
int main(int argc, char * const argv[]) |
|||
{ |
|||
int i, c, r, testmode = 0; |
|||
struct packages_t taskpkgs, packages; |
|||
struct package_t **pkglist; |
|||
char buf[2048]; |
|||
|
|||
signal(SIGWINCH, signalhandler); |
|||
|
|||
setlocale(LC_ALL, ""); |
|||
bindtextdomain(PACKAGE, LOCALEDIR); |
|||
textdomain(PACKAGE); |
|||
|
|||
while (1) { |
|||
c = getopt(argc, argv, "t"); |
|||
if (c == -1) break; |
|||
|
|||
switch (c) { |
|||
case 't': testmode = 1; break; |
|||
default: help(); |
|||
} |
|||
} |
|||
|
|||
packages_readlist(&taskpkgs, &packages); |
|||
ui_init(argc, argv, &taskpkgs, &packages); |
|||
ui_drawscreen(); |
|||
r = ui_eventloop(); |
|||
ui_shutdown(); |
|||
|
|||
pkglist = packages_enumerate(&taskpkgs); |
|||
|
|||
if (r == 0) { |
|||
sprintf(buf, "apt-get install "); |
|||
c = 0; |
|||
for (i = 0; i < taskpkgs.count; i++) { |
|||
if (pkglist[i]->selected > 0) { |
|||
/* TODO check buffer overflow; not likely, but still... */ |
|||
strcat(buf, pkglist[i]->name); |
|||
strcat(buf, " "); |
|||
c++; |
|||
} |
|||
} |
|||
|
|||
if (c > 0) { |
|||
if (testmode == 1) |
|||
printf("%s\n", buf); |
|||
else |
|||
system(buf); |
|||
} else { |
|||
fprintf(stderr, "No packages selected\n"); |
|||
r = 1; |
|||
} |
|||
} |
|||
|
|||
packages_free(&taskpkgs, &packages); |
|||
|
|||
return r; |
|||
} |
|||
|
@ -0,0 +1,7 @@ |
|||
/* $Id: tasksel.h,v 1.1 1999/11/21 22:01:04 tausq Exp $ */ |
|||
#ifndef _TASKSEL_H |
|||
#define _TASKSEL_H |
|||
|
|||
void help(void); |
|||
|
|||
#endif |
@ -0,0 +1,57 @@ |
|||
/* $Id: util.c,v 1.1 1999/11/21 22:01:04 tausq Exp $ */ |
|||
#include "util.h" |
|||
|
|||
#include <string.h> |
|||
#include <stdlib.h> |
|||
|
|||
#include "macros.h" |
|||
|
|||
#ifdef DEBUG |
|||
static int _num_mallocs = 0; |
|||
|
|||
char *safe_strdup(const char *s) |
|||
{ |
|||
char *p; |
|||
|
|||
if (s != NULL) { |
|||
p = strdup(s); |
|||
_num_mallocs++; |
|||
if (p == NULL) DIE("Cannot allocate memory for strdup"); |
|||
return p; |
|||
} else { |
|||
return NULL; |
|||
} |
|||
} |
|||
|
|||
void *safe_malloc(int size) |
|||
{ |
|||
void *p; |
|||
|
|||
if (size == 0) { |
|||
DPRINTF("Attempting to allocate 0 bytes!"); |
|||
return NULL; |
|||
} |
|||
|
|||
p = malloc(size); |
|||
_num_mallocs++; |
|||
if (p == NULL) DIE("Cannot allocate %d bytes of memory", size); |
|||
return p; |
|||
} |
|||
|
|||
void safe_free(void **p) |
|||
{ |
|||
if (p == NULL || *p == NULL) { |
|||
DPRINTF("Attempting to dereference NULL pointer"); |
|||
} else { |
|||
free(*p); |
|||
} |
|||
if (p) *p = NULL; |
|||
_num_mallocs--; |
|||
} |
|||
|
|||
void memleak_check(void) |
|||
{ |
|||
DPRINTF("Outstanding mallocs : %d\n", _num_mallocs); |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,10 @@ |
|||
/* $Id: util.h,v 1.1 1999/11/21 22:01:04 tausq Exp $ */ |
|||
#ifndef _UTIL_H |
|||
#define _UTIL_H |
|||
|
|||
char *safe_strdup(const char *); |
|||
void *safe_malloc(int); |
|||
void safe_free(void **); |
|||
void memleak_check(void); |
|||
|
|||
#endif |
Loading…
Reference in new issue