filesdb.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. /*
  2. * dpkg - main program for package management
  3. * filesdb.c - management of database of files installed on system
  4. *
  5. * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
  6. *
  7. * This is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as
  9. * published by the Free Software Foundation; either version 2,
  10. * or (at your option) any later version.
  11. *
  12. * This is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public
  18. * License along with dpkg; if not, write to the Free Software
  19. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. */
  21. #include <assert.h>
  22. #include <unistd.h>
  23. #include <string.h>
  24. #include <errno.h>
  25. #include <sys/stat.h>
  26. #include "config.h"
  27. #include "dpkg.h"
  28. #include "dpkg-db.h"
  29. #include "filesdb.h"
  30. #include "main.h"
  31. /*** Generic data structures and routines ***/
  32. static int allpackagesdone= 0;
  33. static int nfiles= 0;
  34. static struct diversion *diversions= 0;
  35. static FILE *diversionsfile= 0;
  36. void note_must_reread_files_inpackage(struct pkginfo *pkg) {
  37. allpackagesdone= 0;
  38. ensure_package_clientdata(pkg);
  39. pkg->clientdata->fileslistvalid= 0;
  40. }
  41. static int saidread=0;
  42. void ensure_packagefiles_available(struct pkginfo *pkg) {
  43. static struct varbuf fnvb;
  44. static char stdiobuf[8192];
  45. FILE *file;
  46. const char *filelistfile;
  47. struct fileinlist **lendp, *newent, *current;
  48. struct filepackages *packageslump;
  49. int search, findlast, putat, l;
  50. char *thefilename;
  51. char linebuf[1024];
  52. if (pkg->clientdata && pkg->clientdata->fileslistvalid) return;
  53. ensure_package_clientdata(pkg);
  54. /* Throw away any the stale data, if there was any. */
  55. for (current= pkg->clientdata->files;
  56. current;
  57. current= current->next) {
  58. /* For each file that used to be in the package,
  59. * go through looking for this package's entry in the list
  60. * of packages containing this file, and blank it out.
  61. */
  62. for (packageslump= current->namenode->packages;
  63. packageslump;
  64. packageslump= packageslump->more)
  65. for (search= 0;
  66. search < PERFILEPACKAGESLUMP && packageslump->pkgs[search];
  67. search++)
  68. if (packageslump->pkgs[search] == pkg) {
  69. /* Hah! Found it. */
  70. for (findlast= search+1;
  71. findlast < PERFILEPACKAGESLUMP && packageslump->pkgs[findlast];
  72. findlast++);
  73. findlast--;
  74. /* findlast is now the last occupied entry, which may be the same as
  75. * search. We blank out the entry for this package. We also
  76. * have to copy the last entry into the empty slot, because
  77. * the list is null-pointer-terminated.
  78. */
  79. packageslump->pkgs[search]= packageslump->pkgs[findlast];
  80. packageslump->pkgs[findlast]= 0;
  81. /* This may result in an empty link in the list. This is OK. */
  82. goto xit_search_to_delete_from_perfilenodelist;
  83. }
  84. xit_search_to_delete_from_perfilenodelist:
  85. ;
  86. /* The actual filelist links were allocated using nfmalloc, so
  87. * we shouldn't free them.
  88. */
  89. }
  90. pkg->clientdata->files= 0;
  91. /* Packages which aren't installed don't have a files list. */
  92. if (pkg->status == stat_notinstalled) {
  93. pkg->clientdata->fileslistvalid= 1; return;
  94. }
  95. filelistfile= pkgadminfile(pkg,LISTFILE);
  96. onerr_abort++;
  97. file= fopen(filelistfile,"r");
  98. if (!file) {
  99. if (errno != ENOENT)
  100. ohshite("unable to open files list file for package `%.250s'",pkg->name);
  101. onerr_abort--;
  102. if (pkg->status != stat_configfiles) {
  103. if (saidread == 1) putc('\n',stderr);
  104. fprintf(stderr,
  105. DPKG ": serious warning: files list file for package `%.250s' missing,"
  106. " assuming package has no files currently installed.\n", pkg->name);
  107. }
  108. pkg->clientdata->files= 0;
  109. pkg->clientdata->fileslistvalid= 1;
  110. return;
  111. }
  112. push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)file);
  113. if (setvbuf(file,stdiobuf,_IOFBF,sizeof(stdiobuf)))
  114. ohshite("unable to set buffering on `%.250s'",filelistfile);
  115. lendp= &pkg->clientdata->files;
  116. varbufreset(&fnvb);
  117. while (fgets(linebuf,sizeof(linebuf),file)) {
  118. /* This is a very important loop, and it is therefore rather messy.
  119. * We break the varbuf abstraction even more than usual, and we
  120. * avoid copying where possible.
  121. */
  122. l= strlen(linebuf);
  123. if (l == 0) ohshit("fgets gave an empty null-terminated string from `%.250s'",
  124. filelistfile);
  125. l--;
  126. if (linebuf[l] != '\n') {
  127. varbufaddstr(&fnvb,linebuf);
  128. continue;
  129. } else if (!fnvb.used && l>0 && linebuf[l-1] != '/') { /* fast path */
  130. linebuf[l]= 0;
  131. thefilename= linebuf;
  132. } else {
  133. if (l>0 && linebuf[l-1] == '/') l--; /* strip trailing slashes */
  134. linebuf[l]= 0;
  135. varbufaddstr(&fnvb,linebuf);
  136. varbufaddc(&fnvb,0);
  137. fnvb.used= 0;
  138. thefilename= fnvb.buf;
  139. }
  140. if (!*thefilename)
  141. ohshit("files list file for package `%.250s' contains empty filename",pkg->name);
  142. newent= nfmalloc(sizeof(struct fileinlist));
  143. newent->namenode= findnamenode(thefilename);
  144. newent->next= 0;
  145. *lendp= newent;
  146. lendp= &newent->next;
  147. }
  148. if (ferror(file))
  149. ohshite("error reading files list file for package `%.250s'",pkg->name);
  150. pop_cleanup(ehflag_normaltidy); /* file= fopen() */
  151. if (fclose(file))
  152. ohshite("error closing files list file for package `%.250s'",pkg->name);
  153. if (fnvb.used)
  154. ohshit("files list file for package `%.250s' is truncated",pkg->name);
  155. onerr_abort--;
  156. for (newent= pkg->clientdata->files; newent; newent= newent->next) {
  157. packageslump= newent->namenode->packages;
  158. if (packageslump) {
  159. for (putat= 0;
  160. putat < PERFILEPACKAGESLUMP && packageslump->pkgs[putat];
  161. putat++);
  162. if (putat >= PERFILEPACKAGESLUMP) packageslump= 0;
  163. }
  164. if (!packageslump) {
  165. packageslump= nfmalloc(sizeof(struct filepackages));
  166. packageslump->more= newent->namenode->packages;
  167. newent->namenode->packages= packageslump;
  168. putat= 0;
  169. }
  170. packageslump->pkgs[putat]= pkg;
  171. if (++putat < PERFILEPACKAGESLUMP) packageslump->pkgs[putat]= 0;
  172. }
  173. pkg->clientdata->fileslistvalid= 1;
  174. }
  175. void ensure_allinstfiles_available(void) {
  176. struct pkgiterator *it;
  177. struct pkginfo *pkg;
  178. if (allpackagesdone) return;
  179. if (saidread<2) {
  180. saidread=1;
  181. printf(f_largemem>0 ? "(Reading database ... " : "(Scanning database ... ");
  182. }
  183. it= iterpkgstart();
  184. while ((pkg= iterpkgnext(it)) != 0) ensure_packagefiles_available(pkg);
  185. iterpkgend(it);
  186. allpackagesdone= 1;
  187. if (saidread==1) {
  188. printf("%d files and directories currently installed.)\n",nfiles);
  189. saidread=2;
  190. }
  191. }
  192. void ensure_allinstfiles_available_quiet(void) {
  193. saidread=2;
  194. ensure_allinstfiles_available();
  195. }
  196. void write_filelist_except(struct pkginfo *pkg, struct fileinlist *list, int leaveout) {
  197. /* If leaveout is nonzero, will not write any file whose filenamenode
  198. * has the fnnf_elide_other_lists flag set.
  199. */
  200. static struct varbuf vb, newvb;
  201. FILE *file;
  202. varbufreset(&vb);
  203. varbufaddstr(&vb,admindir);
  204. varbufaddstr(&vb,"/" INFODIR);
  205. varbufaddstr(&vb,pkg->name);
  206. varbufaddstr(&vb,"." LISTFILE);
  207. varbufaddc(&vb,0);
  208. varbufreset(&newvb);
  209. varbufaddstr(&newvb,vb.buf);
  210. varbufaddstr(&newvb,NEWDBEXT);
  211. varbufaddc(&newvb,0);
  212. file= fopen(newvb.buf,"w+");
  213. if (!file)
  214. ohshite("unable to create updated files list file for package %s",pkg->name);
  215. push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)file);
  216. while (list) {
  217. if (!(leaveout && (list->namenode->flags & fnnf_elide_other_lists))) {
  218. fputs(list->namenode->name,file);
  219. putc('\n',file);
  220. }
  221. list= list->next;
  222. }
  223. if (ferror(file))
  224. ohshite("failed to write to updated files list file for package %s",pkg->name);
  225. if (fflush(file))
  226. ohshite("failed to flush updated files list file for package %s",pkg->name);
  227. if (fsync(fileno(file)))
  228. ohshite("failed to sync updated files list file for package %s",pkg->name);
  229. pop_cleanup(ehflag_normaltidy); /* file= fopen() */
  230. if (fclose(file))
  231. ohshite("failed to close updated files list file for package %s",pkg->name);
  232. if (rename(newvb.buf,vb.buf))
  233. ohshite("failed to install updated files list file for package %s",pkg->name);
  234. note_must_reread_files_inpackage(pkg);
  235. }
  236. void reversefilelist_init(struct reversefilelistiter *iterptr,
  237. struct fileinlist *files) {
  238. /* Initialises an iterator that appears to go through the file
  239. * list `files' in reverse order, returning the namenode from
  240. * each. What actually happens is that we walk the list here,
  241. * building up a reverse list, and then peel it apart one
  242. * entry at a time.
  243. */
  244. struct fileinlist *newent;
  245. iterptr->todo= 0;
  246. while (files) {
  247. newent= m_malloc(sizeof(struct fileinlist));
  248. newent->namenode= files->namenode;
  249. newent->next= iterptr->todo;
  250. iterptr->todo= newent;
  251. files= files->next;
  252. }
  253. }
  254. struct filenamenode *reversefilelist_next(struct reversefilelistiter *iterptr) {
  255. struct filenamenode *ret;
  256. struct fileinlist *todo;
  257. todo= iterptr->todo;
  258. if (!todo) return 0;
  259. ret= todo->namenode;
  260. iterptr->todo= todo->next;
  261. free(todo);
  262. return ret;
  263. }
  264. void reversefilelist_abort(struct reversefilelistiter *iterptr) {
  265. /* Clients must call this function to clean up the reversefilelistiter
  266. * if they wish to break out of the iteration before it is all done.
  267. * Calling this function is not necessary if reversefilelist_next has
  268. * been called until it returned 0.
  269. */
  270. while (reversefilelist_next(iterptr));
  271. }
  272. void ensure_diversions(void) {
  273. static struct varbuf vb;
  274. struct stat stab1, stab2;
  275. char linebuf[MAXDIVERTFILENAME];
  276. FILE *file;
  277. struct diversion *ov, *oicontest, *oialtname;
  278. int l;
  279. varbufreset(&vb);
  280. varbufaddstr(&vb,admindir);
  281. varbufaddstr(&vb,"/" DIVERSIONSFILE);
  282. varbufaddc(&vb,0);
  283. onerr_abort++;
  284. file= fopen(vb.buf,"r");
  285. if (!file) {
  286. if (errno != ENOENT) ohshite("failed to open diversions file");
  287. if (!diversionsfile) { onerr_abort--; return; }
  288. } else if (diversionsfile) {
  289. if (fstat(fileno(diversionsfile),&stab1))
  290. ohshite("failed to fstat previous diversions file");
  291. if (fstat(fileno(file),&stab2))
  292. ohshite("failed to fstat diversions file");
  293. if (stab1.st_dev == stab2.st_dev && stab1.st_ino == stab2.st_ino) {
  294. fclose(file); onerr_abort--; return;
  295. }
  296. }
  297. if (diversionsfile) fclose(diversionsfile);
  298. diversionsfile= file;
  299. for (ov= diversions; ov; ov= ov->next) {
  300. ov->useinstead->divert->camefrom->divert= 0;
  301. ov->useinstead->divert= 0;
  302. }
  303. diversions= 0;
  304. if (!file) { onerr_abort--; return; }
  305. while (fgets(linebuf,sizeof(linebuf),file)) {
  306. oicontest= nfmalloc(sizeof(struct diversion));
  307. oialtname= nfmalloc(sizeof(struct diversion));
  308. l= strlen(linebuf);
  309. if (l == 0) ohshit("fgets gave an empty string from diversions [i]");
  310. if (linebuf[--l] != '\n') ohshit("diversions file has too-long line or EOF [i]");
  311. linebuf[l]= 0;
  312. oialtname->camefrom= findnamenode(linebuf);
  313. if (!fgets(linebuf,sizeof(linebuf),file))
  314. if (ferror(file)) ohshite("read error in diversions [ii]");
  315. else ohshit("unexpected EOF in diversions [ii]");
  316. l= strlen(linebuf);
  317. if (l == 0) ohshit("fgets gave an empty string from diversions [ii]");
  318. if (linebuf[--l] != '\n') ohshit("diversions file has too-long line or EOF [ii]");
  319. linebuf[l]= 0;
  320. oicontest->useinstead= findnamenode(linebuf);
  321. if (!fgets(linebuf,sizeof(linebuf),file))
  322. if (ferror(file)) ohshite("read error in diversions [iii]");
  323. else ohshit("unexpected EOF in diversions [iii]");
  324. l= strlen(linebuf);
  325. if (l == 0) ohshit("fgets gave an empty string from diversions [iii]");
  326. if (linebuf[--l] != '\n') ohshit("diversions file has too-long line or EOF [ii]");
  327. linebuf[l]= 0;
  328. oicontest->pkg= oialtname->pkg=
  329. strcmp(linebuf,":") ? findpackage(linebuf) : 0;
  330. if (oialtname->camefrom->divert || oicontest->useinstead->divert)
  331. ohshit("conflicting diversions involving `%.250s' or `%.250s'",
  332. oialtname->camefrom->name, oicontest->useinstead->name);
  333. oialtname->camefrom->divert= oicontest;
  334. oicontest->useinstead->divert= oialtname;
  335. oicontest->next= diversions;
  336. diversions= oicontest;
  337. }
  338. if (ferror(file)) ohshite("read error in diversions [i]");
  339. diversionsfile= file;
  340. onerr_abort--;
  341. }
  342. /*** Data structures common to both in-core databases ***/
  343. struct fileiterator {
  344. union {
  345. struct {
  346. struct filenamenode *current;
  347. } low;
  348. struct {
  349. struct filenamenode *namenode;
  350. int nbinn;
  351. } high;
  352. } u;
  353. };
  354. /*** Data structures for fast, large memory usage database ***/
  355. #define BINS (1 << 13)
  356. /* This must always be a power of two. If you change it
  357. * consider changing the per-character hashing factor (currently
  358. * 1785 = 137*13) too.
  359. */
  360. static struct filenamenode *bins[BINS];
  361. /*** Data structures for low-memory-footprint in-core files database ***/
  362. struct fdirents {
  363. struct fdirents *more;
  364. struct { const char *component; struct fdirnode *go; } entries[1];
  365. /* first one has one entry, then two, then four, &c */
  366. };
  367. struct fdirnode {
  368. struct filenamenode *here;
  369. struct fdirents *contents;
  370. };
  371. static struct fdirnode fdirroot;
  372. static struct filenamenode *allfiles;
  373. struct filenamesblock {
  374. struct filenamesblock *next;
  375. char *data;
  376. };
  377. static struct filenamesblock *namesarea= 0;
  378. static int namesarealeft= 0;
  379. /*** Code for both. This is rather hacky, sorry ... ***/
  380. struct fileiterator *iterfilestart(void) {
  381. struct fileiterator *i;
  382. i= m_malloc(sizeof(struct fileiterator));
  383. switch (f_largemem) {
  384. case 1:
  385. i->u.high.namenode= 0;
  386. i->u.high.nbinn= 0;
  387. break;
  388. case -1:
  389. i->u.low.current= allfiles;
  390. break;
  391. default:
  392. internerr("iterfilestart no f_largemem");
  393. }
  394. return i;
  395. }
  396. struct filenamenode *iterfilenext(struct fileiterator *i) {
  397. struct filenamenode *r;
  398. switch (f_largemem) {
  399. case 1:
  400. while (!i->u.high.namenode) {
  401. if (i->u.high.nbinn >= BINS) return 0;
  402. i->u.high.namenode= bins[i->u.high.nbinn++];
  403. }
  404. r= i->u.high.namenode;
  405. i->u.high.namenode= r->next;
  406. break;
  407. case -1:
  408. if (!i->u.low.current) return 0;
  409. r= i->u.low.current;
  410. i->u.low.current= i->u.low.current->next;
  411. break;
  412. default:
  413. internerr("iterfilenext no f_largemem");
  414. }
  415. return r;
  416. }
  417. void iterfileend(struct fileiterator *i) {
  418. free(i);
  419. }
  420. void filesdbinit(void) {
  421. struct filenamenode *fnn;
  422. struct sysinfo info;
  423. int i;
  424. if (!f_largemem) {
  425. f_largemem= -1;
  426. if (!sysinfo(&info)) {
  427. if (info.freeram + (info.sharedram>>2) + (info.bufferram>>2) >= 4096*1024 ||
  428. info.totalram >= 6144)
  429. f_largemem= 1;
  430. }
  431. }
  432. switch (f_largemem) {
  433. case 1:
  434. for (i=0; i<BINS; i++)
  435. for (fnn= bins[i]; fnn; fnn= fnn->next) {
  436. fnn->flags= 0;
  437. fnn->oldhash= 0;
  438. }
  439. break;
  440. case -1:
  441. for (fnn= allfiles;
  442. fnn;
  443. fnn= fnn->next) {
  444. fnn->flags= 0;
  445. fnn->oldhash= 0;
  446. }
  447. break;
  448. default:
  449. internerr("filesdbinit no f_largemem");
  450. }
  451. }
  452. static struct filenamenode *findnamenode_high(const char *name);
  453. static struct filenamenode *findnamenode_low(const char *name);
  454. struct filenamenode *findnamenode(const char *name) {
  455. switch (f_largemem) {
  456. case 1:
  457. return findnamenode_high(name);
  458. case -1:
  459. return findnamenode_low(name);
  460. default:
  461. internerr("findnamenode no f_largemem");
  462. }
  463. }
  464. /*** Code for low-memory-footprint in-core files database ***/
  465. static struct filenamenode *findnamenode_low(const char *name) {
  466. struct fdirnode *traverse;
  467. struct fdirents *ents, **addto;
  468. const char *nameleft, *slash;
  469. char *p;
  470. struct filenamesblock *newblock;
  471. int n, i, nentrieshere, alloc;
  472. /* We skip initial slashes and ./ pairs, and add our own single leading slash. */
  473. name= skip_slash_dotslash(name);
  474. nameleft= name;
  475. traverse= &fdirroot;
  476. while (nameleft) {
  477. slash= strchr(nameleft,'/');
  478. if (slash) {
  479. n= (int)(slash-nameleft); slash++;
  480. } else {
  481. n= strlen(nameleft);
  482. }
  483. ents= traverse->contents; addto= &traverse->contents; i= 0; nentrieshere= 1;
  484. for (;;) {
  485. if (!ents) break;
  486. if (!ents->entries[i].component) break;
  487. if (!strncmp(ents->entries[i].component,nameleft,n) &&
  488. !ents->entries[i].component[n]) {
  489. break;
  490. }
  491. i++;
  492. if (i < nentrieshere) continue;
  493. addto= &ents->more;
  494. ents= ents->more;
  495. nentrieshere += nentrieshere;
  496. i=0;
  497. }
  498. if (!ents) {
  499. ents= nfmalloc(sizeof(struct fdirents) +
  500. sizeof(ents->entries[0])*(nentrieshere-1));
  501. i=0;
  502. ents->entries[i].component= 0;
  503. ents->more= 0;
  504. *addto= ents;
  505. }
  506. if (!ents->entries[i].component) {
  507. p= nfmalloc(n+1);
  508. memcpy(p,nameleft,n); p[n]= 0;
  509. ents->entries[i].component= p;
  510. ents->entries[i].go= nfmalloc(sizeof(struct fdirnode));
  511. ents->entries[i].go->here= 0;
  512. ents->entries[i].go->contents= 0;
  513. if (i+1 < nentrieshere)
  514. ents->entries[i+1].component= 0;
  515. }
  516. traverse= ents->entries[i].go;
  517. nameleft= slash;
  518. }
  519. if (traverse->here) return traverse->here;
  520. traverse->here= nfmalloc(sizeof(struct filenamenode));
  521. traverse->here->packages= 0;
  522. traverse->here->flags= 0;
  523. traverse->here->divert= 0;
  524. n= strlen(name)+2;
  525. if (namesarealeft < n) {
  526. newblock= m_malloc(sizeof(struct filenamesblock));
  527. alloc= 256*1024;
  528. if (alloc<n) alloc= n;
  529. newblock->data= m_malloc(alloc);
  530. newblock->next= namesarea;
  531. namesarea= newblock;
  532. namesarealeft= alloc;
  533. }
  534. namesarealeft-= n;
  535. p= namesarea->data+namesarealeft;
  536. traverse->here->name= p; *p++= '/'; strcpy(p,name);
  537. traverse->here->next= allfiles;
  538. allfiles= traverse->here;
  539. nfiles++;
  540. return traverse->here;
  541. }
  542. /*** Code for high memory usage fast database ***/
  543. static int hash(const char *name) {
  544. int v= 0;
  545. while (*name) { v *= 1785; v += *name; name++; }
  546. return v;
  547. }
  548. struct filenamenode *findnamenode_high(const char *name) {
  549. struct filenamenode **pointerp, *newnode;
  550. /* We skip initial slashes and ./ pairs, and add our own single leading slash. */
  551. name= skip_slash_dotslash(name);
  552. pointerp= bins + (hash(name) & (BINS-1));
  553. while (*pointerp) {
  554. assert((*pointerp)->name[0] == '/');
  555. if (!strcmp((*pointerp)->name+1,name)) break;
  556. pointerp= &(*pointerp)->next;
  557. }
  558. if (*pointerp) return *pointerp;
  559. newnode= nfmalloc(sizeof(struct filenamenode));
  560. newnode->packages= 0;
  561. newnode->name= nfmalloc(strlen(name)+2);
  562. newnode->name[0]= '/'; strcpy(newnode->name+1,name);
  563. newnode->flags= 0;
  564. newnode->next= 0;
  565. newnode->divert= 0;
  566. *pointerp= newnode;
  567. nfiles++;
  568. return newnode;
  569. }