You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
538 lines
16 KiB
538 lines
16 KiB
/* $Id: data.c,v 1.11 2001/05/20 12:05:54 ajt 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 "slangui.h"
|
|
#include "util.h"
|
|
#include "macros.h"
|
|
|
|
#define PACKAGEFIELD "Package: "
|
|
#define TASKFIELD "Task: "
|
|
#define DEPENDSFIELD "Depends: "
|
|
#define RECOMMENDSFIELD "Recommends: "
|
|
#define SUGGESTSFIELD "Suggests: "
|
|
#define DESCRIPTIONFIELD "Description: "
|
|
#define PRIORITYFIELD "Priority: "
|
|
#define SECTIONFIELD "Section: "
|
|
#define STATUSFIELD "Status: "
|
|
#define AVAILABLEFILE "/var/lib/dpkg/available"
|
|
#define STATUSFILE "/var/lib/dpkg/status"
|
|
#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;
|
|
|
|
static struct task_t **_tasks_enumbuf = NULL;
|
|
static int _tasks_enumcount = 0;
|
|
static void *_tasks_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 == postorder) {
|
|
_packages_enumcount++;
|
|
_packages_enumbuf[_packages_enumcount - 1] = datap;
|
|
}
|
|
}
|
|
|
|
static void tasks_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 task_t *datap = *(struct task_t **)nodep;
|
|
if (order == leaf || order == postorder) {
|
|
_tasks_enumcount++;
|
|
_tasks_enumbuf[_tasks_enumcount - 1] = datap;
|
|
}
|
|
}
|
|
|
|
|
|
static int taskcompare(const struct task_t *l, const struct task_t *r)
|
|
{
|
|
return strcmp(l->name, r->name);
|
|
}
|
|
|
|
static void tasks_walk_delete(const void *nodep, const VISIT order, const int depth)
|
|
{
|
|
/* deletes memory associated with nodep */
|
|
struct task_t *datap = *(struct task_t **)nodep;
|
|
int i;
|
|
|
|
if (order == leaf || order == endorder) {
|
|
tdelete((void *)datap, &_tasks_root, taskcompare);
|
|
if (datap->name) FREE(datap->name);
|
|
if (datap->packages) {
|
|
for (i = 0; i < datap->packagescount; i++) FREE(datap->packages[i]);
|
|
FREE(datap->packages);
|
|
}
|
|
FREE(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);
|
|
}
|
|
}
|
|
|
|
static 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 struct task_t *addtask(
|
|
struct tasks_t *tasks,
|
|
const char *taskname,
|
|
const char *package)
|
|
{
|
|
struct task_t t = {0}, *task;
|
|
void *result;
|
|
|
|
t.name = (char*)taskname;
|
|
result = tfind(&t, &tasks->tasks, taskcompare);
|
|
if (result) {
|
|
task = *(struct task_t**)result;
|
|
} else {
|
|
task = NEW(struct task_t);
|
|
task->name = STRDUP(taskname);
|
|
task->task_pkg = NULL;
|
|
task->packages = MALLOC(sizeof(char*)*10);
|
|
task->packagesmax = 10;
|
|
task->packagescount = 0;
|
|
task->selected = 0;
|
|
tsearch(task, &tasks->tasks, taskcompare);
|
|
tasks->count++;
|
|
}
|
|
|
|
{
|
|
char const *pch;
|
|
if (*package == '\0') return task;
|
|
for (pch = package; *pch; pch++) {
|
|
if ('a' <= *pch && *pch <= 'z') continue;
|
|
if ('0' <= *pch && *pch <= '9') continue;
|
|
if (*pch == '+' || *pch == '-' || *pch == '.') continue;
|
|
return task;
|
|
}
|
|
}
|
|
|
|
if (task->packagescount >= task->packagesmax) {
|
|
ASSERT(task->packagescount == task->packagesmax);
|
|
task->packagesmax *= 2;
|
|
task->packages = REALLOC(task->packages, task->packagesmax * sizeof(char*));
|
|
}
|
|
task->packages[task->packagescount++] = STRDUP(package);
|
|
|
|
return task;
|
|
}
|
|
|
|
static struct package_t *addpackage(
|
|
struct packages_t *pkgs,
|
|
const char *name, const char *dependsdesc,
|
|
const char *recommendsdesc, const char *suggestsdesc,
|
|
const char *shortdesc, const char *longdesc,
|
|
const priority_t priority)
|
|
{
|
|
/* 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);
|
|
node->priority = priority;
|
|
node->section = NULL;
|
|
node->pseudopackage = 0;
|
|
|
|
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(node, &pkgs->packages, packagecompare);
|
|
VERIFY(p != NULL);
|
|
if (*(struct package_t**)p == node) {
|
|
pkgs->count++;
|
|
} else {
|
|
/* hmmm. this happens when there's a task- package and an entry in
|
|
* the task file. what to do about it? I *think* what's happening is
|
|
* the task- package is being replaced by the entry in the file, which
|
|
* would mean **p is getting leaked right now. XXX
|
|
*/
|
|
fprintf(stderr, "W: duplicate task info for %s\n", node->name);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/* 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 task_t *tasks_find(const struct tasks_t *tasks, const char *name)
|
|
{
|
|
/* Given a task name, returns a pointer to the appropriate task_t
|
|
* structure or NULL if none is found */
|
|
struct task_t task;
|
|
void *result;
|
|
|
|
task.name = (char *)name;
|
|
result = tfind((void *)&task, &tasks->tasks, taskcompare);
|
|
|
|
if (result == NULL)
|
|
return NULL;
|
|
else
|
|
return *(struct task_t **)result;
|
|
}
|
|
|
|
struct package_t **packages_enumerate(const struct packages_t *packages)
|
|
{
|
|
/* Converts a packages binary tree into an array */
|
|
|
|
_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;
|
|
}
|
|
|
|
struct task_t **tasks_enumerate(const struct tasks_t *tasks)
|
|
{
|
|
/* Converts the tasks binary tree into an array. */
|
|
|
|
_tasks_enumbuf = MALLOC(sizeof(struct task_t *) * tasks->count);
|
|
if (_tasks_enumbuf == NULL)
|
|
DIE("Cannot allocate memory for enumeration buffer");
|
|
memset(_tasks_enumbuf, 0, sizeof(struct task_t *) * tasks->count);
|
|
_tasks_enumcount = 0;
|
|
twalk((void *)tasks->tasks, tasks_walk_enumerate);
|
|
ASSERT(_tasks_enumcount == tasks->count);
|
|
qsort(_tasks_enumbuf, tasks->count, sizeof(struct task_t *),
|
|
taskcompare);
|
|
return _tasks_enumbuf;
|
|
}
|
|
|
|
static void walktasks(const void *t, const VISIT which, const int depth)
|
|
{
|
|
struct task_t *task = *(struct task_t**)t;
|
|
int i;
|
|
|
|
if (which == postorder || which == leaf) {
|
|
fprintf(stderr, "Task %s [%s]:\n", task->name,
|
|
(task->task_pkg ? task->task_pkg->section : "misc"));
|
|
for (i = 0; i < task->packagescount; i++) {
|
|
fprintf(stderr, " %s\n", task->packages[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void taskfile_read(char *fn, struct tasks_t *tasks, struct packages_t *pkgs)
|
|
{
|
|
/* Reads a task definition file, and populates internal data structures
|
|
* with information about the tasks defined therein.
|
|
*
|
|
* The format of the task definition file is a series of stanzas,
|
|
* seperated by blank lines. Each stanza is in rfc-822 format, and
|
|
* contains fields named Task, Description (with extended desc), and
|
|
* Section. (The information about what packages belong in a task is
|
|
* contained in Task fields in the Packages file.) */
|
|
FILE *f;
|
|
char buf[BUF_SIZE];
|
|
char *task, *shortdesc, *longdesc, *section;
|
|
|
|
f = fopen(fn, "r");
|
|
if (f == NULL) PERROR(fn);
|
|
|
|
while (!feof(f)) {
|
|
fgets(buf, BUF_SIZE, f);
|
|
CHOMP(buf);
|
|
if (MATCHFIELD(buf, TASKFIELD)) {
|
|
shortdesc = longdesc = section = NULL;
|
|
task = STRDUP(FIELDDATA(buf, TASKFIELD));
|
|
VERIFY(task != NULL);
|
|
|
|
while (!feof(f)) {
|
|
fgets(buf, BUF_SIZE, f);
|
|
CHOMP(buf);
|
|
if (buf[0] == 0) break;
|
|
|
|
if (MATCHFIELD(buf, SECTIONFIELD)) {
|
|
section = STRDUP(FIELDDATA(buf, SECTIONFIELD));
|
|
} else if (MATCHFIELD(buf, DESCRIPTIONFIELD)) {
|
|
shortdesc = STRDUP(FIELDDATA(buf, DESCRIPTIONFIELD));
|
|
VERIFY(shortdesc != NULL);
|
|
do {
|
|
if (fgets(buf, BUF_SIZE, f) == 0)
|
|
break;
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* packages_readlist must be called before this function, so we can
|
|
* tell if any packages are in this task, and ignore it if none are. */
|
|
if (tasks_find(tasks, task)) {
|
|
struct package_t *p;
|
|
struct task_t *t;
|
|
char *package;
|
|
|
|
/* This is a fake package to go with the task. I add the task-
|
|
* prefix to the package name to ensure that adding this fake
|
|
* package stomps on the toes of no real package. */
|
|
/* FIXME: It should not be necessary to do this; instead task_t
|
|
* should include description and section fields and not need an
|
|
* associated package. */
|
|
package = MALLOC(strlen(task) + 6);
|
|
package = MALLOC(6 + strlen(task));
|
|
strcpy(package, "task-"); strcat(package, task);
|
|
p = addpackage(pkgs, package, NULL, NULL, NULL, shortdesc, longdesc,
|
|
PRIORITY_UNKNOWN);
|
|
p->section = STRDUP(section);
|
|
p->pseudopackage = 1;
|
|
|
|
t = addtask(tasks, task, "");
|
|
t->task_pkg = p;
|
|
};
|
|
|
|
if (task != NULL) FREE(task);
|
|
if (shortdesc != NULL) FREE(shortdesc);
|
|
if (longdesc != NULL) FREE(longdesc);
|
|
if (section != NULL) FREE(section);
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
void packages_readlist(struct tasks_t *tasks, struct packages_t *pkgs)
|
|
{
|
|
/* Populates internal data structures with information from an available
|
|
* file */
|
|
FILE *f;
|
|
char buf[BUF_SIZE];
|
|
char *name, *shortdesc, *longdesc;
|
|
char *dependsdesc, *recommendsdesc, *suggestsdesc, *prioritydesc, *taskdesc;
|
|
char *section;
|
|
priority_t priority;
|
|
|
|
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 = taskdesc = dependsdesc = recommendsdesc = suggestsdesc = section = NULL;
|
|
priority = PRIORITY_UNKNOWN;
|
|
|
|
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, TASKFIELD)) {
|
|
taskdesc = STRDUP(FIELDDATA(buf, TASKFIELD));
|
|
VERIFY(taskdesc != NULL);
|
|
} else 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, SECTIONFIELD)) {
|
|
section = STRDUP(FIELDDATA(buf, SECTIONFIELD));
|
|
} else if (MATCHFIELD(buf, PRIORITYFIELD)) {
|
|
prioritydesc = FIELDDATA(buf, PRIORITYFIELD);
|
|
if (strcmp(prioritydesc, "required") == 0) {
|
|
priority = PRIORITY_REQUIRED;
|
|
} else if (strcmp(prioritydesc, "important") == 0) {
|
|
priority = PRIORITY_IMPORTANT;
|
|
} else if (strcmp(prioritydesc, "standard") == 0) {
|
|
priority = PRIORITY_STANDARD;
|
|
} else if (strcmp(prioritydesc, "optional") == 0) {
|
|
priority = PRIORITY_OPTIONAL;
|
|
} else if (strcmp(prioritydesc, "extra") == 0) {
|
|
priority = PRIORITY_EXTRA;
|
|
}
|
|
} else if (MATCHFIELD(buf, DESCRIPTIONFIELD)) {
|
|
shortdesc = STRDUP(FIELDDATA(buf, DESCRIPTIONFIELD));
|
|
VERIFY(shortdesc != NULL);
|
|
do {
|
|
if (fgets(buf, BUF_SIZE, f) == 0)
|
|
break;
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (strncmp(name, "task-", 5) != 0) {
|
|
addpackage(pkgs, name, NULL, NULL, NULL, shortdesc,
|
|
NULL, priority);
|
|
} else {
|
|
struct package_t *p;
|
|
struct task_t *t;
|
|
int i;
|
|
p = addpackage(pkgs, name, dependsdesc, recommendsdesc,
|
|
suggestsdesc, shortdesc,
|
|
longdesc, priority);
|
|
#if 1
|
|
if (strncmp(section, "tasks-", 6) == 0) {
|
|
p->section = STRDUP(section+6);
|
|
} else {
|
|
p->section = "junk";
|
|
}
|
|
#else
|
|
p->section = STRDUP(section);
|
|
#endif
|
|
t = addtask(tasks, name+5, "");
|
|
t->task_pkg = p;
|
|
for (i = 0; i < p->dependscount; i++) {
|
|
addtask(tasks, name+5, p->depends[i]);
|
|
}
|
|
}
|
|
|
|
if (taskdesc != NULL) {
|
|
char **ts;
|
|
int tscount;
|
|
int i;
|
|
|
|
tscount = splitlinkdesc(taskdesc, &ts);
|
|
for (i = 0; i < tscount; i++) {
|
|
addtask(tasks, ts[i], name);
|
|
FREE(ts[i]);
|
|
}
|
|
FREE(ts);
|
|
}
|
|
|
|
if (name != NULL) FREE(name);
|
|
if (dependsdesc != NULL) FREE(dependsdesc);
|
|
if (recommendsdesc != NULL) FREE(recommendsdesc);
|
|
if (suggestsdesc != NULL) FREE(suggestsdesc);
|
|
if (taskdesc != NULL) FREE(taskdesc);
|
|
if (shortdesc != NULL) FREE(shortdesc);
|
|
if (longdesc != NULL) FREE(longdesc);
|
|
if (section != NULL) FREE(section);
|
|
}
|
|
};
|
|
fclose(f);
|
|
|
|
/*twalk(tasks->tasks, walktasks);*/
|
|
}
|
|
|
|
void packages_free(struct tasks_t *tasks, struct packages_t *pkgs)
|
|
{
|
|
/* Frees up memory allocated by taskfile_read and packages_readlist */
|
|
|
|
_tasks_root = tasks->tasks;
|
|
twalk(tasks->tasks, tasks_walk_delete);
|
|
|
|
_packages_root = pkgs->packages;
|
|
twalk(pkgs->packages, packages_walk_delete);
|
|
}
|
|
|
|
|