- Fix some duplicate commenting for struct Job, struct GNode and
[dragonfly.git] / usr.bin / make / compat.c
1 /*-
2  * Copyright (c) 1988, 1989, 1990, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1988, 1989 by Adam de Boor
5  * Copyright (c) 1989 by Berkeley Softworks
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Adam de Boor.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  * @(#)compat.c 8.2 (Berkeley) 3/19/94
40  * $FreeBSD: src/usr.bin/make/compat.c,v 1.16.2.2 2000/07/01 12:24:21 ps Exp $
41  * $DragonFly: src/usr.bin/make/Attic/compat.c,v 1.27 2005/02/01 22:05:36 okumoto Exp $
42  */
43
44 /*-
45  * compat.c --
46  *      The routines in this file implement the full-compatibility
47  *      mode of PMake. Most of the special functionality of PMake
48  *      is available in this mode. Things not supported:
49  *          - different shells.
50  *          - friendly variable substitution.
51  *
52  * Interface:
53  *      Compat_Run          Initialize things for this module and recreate
54  *                          thems as need creatin'
55  */
56
57 #include <sys/types.h>
58 #include <sys/wait.h>
59 #include <ctype.h>
60 #include <errno.h>
61 #include <signal.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65
66 #include "buf.h"
67 #include "compat.h"
68 #include "config.h"
69 #include "dir.h"
70 #include "globals.h"
71 #include "GNode.h"
72 #include "job.h"
73 #include "make.h"
74 #include "str.h"
75 #include "suff.h"
76 #include "targ.h"
77 #include "util.h"
78 #include "var.h"
79
80 /*
81  * The following array is used to make a fast determination of which
82  * characters are interpreted specially by the shell.  If a command
83  * contains any of these characters, it is executed by the shell, not
84  * directly by us.
85  */
86
87 static char         meta[256];
88
89 static GNode        *curTarg = NULL;
90 static GNode        *ENDNode;
91 static sig_atomic_t interrupted;
92
93 static void CompatInterrupt(int);
94 static int CompatMake(void *, void *);
95 static int shellneed(char *);
96
97 static void
98 CompatInit(void)
99 {
100     const char  *cp;    /* Pointer to string of shell meta-characters */
101
102     for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) {
103         meta[(unsigned char)*cp] = 1;
104     }
105     /*
106      * The null character serves as a sentinel in the string.
107      */
108     meta[0] = 1;
109 }
110
111 /*
112  * Interrupt handler - set flag and defer handling to the main code
113  */
114 static void
115 CompatCatchSig(int signo)
116 {
117
118         interrupted = signo;
119 }
120
121 /*-
122  *-----------------------------------------------------------------------
123  * CompatInterrupt --
124  *      Interrupt the creation of the current target and remove it if
125  *      it ain't precious.
126  *
127  * Results:
128  *      None.
129  *
130  * Side Effects:
131  *      The target is removed and the process exits. If .INTERRUPT exists,
132  *      its commands are run first WITH INTERRUPTS IGNORED..
133  *
134  *-----------------------------------------------------------------------
135  */
136 static void
137 CompatInterrupt(int signo)
138 {
139     GNode   *gn;
140     sigset_t nmask, omask;
141
142     sigemptyset(&nmask);
143     sigaddset(&nmask, SIGINT);
144     sigaddset(&nmask, SIGTERM);
145     sigaddset(&nmask, SIGHUP);
146     sigaddset(&nmask, SIGQUIT);
147     sigprocmask(SIG_SETMASK, &nmask, &omask);
148
149     /* prevent recursion in evaluation of .INTERRUPT */
150     interrupted = 0;
151
152     if ((curTarg != NULL) && !Targ_Precious(curTarg)) {
153         char      *p1;
154         char      *file = Var_Value(TARGET, curTarg, &p1);
155
156         if (!noExecute && eunlink(file) != -1) {
157             printf("*** %s removed\n", file);
158         }
159         free(p1);
160     }
161
162     /*
163      * Run .INTERRUPT only if hit with interrupt signal
164      */
165     if (signo == SIGINT) {
166         gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
167         if (gn != NULL) {
168             Lst_ForEach(&gn->commands, Compat_RunCommand, (void *)gn);
169         }
170     }
171
172     sigprocmask(SIG_SETMASK, &omask, NULL);
173
174     if (signo == SIGQUIT)
175         exit(signo);
176     signal(signo, SIG_DFL);
177     kill(getpid(), signo);
178 }
179
180 /*-
181  *-----------------------------------------------------------------------
182  * shellneed --
183  *
184  * Results:
185  *      Returns 1 if a specified line must be executed by the shell,
186  *      and 0 if it can be run via execve.
187  *
188  * Side Effects:
189  *      None.
190  *
191  *-----------------------------------------------------------------------
192  */
193 static int
194 shellneed(char *cmd)
195 {
196         static const char *sh_builtin[] = {
197                 "alias", "cd", "eval", "exec",
198                 "exit", "read", "set", "ulimit",
199                 "unalias", "umask", "unset", "wait",
200                 ":", NULL
201         };
202         char            **av;
203         const char      **p;
204         int             ac;
205
206         av = brk_string(cmd, &ac, TRUE);
207         for (p = sh_builtin; *p != 0; p++)
208                 if (strcmp(av[1], *p) == 0)
209                         return (1);
210         return (0);
211 }
212
213 /*-
214  *-----------------------------------------------------------------------
215  * Compat_RunCommand --
216  *      Execute the next command for a target. If the command returns an
217  *      error, the node's made field is set to ERROR and creation stops.
218  *      The node from which the command came is also given.
219  *
220  * Results:
221  *      0 if the command succeeded, 1 if an error occurred.
222  *
223  * Side Effects:
224  *      The node's 'made' field may be set to ERROR.
225  *
226  *-----------------------------------------------------------------------
227  */
228 int
229 Compat_RunCommand(void *cmdp, void *gnp)
230 {
231     char        *cmdStart;      /* Start of expanded command */
232     char        *cp;
233     Boolean     silent;         /* Don't print command */
234     Boolean     doit;           /* Execute even in -n */
235     Boolean     errCheck;       /* Check errors */
236     int         reason;         /* Reason for child's death */
237     int         status;         /* Description of child's death */
238     int         cpid;           /* Child actually found */
239     ReturnStatus        rstat;  /* Status of fork */
240     LstNode     *cmdNode;       /* Node where current command is located */
241     char        **av;           /* Argument vector for thing to exec */
242     int         internal;       /* Various values.. */
243     char        *cmd = cmdp;
244     GNode       *gn = gnp;
245     Buffer      *buf;
246
247     /*
248      * Avoid clobbered variable warnings by forcing the compiler
249      * to ``unregister'' variables
250      */
251 #if __GNUC__
252     (void) &av;
253     (void) &errCheck;
254 #endif
255     silent = gn->type & OP_SILENT;
256     errCheck = !(gn->type & OP_IGNORE);
257     doit = FALSE;
258
259     cmdNode = Lst_Member(&gn->commands, cmd);
260
261     buf = Var_Subst(NULL, cmd, gn, FALSE);
262     cmdStart = Buf_GetAll(buf, NULL);
263     Buf_Destroy(buf, FALSE);
264
265     /*
266      * brk_string will return an argv with a NULL in av[0], thus causing
267      * execvp to choke and die horribly. Besides, how can we execute a null
268      * command? In any case, we warn the user that the command expanded to
269      * nothing (is this the right thing to do?).
270      */
271
272     if (*cmdStart == '\0') {
273         free(cmdStart);
274         Error("%s expands to empty string", cmd);
275         return (0);
276     } else {
277         cmd = cmdStart;
278     }
279     Lst_Replace(cmdNode, cmdStart);
280
281     if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
282         Lst_AtEnd(&ENDNode->commands, cmdStart);
283         return (0);
284     } else if (strcmp(cmdStart, "...") == 0) {
285         gn->type |= OP_SAVE_CMDS;
286         return (0);
287     }
288
289     while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) {
290         switch (*cmd) {
291
292           case '@':
293             silent = DEBUG(LOUD) ? FALSE : TRUE;
294             break;
295
296           case '-':
297             errCheck = FALSE;
298             break;
299
300           case '+':
301             doit = TRUE;
302             if (!meta[0])               /* we came here from jobs */
303                 CompatInit();
304             break;
305         }
306         cmd++;
307     }
308
309     while (isspace((unsigned char)*cmd))
310         cmd++;
311
312     /*
313      * Search for meta characters in the command. If there are no meta
314      * characters, there's no need to execute a shell to execute the
315      * command.
316      */
317     for (cp = cmd; !meta[(unsigned char)*cp]; cp++) {
318         continue;
319     }
320
321     /*
322      * Print the command before echoing if we're not supposed to be quiet for
323      * this one. We also print the command if -n given, but not if '+'.
324      */
325     if (!silent || (noExecute && !doit)) {
326         printf("%s\n", cmd);
327         fflush(stdout);
328     }
329
330     /*
331      * If we're not supposed to execute any commands, this is as far as
332      * we go...
333      */
334     if (!doit && noExecute) {
335         return (0);
336     }
337
338     if (*cp != '\0') {
339         /*
340          * If *cp isn't the null character, we hit a "meta" character and
341          * need to pass the command off to the shell. We give the shell the
342          * -e flag as well as -c if it's supposed to exit when it hits an
343          * error.
344          */
345         static char     *shargv[4];
346
347         shargv[0] = shellPath;
348         shargv[1] = (errCheck ? "-ec" : "-c");
349         shargv[2] = cmd;
350         shargv[3] = NULL;
351         av = shargv;
352     } else if ((internal = shellneed(cmd))) {
353         /*
354          * This command must be passed by the shell for other reasons..
355          * or.. possibly not at all.
356          */
357         static char     *shargv[4];
358
359         shargv[0] = shellPath;
360         shargv[1] = (errCheck ? "-ec" : "-c");
361         shargv[2] = cmd;
362         shargv[3] = NULL;
363         av = shargv;
364     } else {
365         int     argc;   /* Number of arguments in av */
366         /*
367          * No meta-characters, so no need to exec a shell. Break the command
368          * into words to form an argument vector we can execute.
369          * brk_string sticks our name in av[0], so we have to
370          * skip over it...
371          */
372         av = brk_string(cmd, &argc, TRUE);
373         av += 1;
374     }
375
376     /*
377      * Fork and execute the single command. If the fork fails, we abort.
378      */
379     cpid = vfork();
380     if (cpid < 0) {
381         Fatal("Could not fork");
382     }
383     if (cpid == 0) {
384         execvp(av[0], av);
385         write(STDERR_FILENO, av[0], strlen(av[0]));
386         write(STDERR_FILENO, ":", 1);
387         write(STDERR_FILENO, strerror(errno), strlen(strerror(errno)));
388         write(STDERR_FILENO, "\n", 1);
389         _exit(1);
390     }
391
392     /*
393      * we need to print out the command associated with this Gnode in
394      * Targ_PrintCmd from Targ_PrintGraph when debugging at level g2,
395      * in main(), Fatal() and DieHorribly(), therefore do not free it
396      * when debugging.
397      */
398     if (!DEBUG(GRAPH2)) {
399         free(cmdStart);
400         Lst_Replace(cmdNode, cmdp);
401     }
402
403     /*
404      * The child is off and running. Now all we can do is wait...
405      */
406     while (1) {
407
408         while ((rstat = wait(&reason)) != cpid) {
409             if (interrupted || (rstat == -1 && errno != EINTR)) {
410                     break;
411             }
412         }
413         if (interrupted)
414             CompatInterrupt(interrupted);
415
416         if (rstat > -1) {
417             if (WIFSTOPPED(reason)) {
418                 status = WSTOPSIG(reason);              /* stopped */
419             } else if (WIFEXITED(reason)) {
420                 status = WEXITSTATUS(reason);           /* exited */
421                 if (status != 0) {
422                     printf("*** Error code %d", status);
423                 }
424             } else {
425                 status = WTERMSIG(reason);              /* signaled */
426                 printf("*** Signal %d", status);
427             }
428
429
430             if (!WIFEXITED(reason) || (status != 0)) {
431                 if (errCheck) {
432                     gn->made = ERROR;
433                     if (keepgoing) {
434                         /*
435                          * Abort the current target, but let others
436                          * continue.
437                          */
438                         printf(" (continuing)\n");
439                     }
440                 } else {
441                     /*
442                      * Continue executing commands for this target.
443                      * If we return 0, this will happen...
444                      */
445                     printf(" (ignored)\n");
446                     status = 0;
447                 }
448             }
449             break;
450         } else {
451             Fatal("error in wait: %d", rstat);
452             /*NOTREACHED*/
453         }
454     }
455
456     return (status);
457 }
458
459 /*-
460  *-----------------------------------------------------------------------
461  * CompatMake --
462  *      Make a target, given the parent, to abort if necessary.
463  *
464  * Results:
465  *      0
466  *
467  * Side Effects:
468  *      If an error is detected and not being ignored, the process exits.
469  *
470  *-----------------------------------------------------------------------
471  */
472 static int
473 CompatMake(void *gnp, void *pgnp)
474 {
475     GNode *gn = gnp;
476     GNode *pgn = pgnp;
477
478     if (gn->type & OP_USE) {
479         Make_HandleUse(gn, pgn);
480     } else if (gn->made == UNMADE) {
481         /*
482          * First mark ourselves to be made, then apply whatever transformations
483          * the suffix module thinks are necessary. Once that's done, we can
484          * descend and make all our children. If any of them has an error
485          * but the -k flag was given, our 'make' field will be set FALSE again.
486          * This is our signal to not attempt to do anything but abort our
487          * parent as well.
488          */
489         gn->make = TRUE;
490         gn->made = BEINGMADE;
491         Suff_FindDeps(gn);
492         Lst_ForEach(&gn->children, CompatMake, gn);
493         if (!gn->make) {
494             gn->made = ABORTED;
495             pgn->make = FALSE;
496             return (0);
497         }
498
499         if (Lst_Member(&gn->iParents, pgn) != NULL) {
500             char *p1;
501             Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
502             free(p1);
503         }
504
505         /*
506          * All the children were made ok. Now cmtime contains the modification
507          * time of the newest child, we need to find out if we exist and when
508          * we were modified last. The criteria for datedness are defined by the
509          * Make_OODate function.
510          */
511         DEBUGF(MAKE, ("Examining %s...", gn->name));
512         if (!Make_OODate(gn)) {
513             gn->made = UPTODATE;
514             DEBUGF(MAKE, ("up-to-date.\n"));
515             return (0);
516         } else {
517             DEBUGF(MAKE, ("out-of-date.\n"));
518         }
519
520         /*
521          * If the user is just seeing if something is out-of-date, exit now
522          * to tell him/her "yes".
523          */
524         if (queryFlag) {
525             exit(1);
526         }
527
528         /*
529          * We need to be re-made. We also have to make sure we've got a $?
530          * variable. To be nice, we also define the $> variable using
531          * Make_DoAllVar().
532          */
533         Make_DoAllVar(gn);
534
535         /*
536          * Alter our type to tell if errors should be ignored or things
537          * should not be printed so Compat_RunCommand knows what to do.
538          */
539         if (Targ_Ignore(gn)) {
540             gn->type |= OP_IGNORE;
541         }
542         if (Targ_Silent(gn)) {
543             gn->type |= OP_SILENT;
544         }
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 (!touchFlag) {
552                 curTarg = gn;
553                 Lst_ForEach(&gn->commands, Compat_RunCommand, (void *)gn);
554                 curTarg = NULL;
555             } else {
556                 Job_Touch(gn, gn->type & OP_SILENT);
557             }
558         } else {
559             gn->made = ERROR;
560         }
561
562         if (gn->made != ERROR) {
563             /*
564              * If the node was made successfully, mark it so, update
565              * its modification time and timestamp all its parents. Note
566              * that for .ZEROTIME targets, the timestamping isn't done.
567              * This is to keep its state from affecting that of its parent.
568              */
569             gn->made = MADE;
570 #ifndef RECHECK
571             /*
572              * We can't re-stat the thing, but we can at least take care of
573              * rules where a target depends on a source that actually creates
574              * the target, but only if it has changed, e.g.
575              *
576              * parse.h : parse.o
577              *
578              * parse.o : parse.y
579              *          yacc -d parse.y
580              *          cc -c y.tab.c
581              *          mv y.tab.o parse.o
582              *          cmp -s y.tab.h parse.h || mv y.tab.h parse.h
583              *
584              * In this case, if the definitions produced by yacc haven't
585              * changed from before, parse.h won't have been updated and
586              * gn->mtime will reflect the current modification time for
587              * parse.h. This is something of a kludge, I admit, but it's a
588              * useful one..
589              *
590              * XXX: People like to use a rule like
591              *
592              * FRC:
593              *
594              * To force things that depend on FRC to be made, so we have to
595              * check for gn->children being empty as well...
596              */
597             if (!Lst_IsEmpty(&gn->commands) || Lst_IsEmpty(&gn->children)) {
598                 gn->mtime = now;
599             }
600 #else
601             /*
602              * This is what Make does and it's actually a good thing, as it
603              * allows rules like
604              *
605              *  cmp -s y.tab.h parse.h || cp y.tab.h parse.h
606              *
607              * to function as intended. Unfortunately, thanks to the stateless
608              * nature of NFS (and the speed of this program), there are times
609              * when the modification time of a file created on a remote
610              * machine will not be modified before the stat() implied by
611              * the Dir_MTime occurs, thus leading us to believe that the file
612              * is unchanged, wreaking havoc with files that depend on this one.
613              *
614              * I have decided it is better to make too much than to make too
615              * little, so this stuff is commented out unless you're sure it's
616              * ok.
617              * -- ardeb 1/12/88
618              */
619             if (noExecute || Dir_MTime(gn) == 0) {
620                 gn->mtime = now;
621             }
622             if (gn->cmtime > gn->mtime)
623                 gn->mtime = gn->cmtime;
624             DEBUGF(MAKE, ("update time: %s\n", Targ_FmtTime(gn->mtime)));
625 #endif
626             if (!(gn->type & OP_EXEC)) {
627                 pgn->childMade = TRUE;
628                 Make_TimeStamp(pgn, gn);
629             }
630         } else if (keepgoing) {
631             pgn->make = FALSE;
632         } else {
633             char *p1;
634
635             printf("\n\nStop in %s.\n", Var_Value(".CURDIR", gn, &p1));
636             free(p1);
637             exit(1);
638         }
639     } else if (gn->made == ERROR) {
640         /*
641          * Already had an error when making this beastie. Tell the parent
642          * to abort.
643          */
644         pgn->make = FALSE;
645     } else {
646         if (Lst_Member(&gn->iParents, pgn) != NULL) {
647             char *p1;
648             Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
649             free(p1);
650         }
651         switch(gn->made) {
652             case BEINGMADE:
653                 Error("Graph cycles through %s\n", gn->name);
654                 gn->made = ERROR;
655                 pgn->make = FALSE;
656                 break;
657             case MADE:
658                 if ((gn->type & OP_EXEC) == 0) {
659                     pgn->childMade = TRUE;
660                     Make_TimeStamp(pgn, gn);
661                 }
662                 break;
663             case UPTODATE:
664                 if ((gn->type & OP_EXEC) == 0) {
665                     Make_TimeStamp(pgn, gn);
666                 }
667                 break;
668             default:
669                 break;
670         }
671     }
672
673     return (0);
674 }
675
676 /*-
677  *-----------------------------------------------------------------------
678  * Compat_Run --
679  *      Start making again, given a list of target nodes.
680  *
681  * Results:
682  *      None.
683  *
684  * Side Effects:
685  *      Guess what?
686  *
687  *-----------------------------------------------------------------------
688  */
689 void
690 Compat_Run(Lst *targs)
691 {
692     GNode         *gn = NULL;/* Current root target */
693     int           errors;   /* Number of targets not remade due to errors */
694
695     CompatInit();
696     Shell_Init();               /* Set up shell. */
697
698     if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
699         signal(SIGINT, CompatCatchSig);
700     }
701     if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
702         signal(SIGTERM, CompatCatchSig);
703     }
704     if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
705         signal(SIGHUP, CompatCatchSig);
706     }
707     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
708         signal(SIGQUIT, CompatCatchSig);
709     }
710
711     ENDNode = Targ_FindNode(".END", TARG_CREATE);
712     /*
713      * If the user has defined a .BEGIN target, execute the commands attached
714      * to it.
715      */
716     if (!queryFlag) {
717         gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);
718         if (gn != NULL) {
719             Lst_ForEach(&gn->commands, Compat_RunCommand, gn);
720             if (gn->made == ERROR) {
721                 printf("\n\nStop.\n");
722                 exit(1);
723             }
724         }
725     }
726
727     /*
728      * For each entry in the list of targets to create, call CompatMake on
729      * it to create the thing. CompatMake will leave the 'made' field of gn
730      * in one of several states:
731      *      UPTODATE        gn was already up-to-date
732      *      MADE            gn was recreated successfully
733      *      ERROR           An error occurred while gn was being created
734      *      ABORTED         gn was not remade because one of its inferiors
735      *                      could not be made due to errors.
736      */
737     errors = 0;
738     while (!Lst_IsEmpty(targs)) {
739         gn = Lst_DeQueue(targs);
740         CompatMake(gn, gn);
741
742         if (gn->made == UPTODATE) {
743             printf("`%s' is up to date.\n", gn->name);
744         } else if (gn->made == ABORTED) {
745             printf("`%s' not remade because of errors.\n", gn->name);
746             errors += 1;
747         }
748     }
749
750     /*
751      * If the user has defined a .END target, run its commands.
752      */
753     if (errors == 0) {
754         Lst_ForEach(&ENDNode->commands, Compat_RunCommand, gn);
755     }
756 }