rcorder(8): Whitespace fix.
[dragonfly.git] / sbin / rcorder / rcorder.c
1 /*
2  * Copyright (c) 1998, 1999 Matthew R. Green
3  * All rights reserved.
4  * Copyright (c) 1998
5  *      Perry E. Metzger.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed for the NetBSD Project
18  *      by Perry E. Metzger.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  *      $NetBSD: rcorder.c,v 1.7 2000/08/04 07:33:55 enami Exp $
34  */
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38
39 #include <err.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <libutil.h>
45
46 #include "sprite.h"
47 #include "hash.h"
48
49 #ifdef DEBUG
50 int debug = 0;
51 # define        DPRINTF(args) if (debug) { fflush(stdout); fprintf args; }
52 #else
53 # define        DPRINTF(args)
54 #endif
55
56 int provide;
57
58 #define REQUIRE_STR     "# REQUIRE:"
59 #define REQUIRE_LEN     (sizeof(REQUIRE_STR) - 1)
60 #define REQUIRES_STR    "# REQUIRES:"
61 #define REQUIRES_LEN    (sizeof(REQUIRES_STR) - 1)
62 #define PROVIDE_STR     "# PROVIDE:"
63 #define PROVIDE_LEN     (sizeof(PROVIDE_STR) - 1)
64 #define PROVIDES_STR    "# PROVIDES:"
65 #define PROVIDES_LEN    (sizeof(PROVIDES_STR) - 1)
66 #define BEFORE_STR      "# BEFORE:"
67 #define BEFORE_LEN      (sizeof(BEFORE_STR) - 1)
68 #define KEYWORD_STR     "# KEYWORD:"
69 #define KEYWORD_LEN     (sizeof(KEYWORD_STR) - 1)
70 #define KEYWORDS_STR    "# KEYWORDS:"
71 #define KEYWORDS_LEN    (sizeof(KEYWORDS_STR) - 1)
72
73 int exit_code;
74 int file_count;
75 char **file_list;
76
77 typedef int bool;
78 #define TRUE 1
79 #define FALSE 0
80 typedef bool flag;
81 #define SET TRUE
82 #define RESET FALSE
83
84 Hash_Table provide_hash_s, *provide_hash;
85
86 typedef struct provnode provnode;
87 typedef struct filenode filenode;
88 typedef struct f_provnode f_provnode;
89 typedef struct f_reqnode f_reqnode;
90 typedef struct strnodelist strnodelist;
91
92 struct provnode {
93         flag            head;
94         flag            in_progress;
95         filenode        *fnode;
96         provnode        *next, *last;
97         char            *name;
98 };
99
100 struct f_provnode {
101         provnode        *pnode;
102         f_provnode      *next;
103 };
104
105 struct f_reqnode {
106         Hash_Entry      *entry;
107         f_reqnode       *next;
108 };
109
110 struct strnodelist {
111         filenode        *node;
112         strnodelist     *next;
113         char            s[1];
114 };
115
116 struct filenode {
117         char            *filename;
118         flag            in_progress;
119         filenode        *next, *last;
120         f_reqnode       *req_list;
121         f_provnode      *prov_list;
122         strnodelist     *keyword_list;
123 };
124
125 filenode fn_head_s, *fn_head;
126
127 strnodelist *bl_list;
128 strnodelist *keep_list;
129 strnodelist *skip_list;
130 strnodelist *onetime_list;
131
132 void do_file(filenode *fnode);
133 void strnode_add(strnodelist **, char *, filenode *);
134 int skip_ok(filenode *fnode);
135 int keep_ok(filenode *fnode);
136 void satisfy_req(f_reqnode *rnode, char *filename);
137 void crunch_file(char *);
138 void parse_require(filenode *, char *);
139 void parse_provide(filenode *, char *);
140 void parse_before(filenode *, char *);
141 void parse_keywords(filenode *, char *);
142 filenode *filenode_new(char *);
143 void add_require(filenode *, char *);
144 void add_provide(filenode *, char *);
145 void add_before(filenode *, char *);
146 void add_keyword(filenode *, char *);
147 void insert_before(void);
148 Hash_Entry *make_fake_provision(filenode *);
149 void crunch_all_files(void);
150 void initialize(void);
151 void generate_ordering(void);
152 int main(int, char *[]);
153
154 int
155 main(int argc, char **argv)
156 {
157         int ch;
158
159         while ((ch = getopt(argc, argv, "dpk:s:o:")) != -1)
160                 switch (ch) {
161                 case 'd':
162 #ifdef DEBUG
163                         debug = 1;
164 #else
165                         warnx("debugging not compiled in, -d ignored");
166 #endif
167                         break;
168                 case 'k':
169                         strnode_add(&keep_list, optarg, 0);
170                         break;
171                 case 's':
172                         strnode_add(&skip_list, optarg, 0);
173                         break;
174                 case 'o':
175                         strnode_add(&onetime_list, optarg, 0);
176                         break;
177                 case 'p':
178                         provide = 1;
179                         break;
180                 default:
181                         /* XXX should crunch it? */
182                         break;
183                 }
184         argc -= optind;
185         argv += optind;
186
187         file_count = argc;
188         file_list = argv;
189
190         DPRINTF((stderr, "parse_args\n"));
191         initialize();
192         DPRINTF((stderr, "initialize\n"));
193         crunch_all_files();
194         DPRINTF((stderr, "crunch_all_files\n"));
195         generate_ordering();
196         DPRINTF((stderr, "generate_ordering\n"));
197
198         exit(exit_code);
199 }
200
201 /*
202  * initialise various variables.
203  */
204 void
205 initialize(void)
206 {
207
208         fn_head = &fn_head_s;
209
210         provide_hash = &provide_hash_s;
211         Hash_InitTable(provide_hash, file_count);
212 }
213
214 /* generic function to insert a new strnodelist element */
215 void
216 strnode_add(strnodelist **listp, char *s, filenode *fnode)
217 {
218         strnodelist *ent;
219
220         ent = emalloc(sizeof *ent + strlen(s));
221         ent->node = fnode;
222         strcpy(ent->s, s);
223         ent->next = *listp;
224         *listp = ent;
225 }
226
227 /*
228  * below are the functions that deal with creating the lists
229  * from the filename's given and the dependancies and provisions
230  * in each of these files.  no ordering or checking is done here.
231  */
232
233 /*
234  * we have a new filename, create a new filenode structure.
235  * fill in the bits, and put it in the filenode linked list
236  */
237 filenode *
238 filenode_new(char *filename)
239 {
240         filenode *temp;
241
242         temp = emalloc(sizeof(*temp));
243         memset(temp, 0, sizeof(*temp));
244         temp->filename = estrdup(filename);
245         temp->req_list = NULL;
246         temp->prov_list = NULL;
247         temp->keyword_list = NULL;
248         temp->in_progress = RESET;
249         /*
250          * link the filenode into the list of filenodes.
251          * note that the double linking means we can delete a
252          * filenode without searching for where it belongs.
253          */
254         temp->next = fn_head->next;
255         if (temp->next != NULL)
256                 temp->next->last = temp;
257         temp->last = fn_head;
258         fn_head->next = temp;
259         return (temp);
260 }
261
262 /*
263  * add a requirement to a filenode.
264  */
265 void
266 add_require(filenode *fnode, char *s)
267 {
268         Hash_Entry *entry;
269         f_reqnode *rnode;
270         int new;
271
272         entry = Hash_CreateEntry(provide_hash, s, &new);
273         if (new)
274                 Hash_SetValue(entry, NULL);
275         rnode = emalloc(sizeof(*rnode));
276         rnode->entry = entry;
277         rnode->next = fnode->req_list;
278         fnode->req_list = rnode;
279 }
280
281 /*
282  * add a provision to a filenode.  if this provision doesn't
283  * have a head node, create one here.
284  */
285 void
286 add_provide(filenode *fnode, char *s)
287 {
288         Hash_Entry *entry;
289         f_provnode *f_pnode;
290         provnode *pnode, *head;
291         int new;
292
293         entry = Hash_CreateEntry(provide_hash, s, &new);
294         head = Hash_GetValue(entry);
295
296         /* create a head node if necessary. */
297         if (head == NULL) {
298                 head = emalloc(sizeof(*head));
299                 head->head = SET;
300                 head->in_progress = RESET;
301                 head->fnode = NULL;
302                 head->last = head->next = NULL;
303                 Hash_SetValue(entry, head);
304         }
305 #if 0
306         /*
307          * Don't warn about this.  We want to be able to support
308          * scripts that do two complex things:
309          *
310          *      - Two independent scripts which both provide the
311          *        same thing.  Both scripts must be executed in
312          *        any order to meet the barrier.  An example:
313          *
314          *              Script 1:
315          *
316          *                      PROVIDE: mail
317          *                      REQUIRE: LOGIN
318          *
319          *              Script 2:
320          *
321          *                      PROVIDE: mail
322          *                      REQUIRE: LOGIN
323          *
324          *      - Two interdependent scripts which both provide the
325          *        same thing.  Both scripts must be executed in
326          *        graph order to meet the barrier.  An example:
327          *
328          *              Script 1:
329          *
330          *                      PROVIDE: nameservice dnscache
331          *                      REQUIRE: SERVERS
332          *
333          *              Script 2:
334          *
335          *                      PROVIDE: nameservice nscd
336          *                      REQUIRE: dnscache
337          */
338         else if (new == 0) {
339                 warnx("file `%s' provides `%s'.", fnode->filename, s);
340                 warnx("\tpreviously seen in `%s'.",
341                     head->next->fnode->filename);
342         }
343 #endif
344
345         pnode = emalloc(sizeof(*pnode));
346         pnode->head = RESET;
347         pnode->in_progress = RESET;
348         pnode->fnode = fnode;
349         pnode->next = head->next;
350         pnode->last = head;
351         pnode->name = strdup(s);
352         head->next = pnode;
353         if (pnode->next != NULL)
354                 pnode->next->last = pnode;
355
356         f_pnode = emalloc(sizeof(*f_pnode));
357         f_pnode->pnode = pnode;
358         f_pnode->next = fnode->prov_list;
359         fnode->prov_list = f_pnode;
360 }
361
362 /*
363  * put the BEFORE: lines to a list and handle them later.
364  */
365 void
366 add_before(filenode *fnode, char *s)
367 {
368         strnodelist *bf_ent;
369
370         bf_ent = emalloc(sizeof *bf_ent + strlen(s));
371         bf_ent->node = fnode;
372         strcpy(bf_ent->s, s);
373         bf_ent->next = bl_list;
374         bl_list = bf_ent;
375 }
376
377 /*
378  * add a key to a filenode.
379  */
380 void
381 add_keyword(filenode *fnode, char *s)
382 {
383
384         strnode_add(&fnode->keyword_list, s, fnode);
385 }
386
387 /*
388  * loop over the rest of a REQUIRE line, giving each word to
389  * add_require() to do the real work.
390  */
391 void
392 parse_require(filenode *node, char *buffer)
393 {
394         char *s;
395         
396         while ((s = strsep(&buffer, " \t\n")) != NULL)
397                 if (*s != '\0')
398                         add_require(node, s);
399 }
400
401 /*
402  * loop over the rest of a PROVIDE line, giving each word to
403  * add_provide() to do the real work.
404  */
405 void
406 parse_provide(filenode *node, char *buffer)
407 {
408         char *s;
409         
410         while ((s = strsep(&buffer, " \t\n")) != NULL)
411                 if (*s != '\0')
412                         add_provide(node, s);
413 }
414
415 /*
416  * loop over the rest of a BEFORE line, giving each word to
417  * add_before() to do the real work.
418  */
419 void
420 parse_before(filenode *node, char *buffer)
421 {
422         char *s;
423         
424         while ((s = strsep(&buffer, " \t\n")) != NULL)
425                 if (*s != '\0')
426                         add_before(node, s);
427 }
428
429 /*
430  * loop over the rest of a KEYWORD line, giving each word to
431  * add_keyword() to do the real work.
432  */
433 void
434 parse_keywords(filenode *node, char *buffer)
435 {
436         char *s;
437         
438         while ((s = strsep(&buffer, " \t\n")) != NULL)
439                 if (*s != '\0')
440                         add_keyword(node, s);
441 }
442
443 /*
444  * given a file name, create a filenode for it, read in lines looking
445  * for provision and requirement lines, building the graphs as needed.
446  */
447 void
448 crunch_file(char *filename)
449 {
450         FILE *fp;
451         char *buf;
452         int require_flag, provide_flag, before_flag, keywords_flag;
453         enum { BEFORE_PARSING, PARSING, PARSING_DONE } state;
454         filenode *node;
455         char delims[3] = { '\\', '\\', '\0' };
456         struct stat st;
457
458         if ((fp = fopen(filename, "r")) == NULL) {
459                 warn("could not open %s", filename);
460                 return;
461         }
462
463         if (fstat(fileno(fp), &st) == -1) {
464                 warn("could not stat %s", filename);
465                 fclose(fp);
466                 return;
467         }
468
469         if (!S_ISREG(st.st_mode)) {
470 #if 0
471                 warnx("%s is not a file", filename);
472 #endif
473                 fclose(fp);
474                 return;
475         }
476
477         node = filenode_new(filename);
478
479         /*
480          * we don't care about length, line number, don't want # for comments,
481          * and have no flags.
482          */
483         for (state = BEFORE_PARSING; state != PARSING_DONE &&
484             (buf = fparseln(fp, NULL, NULL, delims, 0)) != NULL; free(buf)) {
485                 require_flag = provide_flag = before_flag = keywords_flag = 0;
486                 if (strncmp(REQUIRE_STR, buf, REQUIRE_LEN) == 0)
487                         require_flag = REQUIRE_LEN;
488                 else if (strncmp(REQUIRES_STR, buf, REQUIRES_LEN) == 0)
489                         require_flag = REQUIRES_LEN;
490                 else if (strncmp(PROVIDE_STR, buf, PROVIDE_LEN) == 0)
491                         provide_flag = PROVIDE_LEN;
492                 else if (strncmp(PROVIDES_STR, buf, PROVIDES_LEN) == 0)
493                         provide_flag = PROVIDES_LEN;
494                 else if (strncmp(BEFORE_STR, buf, BEFORE_LEN) == 0)
495                         before_flag = BEFORE_LEN;
496                 else if (strncmp(KEYWORD_STR, buf, KEYWORD_LEN) == 0)
497                         keywords_flag = KEYWORD_LEN;
498                 else if (strncmp(KEYWORDS_STR, buf, KEYWORDS_LEN) == 0)
499                         keywords_flag = KEYWORDS_LEN;
500                 else {
501                         if (state == PARSING)
502                                 state = PARSING_DONE;
503                         continue;
504                 }
505
506                 state = PARSING;
507                 if (require_flag)
508                         parse_require(node, buf + require_flag);
509                 else if (provide_flag)
510                         parse_provide(node, buf + provide_flag);
511                 else if (before_flag)
512                         parse_before(node, buf + before_flag);
513                 else if (keywords_flag)
514                         parse_keywords(node, buf + keywords_flag);
515         }
516         fclose(fp);
517 }
518
519 Hash_Entry *
520 make_fake_provision(filenode *node)
521 {
522         Hash_Entry *entry;
523         f_provnode *f_pnode;
524         provnode *head, *pnode;
525         static  int i = 0;
526         int     new;
527         char buffer[30];
528
529         do {
530                 snprintf(buffer, sizeof buffer, "fake_prov_%08d", i++);
531                 entry = Hash_CreateEntry(provide_hash, buffer, &new);
532         } while (new == 0);
533         head = emalloc(sizeof(*head));
534         head->head = SET;
535         head->in_progress = RESET;
536         head->fnode = NULL;
537         head->last = head->next = NULL;
538         Hash_SetValue(entry, head);
539
540         pnode = emalloc(sizeof(*pnode));
541         pnode->head = RESET;
542         pnode->in_progress = RESET;
543         pnode->fnode = node;
544         pnode->next = head->next;
545         pnode->last = head;
546         pnode->name = strdup(buffer);
547         head->next = pnode;
548         if (pnode->next != NULL)
549                 pnode->next->last = pnode;
550
551         f_pnode = emalloc(sizeof(*f_pnode));
552         f_pnode->pnode = pnode;
553         f_pnode->next = node->prov_list;
554         node->prov_list = f_pnode;
555
556         return (entry);
557 }
558
559 /*
560  * go through the BEFORE list, inserting requirements into the graph(s)
561  * as required.  in the before list, for each entry B, we have a file F
562  * and a string S.  we create a "fake" provision (P) that F provides.
563  * for each entry in the provision list for S, add a requirement to
564  * that provisions filenode for P.
565  */
566 void
567 insert_before(void)
568 {
569         Hash_Entry *entry, *fake_prov_entry;
570         provnode *pnode;
571         f_reqnode *rnode;
572         strnodelist *bl;
573         int new;
574         
575         while (bl_list != NULL) {
576                 bl = bl_list->next;
577
578                 fake_prov_entry = make_fake_provision(bl_list->node);
579
580                 entry = Hash_CreateEntry(provide_hash, bl_list->s, &new);
581                 if (new == 1 && !provide)
582                         warnx("file `%s' is before unknown provision `%s'", bl_list->node->filename, bl_list->s);
583
584                 for (pnode = Hash_GetValue(entry); pnode; pnode = pnode->next) {
585                         if (pnode->head)
586                                 continue;
587
588                         rnode = emalloc(sizeof(*rnode));
589                         rnode->entry = fake_prov_entry;
590                         rnode->next = pnode->fnode->req_list;
591                         pnode->fnode->req_list = rnode;
592                 }
593
594                 free(bl_list);
595                 bl_list = bl;
596         }
597 }
598
599 /*
600  * loop over all the files calling crunch_file() on them to do the
601  * real work.  after we have built all the nodes, insert the BEFORE:
602  * lines into graph(s).
603  */
604 void
605 crunch_all_files(void)
606 {
607         int i;
608         
609         for (i = 0; i < file_count; i++)
610                 crunch_file(file_list[i]);
611         insert_before();
612 }
613
614 /*
615  * below are the functions that traverse the graphs we have built
616  * finding out the desired ordering, printing each file in turn.
617  * if missing requirements, or cyclic graphs are detected, a
618  * warning will be issued, and we will continue on..
619  */
620
621 /*
622  * given a requirement node (in a filename) we attempt to satisfy it.
623  * we do some sanity checking first, to ensure that we have providers,
624  * aren't already satisfied and aren't already being satisfied (ie,
625  * cyclic).  if we pass all this, we loop over the provision list
626  * calling do_file() (enter recursion) for each filenode in this
627  * provision.
628  */
629 void
630 satisfy_req(f_reqnode *rnode, char *filename)
631 {
632         Hash_Entry *entry;
633         provnode *head;
634
635         entry = rnode->entry;
636         head = Hash_GetValue(entry);
637
638         if (head == NULL) {
639                 warnx("requirement `%s' in file `%s' has no providers.",
640                     Hash_GetKey(entry), filename);
641                 exit_code = 1;
642                 return;
643         }
644
645         /* return if the requirement is already satisfied. */
646         if (head->next == NULL)
647                 return;
648
649         /* 
650          * if list is marked as in progress,
651          *      print that there is a circular dependency on it and abort
652          */
653         if (head->in_progress == SET) {
654                 warnx("Circular dependency on provision `%s' in file `%s'.",
655                     Hash_GetKey(entry), filename);
656                 exit_code = 1;
657                 return;
658         }
659
660         head->in_progress = SET;
661         
662         /*
663          * while provision_list is not empty
664          *      do_file(first_member_of(provision_list));
665          */
666         while (head->next != NULL)
667                 do_file(head->next->fnode);
668 }
669
670 int
671 skip_ok(filenode *fnode)
672 {
673         strnodelist *s;
674         strnodelist *k;
675
676         for (s = skip_list; s; s = s->next)
677                 for (k = fnode->keyword_list; k; k = k->next)
678                         if (strcmp(k->s, s->s) == 0)
679                                 return (0);
680
681         return (1);
682 }
683
684 int
685 keep_ok(filenode *fnode)
686 {
687         strnodelist *s;
688         strnodelist *k;
689
690         for (s = keep_list; s; s = s->next)
691                 for (k = fnode->keyword_list; k; k = k->next)
692                         if (strcmp(k->s, s->s) == 0)
693                                 return (1);
694
695         /* an empty keep_list means every one */
696         return (!keep_list);
697 }
698
699 /*
700  * given a filenode, we ensure we are not a cyclic graph.  if this
701  * is ok, we loop over the filenodes requirements, calling satisfy_req()
702  * for each of them.. once we have done this, remove this filenode
703  * from each provision table, as we are now done.
704  *
705  * NOTE: do_file() is called recursively from several places and cannot
706  * safely free() anything related to items that may be recursed on.
707  * Circular dependancies will cause problems if we do.
708  */
709 void
710 do_file(filenode *fnode)
711 {
712         f_reqnode *r;
713         /*f_reqnode *r_tmp;*/
714         f_provnode *p, *p_tmp;
715         provnode *pnode;
716         int was_set;    
717
718         DPRINTF((stderr, "do_file on %s.\n", fnode->filename));
719
720         /*
721          * if fnode is marked as in progress,
722          *       print that fnode; is circularly depended upon and abort.
723          */
724         if (fnode->in_progress == SET) {
725                 warnx("Circular dependency on file `%s'.",
726                         fnode->filename);
727                 was_set = exit_code = 1;
728         } else
729                 was_set = 0;
730
731         /* mark fnode */
732         fnode->in_progress = SET;
733
734         /*
735          * for each requirement of fnode -> r
736          *      satisfy_req(r, filename)
737          */
738         r = fnode->req_list;
739         while (r != NULL) {
740                 /*r_tmp = r;*/
741                 satisfy_req(r, fnode->filename);
742                 r = r->next;
743                 /*free(r_tmp);*/
744         }
745         fnode->req_list = NULL;
746
747         /*
748          * for each provision of fnode -> p
749          *      remove fnode from provision list for p in hash table
750          */
751         p = fnode->prov_list;
752         while (p != NULL) {
753                 p_tmp = p;
754                 pnode = p->pnode;
755                 if (pnode->next != NULL) {
756                         pnode->next->last = pnode->last;
757                 }
758                 if (pnode->last != NULL) {
759                         pnode->last->next = pnode->next;
760                 }
761                 free(pnode);
762                 p = p->next;
763                 free(p_tmp);
764         }
765         fnode->prov_list = NULL;
766
767         /* do_it(fnode) */
768         DPRINTF((stderr, "next do: "));
769
770         /* if we were already in progress, don't print again */
771         if (was_set == 0 && skip_ok(fnode) && keep_ok(fnode))
772                 printf("%s\n", fnode->filename);
773         
774         if (fnode->next != NULL) {
775                 fnode->next->last = fnode->last;
776         }
777         if (fnode->last != NULL) {
778                 fnode->last->next = fnode->next;
779         }
780
781         DPRINTF((stderr, "nuking %s\n", fnode->filename));
782         /*free(fnode->filename);*/
783         /*free(fnode);*/
784 }
785
786 void
787 generate_ordering(void)
788 {
789
790         /*
791          * while there remain undone files{f},
792          *      pick an arbitrary f, and do_file(f)
793          * Note that the first file in the file list is perfectly
794          * arbitrary, and easy to find, so we use that.
795          */
796
797         /*
798          * N.B.: the file nodes "self delete" after they execute, so
799          * after each iteration of the loop, the head will be pointing
800          * to something totally different. The loop ends up being
801          * executed only once for every strongly connected set of
802          * nodes.
803          */
804         if (provide) {
805                 /*
806                  * List all keywords provided by the listed files
807                  */
808                 filenode *file;
809                 f_provnode *f_prov;
810
811                 for (file = fn_head->next; file; file = file->next) {
812                     for (f_prov = file->prov_list; f_prov; f_prov = f_prov->next) {
813                         if (strncmp(f_prov->pnode->name, "fake_", 5) != 0)
814                             printf("%s\n", f_prov->pnode->name);
815                     }
816                 }
817         } else if (onetime_list) {
818                 /*
819                  * Only list dependanacies required to start particular
820                  * keywords.
821                  */
822                 strnodelist *scan;
823                 filenode *file;
824                 f_provnode *f_prov;
825
826                 for (scan = onetime_list; scan; scan = scan->next) {
827                     for (file = fn_head->next; file; file = file->next) {
828                         for (f_prov = file->prov_list; f_prov; f_prov = f_prov->next) {
829                             if (strcmp(scan->s, f_prov->pnode->name) == 0) {
830                                 do_file(file);
831                                 break;
832                             }
833                         }
834                         if (f_prov)
835                             break;
836                     }
837                 }
838         } else {
839                 while (fn_head->next != NULL) {
840                         DPRINTF((stderr, "generate on %s\n", fn_head->next->filename));
841                         do_file(fn_head->next);
842                 }
843         }
844 }