]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bmake/compat.c
Update to bmake-20201101
[FreeBSD/FreeBSD.git] / contrib / bmake / compat.c
1 /*      $NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $      */
2
3 /*
4  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Adam de Boor.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 /*
36  * Copyright (c) 1988, 1989 by Adam de Boor
37  * Copyright (c) 1989 by Berkeley Softworks
38  * All rights reserved.
39  *
40  * This code is derived from software contributed to Berkeley by
41  * Adam de Boor.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgement:
53  *      This product includes software developed by the University of
54  *      California, Berkeley and its contributors.
55  * 4. Neither the name of the University nor the names of its contributors
56  *    may be used to endorse or promote products derived from this software
57  *    without specific prior written permission.
58  *
59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69  * SUCH DAMAGE.
70  */
71
72 /*-
73  * compat.c --
74  *      The routines in this file implement the full-compatibility
75  *      mode of PMake. Most of the special functionality of PMake
76  *      is available in this mode. Things not supported:
77  *          - different shells.
78  *          - friendly variable substitution.
79  *
80  * Interface:
81  *      Compat_Run      Initialize things for this module and recreate
82  *                      thems as need creatin'
83  */
84
85 #ifdef HAVE_CONFIG_H
86 # include   "config.h"
87 #endif
88 #include <sys/types.h>
89 #include <sys/stat.h>
90 #include "wait.h"
91
92 #include <errno.h>
93 #include <signal.h>
94
95 #include "make.h"
96 #include "dir.h"
97 #include "job.h"
98 #include "metachar.h"
99 #include "pathnames.h"
100
101 /*      "@(#)compat.c   8.2 (Berkeley) 3/19/94" */
102 MAKE_RCSID("$NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $");
103
104 static GNode *curTarg = NULL;
105 static pid_t compatChild;
106 static int compatSigno;
107
108 /*
109  * CompatDeleteTarget -- delete a failed, interrupted, or otherwise
110  * duffed target if not inhibited by .PRECIOUS.
111  */
112 static void
113 CompatDeleteTarget(GNode *gn)
114 {
115     if (gn != NULL && !Targ_Precious(gn)) {
116         const char *file = GNode_VarTarget(gn);
117
118         if (!opts.noExecute && eunlink(file) != -1) {
119             Error("*** %s removed", file);
120         }
121     }
122 }
123
124 /* Interrupt the creation of the current target and remove it if it ain't
125  * precious. Then exit.
126  *
127  * If .INTERRUPT exists, its commands are run first WITH INTERRUPTS IGNORED.
128  *
129  * XXX: is .PRECIOUS supposed to inhibit .INTERRUPT? I doubt it, but I've
130  * left the logic alone for now. - dholland 20160826
131  */
132 static void
133 CompatInterrupt(int signo)
134 {
135     GNode   *gn;
136
137     CompatDeleteTarget(curTarg);
138
139     if (curTarg != NULL && !Targ_Precious(curTarg)) {
140         /*
141          * Run .INTERRUPT only if hit with interrupt signal
142          */
143         if (signo == SIGINT) {
144             gn = Targ_FindNode(".INTERRUPT");
145             if (gn != NULL) {
146                 Compat_Make(gn, gn);
147             }
148         }
149     }
150
151     if (signo == SIGQUIT)
152         _exit(signo);
153
154     /*
155      * If there is a child running, pass the signal on.
156      * We will exist after it has exited.
157      */
158     compatSigno = signo;
159     if (compatChild > 0) {
160         KILLPG(compatChild, signo);
161     } else {
162         bmake_signal(signo, SIG_DFL);
163         kill(myPid, signo);
164     }
165 }
166
167 /* Execute the next command for a target. If the command returns an error,
168  * the node's made field is set to ERROR and creation stops.
169  *
170  * Input:
171  *      cmdp            Command to execute
172  *      gnp             Node from which the command came
173  *
174  * Results:
175  *      0 if the command succeeded, 1 if an error occurred.
176  */
177 int
178 Compat_RunCommand(const char *cmdp, GNode *gn)
179 {
180     char *cmdStart;             /* Start of expanded command */
181     char *bp;
182     Boolean silent;             /* Don't print command */
183     Boolean doIt;               /* Execute even if -n */
184     volatile Boolean errCheck;  /* Check errors */
185     WAIT_T reason;              /* Reason for child's death */
186     int status;                 /* Description of child's death */
187     pid_t cpid;                 /* Child actually found */
188     pid_t retstat;              /* Result of wait */
189     StringListNode *cmdNode;    /* Node where current command is located */
190     const char **volatile av;   /* Argument vector for thing to exec */
191     char **volatile mav;        /* Copy of the argument vector for freeing */
192     Boolean useShell;           /* TRUE if command should be executed
193                                  * using a shell */
194     const char *volatile cmd = cmdp;
195
196     silent = (gn->type & OP_SILENT) != 0;
197     errCheck = !(gn->type & OP_IGNORE);
198     doIt = FALSE;
199
200     /* Luckily the commands don't end up in a string pool, otherwise
201      * this comparison could match too early, in a dependency using "..."
202      * for delayed commands, run in parallel mode, using the same shell
203      * command line more than once; see JobPrintCommand.
204      * TODO: write a unit-test to protect against this potential bug. */
205     cmdNode = Lst_FindDatum(gn->commands, cmd);
206     (void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
207     /* TODO: handle errors */
208
209     if (*cmdStart == '\0') {
210         free(cmdStart);
211         return 0;
212     }
213     cmd = cmdStart;
214     LstNode_Set(cmdNode, cmdStart);
215
216     if (gn->type & OP_SAVE_CMDS) {
217         GNode *endNode = Targ_GetEndNode();
218         if (gn != endNode) {
219             Lst_Append(endNode->commands, cmdStart);
220             return 0;
221         }
222     }
223     if (strcmp(cmdStart, "...") == 0) {
224         gn->type |= OP_SAVE_CMDS;
225         return 0;
226     }
227
228     while (*cmd == '@' || *cmd == '-' || *cmd == '+') {
229         switch (*cmd) {
230         case '@':
231             silent = !DEBUG(LOUD);
232             break;
233         case '-':
234             errCheck = FALSE;
235             break;
236         case '+':
237             doIt = TRUE;
238             if (!shellName)             /* we came here from jobs */
239                 Shell_Init();
240             break;
241         }
242         cmd++;
243     }
244
245     while (ch_isspace(*cmd))
246         cmd++;
247
248     /*
249      * If we did not end up with a command, just skip it.
250      */
251     if (!*cmd)
252         return 0;
253
254 #if !defined(MAKE_NATIVE)
255     /*
256      * In a non-native build, the host environment might be weird enough
257      * that it's necessary to go through a shell to get the correct
258      * behaviour.  Or perhaps the shell has been replaced with something
259      * that does extra logging, and that should not be bypassed.
260      */
261     useShell = TRUE;
262 #else
263     /*
264      * Search for meta characters in the command. If there are no meta
265      * characters, there's no need to execute a shell to execute the
266      * command.
267      *
268      * Additionally variable assignments and empty commands
269      * go to the shell. Therefore treat '=' and ':' like shell
270      * meta characters as documented in make(1).
271      */
272
273     useShell = needshell(cmd);
274 #endif
275
276     /*
277      * Print the command before echoing if we're not supposed to be quiet for
278      * this one. We also print the command if -n given.
279      */
280     if (!silent || !GNode_ShouldExecute(gn)) {
281         printf("%s\n", cmd);
282         fflush(stdout);
283     }
284
285     /*
286      * If we're not supposed to execute any commands, this is as far as
287      * we go...
288      */
289     if (!doIt && !GNode_ShouldExecute(gn)) {
290         return 0;
291     }
292     DEBUG1(JOB, "Execute: '%s'\n", cmd);
293
294     if (useShell) {
295         /*
296          * We need to pass the command off to the shell, typically
297          * because the command contains a "meta" character.
298          */
299         static const char *shargv[5];
300         int shargc;
301
302         shargc = 0;
303         shargv[shargc++] = shellPath;
304         /*
305          * The following work for any of the builtin shell specs.
306          */
307         if (errCheck && shellErrFlag) {
308             shargv[shargc++] = shellErrFlag;
309         }
310         if (DEBUG(SHELL))
311                 shargv[shargc++] = "-xc";
312         else
313                 shargv[shargc++] = "-c";
314         shargv[shargc++] = cmd;
315         shargv[shargc] = NULL;
316         av = shargv;
317         bp = NULL;
318         mav = NULL;
319     } else {
320         /*
321          * No meta-characters, so no need to exec a shell. Break the command
322          * into words to form an argument vector we can execute.
323          */
324         Words words = Str_Words(cmd, FALSE);
325         mav = words.words;
326         bp = words.freeIt;
327         av = (void *)mav;
328     }
329
330 #ifdef USE_META
331     if (useMeta) {
332         meta_compat_start();
333     }
334 #endif
335
336     /*
337      * Fork and execute the single command. If the fork fails, we abort.
338      */
339     compatChild = cpid = vFork();
340     if (cpid < 0) {
341         Fatal("Could not fork");
342     }
343     if (cpid == 0) {
344         Var_ExportVars();
345 #ifdef USE_META
346         if (useMeta) {
347             meta_compat_child();
348         }
349 #endif
350         (void)execvp(av[0], (char *const *)UNCONST(av));
351         execDie("exec", av[0]);
352     }
353
354     free(mav);
355     free(bp);
356
357     /* XXX: Memory management looks suspicious here. */
358     /* XXX: Setting a list item to NULL is unexpected. */
359     LstNode_SetNull(cmdNode);
360
361 #ifdef USE_META
362     if (useMeta) {
363         meta_compat_parent(cpid);
364     }
365 #endif
366
367     /*
368      * The child is off and running. Now all we can do is wait...
369      */
370     while ((retstat = wait(&reason)) != cpid) {
371         if (retstat > 0)
372             JobReapChild(retstat, reason, FALSE); /* not ours? */
373         if (retstat == -1 && errno != EINTR) {
374             break;
375         }
376     }
377
378     if (retstat < 0)
379         Fatal("error in wait: %d: %s", retstat, strerror(errno));
380
381     if (WIFSTOPPED(reason)) {
382         status = WSTOPSIG(reason);              /* stopped */
383     } else if (WIFEXITED(reason)) {
384         status = WEXITSTATUS(reason);           /* exited */
385 #if defined(USE_META) && defined(USE_FILEMON_ONCE)
386         if (useMeta) {
387             meta_cmd_finish(NULL);
388         }
389 #endif
390         if (status != 0) {
391             if (DEBUG(ERROR)) {
392                 const char *cp;
393                 debug_printf("\n*** Failed target:  %s\n*** Failed command: ",
394                              gn->name);
395                 for (cp = cmd; *cp; ) {
396                     if (ch_isspace(*cp)) {
397                         debug_printf(" ");
398                         while (ch_isspace(*cp))
399                             cp++;
400                     } else {
401                         debug_printf("%c", *cp);
402                         cp++;
403                     }
404                 }
405                 debug_printf("\n");
406             }
407             printf("*** Error code %d", status);
408         }
409     } else {
410         status = WTERMSIG(reason);              /* signaled */
411         printf("*** Signal %d", status);
412     }
413
414
415     if (!WIFEXITED(reason) || status != 0) {
416         if (errCheck) {
417 #ifdef USE_META
418             if (useMeta) {
419                 meta_job_error(NULL, gn, 0, status);
420             }
421 #endif
422             gn->made = ERROR;
423             if (opts.keepgoing) {
424                 /* Abort the current target, but let others continue. */
425                 printf(" (continuing)\n");
426             } else {
427                 printf("\n");
428             }
429             if (deleteOnError)
430                 CompatDeleteTarget(gn);
431         } else {
432             /*
433              * Continue executing commands for this target.
434              * If we return 0, this will happen...
435              */
436             printf(" (ignored)\n");
437             status = 0;
438         }
439     }
440
441     free(cmdStart);
442     compatChild = 0;
443     if (compatSigno) {
444         bmake_signal(compatSigno, SIG_DFL);
445         kill(myPid, compatSigno);
446     }
447
448     return status;
449 }
450
451 static void
452 RunCommands(GNode *gn)
453 {
454     StringListNode *ln;
455     for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
456         const char *cmd = ln->datum;
457         if (Compat_RunCommand(cmd, gn) != 0)
458             break;
459     }
460 }
461
462 static void
463 MakeNodes(GNodeList *gnodes, GNode *pgn)
464 {
465     GNodeListNode *ln;
466     for (ln = gnodes->first; ln != NULL; ln = ln->next) {
467         GNode *cohort = ln->datum;
468         Compat_Make(cohort, pgn);
469     }
470 }
471
472 /* Make a target.
473  *
474  * If an error is detected and not being ignored, the process exits.
475  *
476  * Input:
477  *      gn              The node to make
478  *      pgn             Parent to abort if necessary
479  */
480 void
481 Compat_Make(GNode *gn, GNode *pgn)
482 {
483     if (!shellName)             /* we came here from jobs */
484         Shell_Init();
485     if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
486         /*
487          * First mark ourselves to be made, then apply whatever transformations
488          * the suffix module thinks are necessary. Once that's done, we can
489          * descend and make all our children. If any of them has an error
490          * but the -k flag was given, our 'make' field will be set FALSE again.
491          * This is our signal to not attempt to do anything but abort our
492          * parent as well.
493          */
494         gn->flags |= REMAKE;
495         gn->made = BEINGMADE;
496         if (!(gn->type & OP_MADE))
497             Suff_FindDeps(gn);
498         MakeNodes(gn->children, gn);
499         if (!(gn->flags & REMAKE)) {
500             gn->made = ABORTED;
501             pgn->flags &= ~(unsigned)REMAKE;
502             goto cohorts;
503         }
504
505         if (Lst_FindDatum(gn->implicitParents, pgn) != NULL)
506             Var_Set(IMPSRC, GNode_VarTarget(gn), pgn);
507
508         /*
509          * All the children were made ok. Now youngestChild->mtime contains the
510          * modification time of the newest child, we need to find out if we
511          * exist and when we were modified last. The criteria for datedness
512          * are defined by the Make_OODate function.
513          */
514         DEBUG1(MAKE, "Examining %s...", gn->name);
515         if (!Make_OODate(gn)) {
516             gn->made = UPTODATE;
517             DEBUG0(MAKE, "up-to-date.\n");
518             goto cohorts;
519         } else
520             DEBUG0(MAKE, "out-of-date.\n");
521
522         /*
523          * If the user is just seeing if something is out-of-date, exit now
524          * to tell him/her "yes".
525          */
526         if (opts.queryFlag) {
527             exit(1);
528         }
529
530         /*
531          * We need to be re-made. We also have to make sure we've got a $?
532          * variable. To be nice, we also define the $> variable using
533          * Make_DoAllVar().
534          */
535         Make_DoAllVar(gn);
536
537         /*
538          * Alter our type to tell if errors should be ignored or things
539          * should not be printed so CompatRunCommand knows what to do.
540          */
541         if (Targ_Ignore(gn))
542             gn->type |= OP_IGNORE;
543         if (Targ_Silent(gn))
544             gn->type |= OP_SILENT;
545
546         if (Job_CheckCommands(gn, Fatal)) {
547             /*
548              * Our commands are ok, but we still have to worry about the -t
549              * flag...
550              */
551             if (!opts.touchFlag || (gn->type & OP_MAKE)) {
552                 curTarg = gn;
553 #ifdef USE_META
554                 if (useMeta && GNode_ShouldExecute(gn)) {
555                     meta_job_start(NULL, gn);
556                 }
557 #endif
558                 RunCommands(gn);
559                 curTarg = NULL;
560             } else {
561                 Job_Touch(gn, (gn->type & OP_SILENT) != 0);
562             }
563         } else {
564             gn->made = ERROR;
565         }
566 #ifdef USE_META
567         if (useMeta && GNode_ShouldExecute(gn)) {
568             if (meta_job_finish(NULL) != 0)
569                 gn->made = ERROR;
570         }
571 #endif
572
573         if (gn->made != ERROR) {
574             /*
575              * If the node was made successfully, mark it so, update
576              * its modification time and timestamp all its parents. Note
577              * that for .ZEROTIME targets, the timestamping isn't done.
578              * This is to keep its state from affecting that of its parent.
579              */
580             gn->made = MADE;
581             pgn->flags |= Make_Recheck(gn) == 0 ? FORCE : 0;
582             if (!(gn->type & OP_EXEC)) {
583                 pgn->flags |= CHILDMADE;
584                 Make_TimeStamp(pgn, gn);
585             }
586         } else if (opts.keepgoing) {
587             pgn->flags &= ~(unsigned)REMAKE;
588         } else {
589             PrintOnError(gn, "\nStop.");
590             exit(1);
591         }
592     } else if (gn->made == ERROR) {
593         /* Already had an error when making this. Tell the parent to abort. */
594         pgn->flags &= ~(unsigned)REMAKE;
595     } else {
596         if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
597             const char *target = GNode_VarTarget(gn);
598             Var_Set(IMPSRC, target != NULL ? target : "", pgn);
599         }
600         switch(gn->made) {
601             case BEINGMADE:
602                 Error("Graph cycles through %s", gn->name);
603                 gn->made = ERROR;
604                 pgn->flags &= ~(unsigned)REMAKE;
605                 break;
606             case MADE:
607                 if ((gn->type & OP_EXEC) == 0) {
608                     pgn->flags |= CHILDMADE;
609                     Make_TimeStamp(pgn, gn);
610                 }
611                 break;
612             case UPTODATE:
613                 if ((gn->type & OP_EXEC) == 0) {
614                     Make_TimeStamp(pgn, gn);
615                 }
616                 break;
617             default:
618                 break;
619         }
620     }
621
622 cohorts:
623     MakeNodes(gn->cohorts, pgn);
624 }
625
626 /* Initialize this module and start making.
627  *
628  * Input:
629  *      targs           The target nodes to re-create
630  */
631 void
632 Compat_Run(GNodeList *targs)
633 {
634     GNode *gn = NULL;           /* Current root target */
635     int errors;                 /* Number of targets not remade due to errors */
636
637     if (!shellName)
638         Shell_Init();
639
640     if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN)
641         bmake_signal(SIGINT, CompatInterrupt);
642     if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN)
643         bmake_signal(SIGTERM, CompatInterrupt);
644     if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN)
645         bmake_signal(SIGHUP, CompatInterrupt);
646     if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN)
647         bmake_signal(SIGQUIT, CompatInterrupt);
648
649     /* Create the .END node now, to keep the (debug) output of the
650      * counter.mk test the same as before 2020-09-23.  This implementation
651      * detail probably doesn't matter though. */
652     (void)Targ_GetEndNode();
653     /*
654      * If the user has defined a .BEGIN target, execute the commands attached
655      * to it.
656      */
657     if (!opts.queryFlag) {
658         gn = Targ_FindNode(".BEGIN");
659         if (gn != NULL) {
660             Compat_Make(gn, gn);
661             if (gn->made == ERROR) {
662                 PrintOnError(gn, "\nStop.");
663                 exit(1);
664             }
665         }
666     }
667
668     /*
669      * Expand .USE nodes right now, because they can modify the structure
670      * of the tree.
671      */
672     Make_ExpandUse(targs);
673
674     /*
675      * For each entry in the list of targets to create, call Compat_Make on
676      * it to create the thing. Compat_Make will leave the 'made' field of gn
677      * in one of several states:
678      *      UPTODATE    gn was already up-to-date
679      *      MADE        gn was recreated successfully
680      *      ERROR       An error occurred while gn was being created
681      *      ABORTED     gn was not remade because one of its inferiors
682      *                  could not be made due to errors.
683      */
684     errors = 0;
685     while (!Lst_IsEmpty(targs)) {
686         gn = Lst_Dequeue(targs);
687         Compat_Make(gn, gn);
688
689         if (gn->made == UPTODATE) {
690             printf("`%s' is up to date.\n", gn->name);
691         } else if (gn->made == ABORTED) {
692             printf("`%s' not remade because of errors.\n", gn->name);
693             errors++;
694         }
695     }
696
697     /*
698      * If the user has defined a .END target, run its commands.
699      */
700     if (errors == 0) {
701         GNode *endNode = Targ_GetEndNode();
702         Compat_Make(endNode, endNode);
703         /* XXX: Did you mean endNode->made instead of gn->made? */
704         if (gn->made == ERROR) {
705             PrintOnError(gn, "\nStop.");
706             exit(1);
707         }
708     }
709 }