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.
 
 
 

638 lines
19 KiB

  1. /* $Id: data.c,v 1.18 2003/08/01 01:39:35 joeyh Rel $ */
  2. /* data.c - encapsulates functions for reading a package listing like dpkg's available file
  3. * Internally, packages are stored in a binary tree format to faciliate search operations
  4. */
  5. #include "data.h"
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <search.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11. #include <libintl.h>
  12. #include "slangui.h"
  13. #include "util.h"
  14. #include "macros.h"
  15. #define PACKAGEFIELD "Package: "
  16. #define TASKFIELD "Task: "
  17. #define KEYFIELD "Key:" /* multiline; no space necessary */
  18. #define RELEVANCEFIELD "Relevance: "
  19. #define DEPENDSFIELD "Depends: "
  20. #define RECOMMENDSFIELD "Recommends: "
  21. #define SUGGESTSFIELD "Suggests: "
  22. #define DESCRIPTIONFIELD "Description: "
  23. #define PRIORITYFIELD "Priority: "
  24. #define SECTIONFIELD "Section: "
  25. #define STATUSFIELD "Status: "
  26. #define AVAILABLEFILE "/var/lib/dpkg/available"
  27. #define STATUSFILE "/var/lib/dpkg/status"
  28. #define BUF_SIZE 1024
  29. #define MATCHFIELD(buf, s) (strncmp(buf, s, strlen(s)) == 0)
  30. #define FIELDDATA(buf, s) (buf + strlen(s))
  31. #define CHOMP(s) if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = 0
  32. /* module variables */
  33. static struct package_t **_packages_enumbuf = NULL;
  34. static int _packages_enumcount = 0;
  35. static void *_packages_root = NULL;
  36. static struct task_t **_tasks_enumbuf = NULL;
  37. static int _tasks_enumcount = 0;
  38. static void *_tasks_root = NULL;
  39. int _tasks_count;
  40. /* private functions */
  41. static int packagecompare(const void *p1, const void *p2)
  42. {
  43. /* compares two packages by name; used for binary tree routines */
  44. char *s1, *s2;
  45. s1 = ((struct package_t *)p1)->name;
  46. s2 = ((struct package_t *)p2)->name;
  47. return strcmp(s1, s2);
  48. }
  49. static void packages_walk_enumerate(const void *nodep, const VISIT order, const int depth)
  50. {
  51. /* adds nodep to list of nodes if we haven't visited this node before */
  52. struct package_t *datap = *(struct package_t **)nodep;
  53. if (order == leaf || order == postorder) {
  54. _packages_enumcount++;
  55. _packages_enumbuf[_packages_enumcount - 1] = datap;
  56. }
  57. }
  58. static void tasks_walk_enumerate(const void *nodep, const VISIT order, const int depth)
  59. {
  60. /* adds nodep to list of nodes if we haven't visited this node before */
  61. struct task_t *datap = *(struct task_t **)nodep;
  62. if (order == leaf || order == postorder) {
  63. _tasks_enumcount++;
  64. _tasks_enumbuf[_tasks_enumcount - 1] = datap;
  65. }
  66. }
  67. static int taskcompare(const void *lp, const void *rp)
  68. {
  69. const struct task_t *l = (const struct task_t *)lp;
  70. const struct task_t *r = (const struct task_t *)rp;
  71. return strcmp(l->name, r->name);
  72. }
  73. static void tasks_walk_delete(const void *nodep, const VISIT order, const int depth)
  74. {
  75. /* deletes memory associated with nodep */
  76. struct task_t *datap = *(struct task_t **)nodep;
  77. int i;
  78. if (order == leaf || order == endorder) {
  79. tdelete((void *)datap, &_tasks_root, taskcompare);
  80. if (datap->name) FREE(datap->name);
  81. if (datap->packages) {
  82. for (i = 0; i < datap->packagescount; i++) FREE(datap->packages[i]);
  83. FREE(datap->packages);
  84. }
  85. FREE(datap);
  86. }
  87. }
  88. static void tasks_walk_crop(const void *nodep, const VISIT order, const int depth)
  89. {
  90. struct task_t *datap = *(struct task_t **)nodep;
  91. if (order == leaf || order == endorder) {
  92. if (! datap->task_pkg || ! datap->task_pkg->shortdesc) {
  93. if (tdelete((void *)datap, &_tasks_root, taskcompare)) {
  94. _tasks_count--;
  95. }
  96. }
  97. }
  98. }
  99. static void packages_walk_delete(const void *nodep, const VISIT order, const int depth)
  100. {
  101. /* deletes memory associated with nodep */
  102. struct package_t *datap = *(struct package_t **)nodep;
  103. int i;
  104. if (order == leaf || order == endorder) {
  105. tdelete((void *)datap, &_packages_root, packagecompare);
  106. if (datap->name) FREE(datap->name);
  107. if (datap->shortdesc) FREE(datap->shortdesc);
  108. if (datap->longdesc) FREE(datap->longdesc);
  109. if (datap->depends) {
  110. for (i = 0; i < datap->dependscount; i++) FREE(datap->depends[i]);
  111. FREE(datap->depends);
  112. }
  113. if (datap->suggests) {
  114. for (i = 0; i < datap->suggestscount; i++) FREE(datap->suggests[i]);
  115. FREE(datap->suggests);
  116. }
  117. if (datap->recommends) {
  118. for (i = 0; i < datap->recommendscount; i++) FREE(datap->recommends[i]);
  119. FREE(datap->recommends);
  120. }
  121. FREE(datap);
  122. }
  123. }
  124. static int splitlinkdesc(const char *desc, char ***array)
  125. {
  126. /* given a comma separate list of names, returns an array with the names split into elts of the array */
  127. char *p;
  128. char *token;
  129. int i = 0, elts = 1;
  130. VERIFY(array != NULL);
  131. *array = NULL;
  132. if (desc == NULL) return 0;
  133. p = (char *)desc;
  134. while (*p != 0) if (*p++ == ',') elts++;
  135. *array = MALLOC(sizeof(char *) * elts);
  136. memset(*array, 0, sizeof(char *) * elts);
  137. p = (char *)desc;
  138. while ((token = strsep(&p, ","))) {
  139. while (isspace(*token)) token++;
  140. (*array)[i++] = STRDUP(token);
  141. }
  142. return elts;
  143. }
  144. void deletetask(struct tasks_t *tasks, const char *taskname) {
  145. struct task_t *task;
  146. task = tasks_find(tasks, taskname);
  147. if (! task)
  148. return;
  149. /* TODO: free structs */
  150. if (tdelete((void *)task, &tasks->tasks, taskcompare)) {
  151. tasks->count--;
  152. }
  153. }
  154. static struct task_t *addtask(
  155. struct tasks_t *tasks,
  156. const char *taskname,
  157. const char *package)
  158. {
  159. struct task_t t = {0}, *task;
  160. void *result;
  161. t.name = (char*)taskname;
  162. result = tfind(&t, &tasks->tasks, taskcompare);
  163. if (result) {
  164. task = *(struct task_t**)result;
  165. } else {
  166. task = NEW(struct task_t);
  167. task->name = STRDUP(taskname);
  168. task->task_pkg = NULL;
  169. task->packages = MALLOC(sizeof(char*)*10);
  170. task->packagesmax = 10;
  171. task->packagescount = 0;
  172. task->selected = 0;
  173. task->relevance = 5;
  174. tsearch(task, &tasks->tasks, taskcompare);
  175. tasks->count++;
  176. }
  177. {
  178. char const *pch;
  179. if (*package == '\0') return task;
  180. for (pch = package; *pch; pch++) {
  181. if ('a' <= *pch && *pch <= 'z') continue;
  182. if ('0' <= *pch && *pch <= '9') continue;
  183. if (*pch == '+' || *pch == '-' || *pch == '.') continue;
  184. return task;
  185. }
  186. }
  187. if (task->packagescount >= task->packagesmax) {
  188. ASSERT(task->packagescount == task->packagesmax);
  189. task->packagesmax *= 2;
  190. task->packages = REALLOC(task->packages, task->packagesmax * sizeof(char*));
  191. }
  192. task->packages[task->packagescount++] = STRDUP(package);
  193. return task;
  194. }
  195. static struct package_t *addpackage(
  196. struct packages_t *pkgs,
  197. const char *name, const char *dependsdesc,
  198. const char *recommendsdesc, const char *suggestsdesc,
  199. const char *shortdesc, const char *longdesc,
  200. const priority_t priority)
  201. {
  202. /* Adds package to the package list binary tree */
  203. struct package_t *node = NEW(struct package_t);
  204. void *p;
  205. VERIFY(name != NULL);
  206. /* DPRINTF("Adding package %s to list\n", name); */
  207. memset(node, 0, sizeof(struct package_t));
  208. node->name = STRDUP(name);
  209. node->shortdesc = STRDUP(shortdesc);
  210. node->longdesc = STRDUP(longdesc);
  211. node->priority = priority;
  212. node->section = NULL;
  213. node->pseudopackage = 0;
  214. if (dependsdesc) node->dependscount = splitlinkdesc(dependsdesc, &node->depends);
  215. if (recommendsdesc) node->recommendscount = splitlinkdesc(recommendsdesc, &node->recommends);
  216. if (suggestsdesc) node->suggestscount = splitlinkdesc(suggestsdesc, &node->suggests);
  217. p = tsearch(node, &pkgs->packages, packagecompare);
  218. VERIFY(p != NULL);
  219. if (*(struct package_t**)p == node) {
  220. pkgs->count++;
  221. } else {
  222. /* hmmm. this happens when there's a task- package and an entry in
  223. * the task file. what to do about it? I *think* what's happening is
  224. * the task- package is being replaced by the entry in the file, which
  225. * would mean **p is getting leaked right now. XXX
  226. */
  227. fprintf(stderr, "W: duplicate task info for %s\n", node->name);
  228. }
  229. return node;
  230. }
  231. /* public functions */
  232. struct package_t *packages_find(const struct packages_t *pkgs, const char *name)
  233. {
  234. /* Given a package name, returns a pointer to the appropriate package_t
  235. * structure or NULL if none is found */
  236. struct package_t pkg;
  237. void *result;
  238. pkg.name = (char *)name;
  239. result = tfind((void *)&pkg, &pkgs->packages, packagecompare);
  240. if (result == NULL)
  241. return NULL;
  242. else
  243. return *(struct package_t **)result;
  244. }
  245. struct task_t *tasks_find(const struct tasks_t *tasks, const char *name)
  246. {
  247. /* Given a task name, returns a pointer to the appropriate task_t
  248. * structure or NULL if none is found */
  249. struct task_t task;
  250. void *result;
  251. task.name = (char *)name;
  252. result = tfind((void *)&task, &tasks->tasks, taskcompare);
  253. if (result == NULL)
  254. return NULL;
  255. else
  256. return *(struct task_t **)result;
  257. }
  258. struct package_t **packages_enumerate(const struct packages_t *packages)
  259. {
  260. /* Converts a packages binary tree into an array */
  261. _packages_enumbuf = MALLOC(sizeof(struct package_t *) * packages->count);
  262. if (_packages_enumbuf == NULL)
  263. DIE(_("Cannot allocate memory for enumeration buffer"));
  264. memset(_packages_enumbuf, 0, sizeof(struct package_t *) * packages->count);
  265. _packages_enumcount = 0;
  266. twalk((void *)packages->packages, packages_walk_enumerate);
  267. ASSERT(_packages_enumcount == packages->count);
  268. return _packages_enumbuf;
  269. }
  270. struct task_t **tasks_enumerate(const struct tasks_t *tasks)
  271. {
  272. /* Converts the tasks binary tree into an array. */
  273. _tasks_enumbuf = MALLOC(sizeof(struct task_t *) * tasks->count);
  274. if (_tasks_enumbuf == NULL)
  275. DIE(_("Cannot allocate memory for enumeration buffer"));
  276. memset(_tasks_enumbuf, 0, sizeof(struct task_t *) * tasks->count);
  277. _tasks_enumcount = 0;
  278. twalk((void *)tasks->tasks, tasks_walk_enumerate);
  279. ASSERT(_tasks_enumcount == tasks->count);
  280. qsort(_tasks_enumbuf, tasks->count, sizeof(struct task_t *),
  281. taskcompare);
  282. return _tasks_enumbuf;
  283. }
  284. void tasks_crop(struct tasks_t *tasks) {
  285. _tasks_root = tasks->tasks;
  286. _tasks_count = tasks->count;
  287. twalk(tasks->tasks, tasks_walk_crop);
  288. tasks->count = _tasks_count;
  289. }
  290. static void walktasks(const void *t, const VISIT which, const int depth)
  291. {
  292. struct task_t *task = *(struct task_t**)t;
  293. int i;
  294. if (which == postorder || which == leaf) {
  295. fprintf(stderr, "Task %s [%s]:\n", task->name,
  296. (task->task_pkg ? task->task_pkg->section : "misc"));
  297. for (i = 0; i < task->packagescount; i++) {
  298. fprintf(stderr, " %s\n", task->packages[i]);
  299. }
  300. }
  301. }
  302. void taskfile_read(char *fn, struct tasks_t *tasks, struct packages_t *pkgs,
  303. unsigned char showempties)
  304. {
  305. /* Reads a task definition file, and populates internal data structures
  306. * with information about the tasks defined therein.
  307. *
  308. * The format of the task definition file is a series of stanzas,
  309. * seperated by blank lines. Each stanza is in rfc-822 format, and
  310. * contains fields named Task, Description (with extended desc), and
  311. * Section. (The information about what packages belong in a task is
  312. * contained in Task fields in the Packages file.) */
  313. FILE *f;
  314. char buf[BUF_SIZE];
  315. char *pkgname, *s;
  316. char *task, *shortdesc, *longdesc, *section;
  317. int relevance = 5;
  318. struct package_t *p;
  319. struct task_t *t;
  320. char *package;
  321. int key_missing;
  322. char *domainname, *l;
  323. f = fopen(fn, "r");
  324. if (f == NULL) PERROR(fn);
  325. /* Use a domain matching the task file that's being read, so translations
  326. * can be found. */
  327. domainname=strdup(fn);
  328. l = strrchr(domainname, '/');
  329. if (l)
  330. domainname=l;
  331. l = strrchr(domainname, '.');
  332. if (l)
  333. l[0] = '\0';
  334. while (!feof(f)) {
  335. fgets(buf, BUF_SIZE, f);
  336. CHOMP(buf);
  337. if (MATCHFIELD(buf, TASKFIELD)) {
  338. shortdesc = longdesc = section = NULL;
  339. task = STRDUP(FIELDDATA(buf, TASKFIELD));
  340. VERIFY(task != NULL);
  341. key_missing=0;
  342. while (!feof(f)) {
  343. fgets(buf, BUF_SIZE, f);
  344. dontmakemethink:
  345. /* after reading the Description:, we may actually have some more fields.
  346. * but the only way we might know this is if we've just read one of those
  347. * fields. to ensure we don't miss it, we immediately goto the label above
  348. * when we realise our mistake. a computer scientist would use lookahead
  349. * for this
  350. */
  351. CHOMP(buf);
  352. if (buf[0] == 0) break;
  353. if (MATCHFIELD(buf, SECTIONFIELD)) {
  354. section = STRDUP(FIELDDATA(buf, SECTIONFIELD));
  355. } else if (MATCHFIELD(buf, RELEVANCEFIELD)) {
  356. char *data = FIELDDATA(buf, RELEVANCEFIELD);
  357. if (strlen(data))
  358. relevance = atoi(data);
  359. } else if (MATCHFIELD(buf, DESCRIPTIONFIELD)) {
  360. shortdesc = STRDUP(dgettext(domainname, FIELDDATA(buf, DESCRIPTIONFIELD)));
  361. VERIFY(shortdesc != NULL);
  362. do {
  363. if (fgets(buf, BUF_SIZE, f) == 0)
  364. break;
  365. if (buf[0] != ' ') goto dontmakemethink;
  366. if (buf[1] == '.') buf[1] = ' ';
  367. if (longdesc == NULL) {
  368. longdesc = (char *)MALLOC(strlen(buf) + 1);
  369. strcpy(longdesc, buf + 1);
  370. } else {
  371. longdesc = realloc(longdesc, strlen(longdesc) + strlen(buf) + 1);
  372. strcat(longdesc, buf + 1);
  373. }
  374. } while (buf[0] != '\n' && !feof(f));
  375. longdesc = STRDUP(dgettext(domainname, longdesc));
  376. break;
  377. } else if (MATCHFIELD(buf, KEYFIELD)) {
  378. do {
  379. if (fgets(buf, BUF_SIZE, f) == 0)
  380. break;
  381. if (buf[0] != ' ') goto dontmakemethink;
  382. CHOMP(buf);
  383. pkgname=buf;
  384. while(pkgname[0] == ' ')
  385. pkgname++;
  386. s=pkgname+strlen(pkgname)-1;
  387. while(s[0] == ' ') {
  388. s[0]='\0';
  389. s--;
  390. }
  391. if (pkgname) {
  392. if (! packages_find(pkgs, pkgname)) {
  393. DPRINTF("task %s is missing key package %s", task, pkgname);
  394. key_missing=1;
  395. }
  396. }
  397. } while (buf[0] != '\n' && !feof(f));
  398. }
  399. }
  400. /* packages_readlist must be called before this function, so we can
  401. * tell if any packages are in this task, and ignore it if none are */
  402. if (showempties || (!key_missing && tasks_find(tasks, task))) {
  403. /* This is a fake package to go with the task. I add the task-
  404. * prefix to the package name to ensure that adding this fake
  405. * package stomps on the toes of no real package. */
  406. /* FIXME: It should not be necessary to do this; instead task_t
  407. * should include description and section fields and not need an
  408. * associated package. */
  409. package = MALLOC(strlen(task) + 6);
  410. package = MALLOC(6 + strlen(task));
  411. strcpy(package, "task-"); strcat(package, task);
  412. p = addpackage(pkgs, package, NULL, NULL, NULL, shortdesc, longdesc,
  413. PRIORITY_UNKNOWN);
  414. p->section = STRDUP(section);
  415. p->pseudopackage = 1;
  416. t = addtask(tasks, task, "");
  417. t->task_pkg = p;
  418. t->relevance = relevance;
  419. }
  420. else {
  421. DPRINTF("skipping empty task %s", task);
  422. deletetask(tasks, task);
  423. }
  424. if (task != NULL) FREE(task);
  425. if (shortdesc != NULL) FREE(shortdesc);
  426. if (longdesc != NULL) FREE(longdesc);
  427. if (section != NULL) FREE(section);
  428. }
  429. }
  430. fclose(f);
  431. }
  432. void packages_readlist(struct tasks_t *tasks, struct packages_t *pkgs)
  433. {
  434. /* Populates internal data structures with information from an available
  435. * file */
  436. FILE *f;
  437. char buf[BUF_SIZE];
  438. char *name, *shortdesc, *longdesc;
  439. char *dependsdesc, *recommendsdesc, *suggestsdesc, *prioritydesc, *taskdesc;
  440. char *section;
  441. priority_t priority;
  442. if ((f = fopen(AVAILABLEFILE, "r")) == NULL) PERROR(AVAILABLEFILE);
  443. while (!feof(f)) {
  444. fgets(buf, BUF_SIZE, f);
  445. CHOMP(buf);
  446. if (MATCHFIELD(buf, PACKAGEFIELD)) {
  447. /*DPRINTF("Package = %s\n", FIELDDATA(buf, PACKAGEFIELD)); */
  448. name = shortdesc = longdesc = taskdesc = dependsdesc = recommendsdesc = suggestsdesc = section = NULL;
  449. priority = PRIORITY_UNKNOWN;
  450. name = STRDUP(FIELDDATA(buf, PACKAGEFIELD));
  451. VERIFY(name != NULL);
  452. /* look for depends/suggests and shotdesc/longdesc */
  453. while (!feof(f)) {
  454. fgets(buf, BUF_SIZE, f);
  455. dontmakemethink:
  456. /* after reading the Description:, we make actually have some more fields.
  457. * but the only way we might know this is if we've just read one of those
  458. * fields. to ensure we don't miss it, we immediately goto the label above
  459. * when we realise our mistake. a computer scientist would use lookahead
  460. * for this
  461. */
  462. CHOMP(buf);
  463. if (buf[0] == 0) break;
  464. if (MATCHFIELD(buf, TASKFIELD)) {
  465. taskdesc = STRDUP(FIELDDATA(buf, TASKFIELD));
  466. VERIFY(taskdesc != NULL);
  467. } else if (MATCHFIELD(buf, DEPENDSFIELD)) {
  468. dependsdesc = STRDUP(FIELDDATA(buf, DEPENDSFIELD));
  469. VERIFY(dependsdesc != NULL);
  470. } else if (MATCHFIELD(buf, RECOMMENDSFIELD)) {
  471. recommendsdesc = STRDUP(FIELDDATA(buf, RECOMMENDSFIELD));
  472. VERIFY(recommendsdesc != NULL);
  473. } else if (MATCHFIELD(buf, SUGGESTSFIELD)) {
  474. suggestsdesc = STRDUP(FIELDDATA(buf, SUGGESTSFIELD));
  475. VERIFY(suggestsdesc != NULL);
  476. } else if (MATCHFIELD(buf, SECTIONFIELD)) {
  477. section = STRDUP(FIELDDATA(buf, SECTIONFIELD));
  478. } else if (MATCHFIELD(buf, PRIORITYFIELD)) {
  479. prioritydesc = FIELDDATA(buf, PRIORITYFIELD);
  480. if (strcmp(prioritydesc, "required") == 0) {
  481. priority = PRIORITY_REQUIRED;
  482. } else if (strcmp(prioritydesc, "important") == 0) {
  483. priority = PRIORITY_IMPORTANT;
  484. } else if (strcmp(prioritydesc, "standard") == 0) {
  485. priority = PRIORITY_STANDARD;
  486. } else if (strcmp(prioritydesc, "optional") == 0) {
  487. priority = PRIORITY_OPTIONAL;
  488. } else if (strcmp(prioritydesc, "extra") == 0) {
  489. priority = PRIORITY_EXTRA;
  490. }
  491. } else if (MATCHFIELD(buf, DESCRIPTIONFIELD)) {
  492. shortdesc = STRDUP(FIELDDATA(buf, DESCRIPTIONFIELD));
  493. VERIFY(shortdesc != NULL);
  494. do {
  495. if (fgets(buf, BUF_SIZE, f) == 0)
  496. break;
  497. if (buf[0] != ' ') goto dontmakemethink;
  498. if (buf[1] == '.') buf[1] = ' ';
  499. if (longdesc == NULL) {
  500. longdesc = (char *)MALLOC(strlen(buf) + 1);
  501. strcpy(longdesc, buf + 1);
  502. } else {
  503. longdesc = realloc(longdesc, strlen(longdesc) + strlen(buf) + 1);
  504. strcat(longdesc, buf + 1);
  505. }
  506. } while (buf[0] != '\n' && !feof(f));
  507. break;
  508. }
  509. }
  510. if (strncmp(name, "task-", 5) != 0) {
  511. addpackage(pkgs, name, NULL, NULL, NULL, shortdesc,
  512. NULL, priority);
  513. } else {
  514. struct package_t *p;
  515. struct task_t *t;
  516. int i;
  517. p = addpackage(pkgs, name, dependsdesc, recommendsdesc,
  518. suggestsdesc, shortdesc,
  519. longdesc, priority);
  520. #if 1
  521. if (strncmp(section, "tasks-", 6) == 0) {
  522. p->section = STRDUP(section+6);
  523. } else {
  524. p->section = "junk";
  525. }
  526. #else
  527. p->section = STRDUP(section);
  528. #endif
  529. t = addtask(tasks, name+5, "");
  530. t->task_pkg = p;
  531. for (i = 0; i < p->dependscount; i++) {
  532. addtask(tasks, name+5, p->depends[i]);
  533. }
  534. }
  535. if (taskdesc != NULL) {
  536. char **ts;
  537. int tscount;
  538. int i;
  539. tscount = splitlinkdesc(taskdesc, &ts);
  540. for (i = 0; i < tscount; i++) {
  541. addtask(tasks, ts[i], name);
  542. FREE(ts[i]);
  543. }
  544. FREE(ts);
  545. }
  546. if (name != NULL) FREE(name);
  547. if (dependsdesc != NULL) FREE(dependsdesc);
  548. if (recommendsdesc != NULL) FREE(recommendsdesc);
  549. if (suggestsdesc != NULL) FREE(suggestsdesc);
  550. if (taskdesc != NULL) FREE(taskdesc);
  551. if (shortdesc != NULL) FREE(shortdesc);
  552. if (longdesc != NULL) FREE(longdesc);
  553. if (section != NULL) FREE(section);
  554. }
  555. };
  556. fclose(f);
  557. /*twalk(tasks->tasks, walktasks);*/
  558. }
  559. void packages_free(struct tasks_t *tasks, struct packages_t *pkgs)
  560. {
  561. /* Frees up memory allocated by taskfile_read and packages_readlist */
  562. _tasks_root = tasks->tasks;
  563. twalk(tasks->tasks, tasks_walk_delete);
  564. _packages_root = pkgs->packages;
  565. twalk(pkgs->packages, packages_walk_delete);
  566. }