[scripting] Modified if-else-fi branches
[people/lynusvaz/gpxe.git] / src / core / exec.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 FILE_LICENCE ( GPL2_OR_LATER );
20
21 #include <stdint.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <unistd.h>
27 #include <getopt.h>
28 #include <errno.h>
29 #include <assert.h>
30 #include <gpxe/command.h>
31 #include <gpxe/parse.h>
32 #include <gpxe/gen_stack.h>
33
34 /** @file
35  *
36  * Command execution
37  *
38  */
39
40 /* Avoid dragging in getopt.o unless a command really uses it */
41 int optind;
42 int nextchar;
43
44 extern struct generic_stack if_stack;
45
46 /**
47  * Execute command
48  *
49  * @v command           Command name
50  * @v argv              Argument list
51  * @ret rc              Command exit status
52  *
53  * Execute the named command.  Unlike a traditional POSIX execv(),
54  * this function returns the exit status of the command.
55  */
56 int execv ( const char *command, char * const argv[] ) {
57         struct command *cmd;
58         int argc;
59
60         /* Count number of arguments */
61         for ( argc = 0 ; argv[argc] ; argc++ ) {}
62
63         /* Sanity checks */
64         if ( ! command ) {
65                 DBG ( "No command\n" );
66                 return -EINVAL;
67         }
68         if ( ! argc ) {
69                 DBG ( "%s: empty argument list\n", command );
70                 return -EINVAL;
71         }
72
73         /* Reset getopt() library ready for use by the command.  This
74          * is an artefact of the POSIX getopt() API within the context
75          * of Etherboot; see the documentation for reset_getopt() for
76          * details.
77          */
78         reset_getopt();
79
80         /* Hand off to command implementation */
81         for_each_table_entry ( cmd, COMMANDS ) {
82                 if ( strcmp ( command, cmd->name ) == 0 ) {
83                         if ( TOP_GEN_STACK_INT ( &if_stack ) || !strcmp ( cmd->name, "if" ) || !strcmp ( cmd->name, "fi" ) || !strcmp ( cmd->name, "else" ) )
84                                 return cmd->exec ( argc, ( char ** ) argv );
85                         else
86                                 return 0;
87                 }
88         }
89
90         printf ( "%s: command not found\n", command );
91         return -ENOEXEC;
92 }
93
94 static int expand_command ( const char *command, struct generic_stack *argv_stack ) {
95         char *head, *end;
96         char *nstring;
97         int success;
98         int argc;
99         
100         struct string expcmd = { .value = NULL };
101         
102         argc = 0;
103         init_generic_stack ( argv_stack, sizeof ( int ) );
104         
105         if ( !stringcpy ( &expcmd, command ) ) {
106                 argc = -ENOMEM;
107                 return argc;
108         }
109         head = expcmd.value;
110         
111         /* Expand while expansions remain */
112         while ( *head ) {
113                 while ( isspace ( *head ) ) {
114                         *head = 0;
115                         head++;
116                 }
117                 if ( *head == '#' ) { /* Comment is a new word that starts with # */
118                         break;
119                 }
120                 if ( !stringcpy ( &expcmd, head ) ) {
121                         argc = -ENOMEM;
122                         break;
123                 }
124                 head = expcmd.value;
125                 nstring = expand_string ( &expcmd, &head, &end, table, 6, 0, &success );
126                 if ( nstring ) {
127                         if ( success ) {
128                                 argc++;
129                                 push_generic_stack ( argv_stack, &expcmd.value, 0 );
130                                 expcmd.value = NULL;
131                                 stringcpy ( &expcmd, end );
132                                 *end = 0;
133                                 /*
134                                 So if the command is: word1 word2 word3
135                                 argv_stack:     word1\0word2 word3
136                                                         word2\0word3
137                                                         word3
138                                 */
139                         }
140                 } else {
141                         argc = -ENOMEM;
142                         break;
143                 }
144                 head = expcmd.value;
145         }
146         free_string ( &expcmd );
147         return argc;
148 }
149
150 /**
151  * Execute command line
152  *
153  * @v command           Command line
154  * @ret rc              Command exit status
155  *
156  * Execute the named command and arguments.
157  */
158 int system ( const char *command ) {
159         int argc;
160         int rc = 0;
161         struct generic_stack argv_stack;
162
163         argc = expand_command ( command, &argv_stack );
164         if ( argc < 0 ) {
165                 rc = argc;
166         } else {
167                 char **argv;
168                 if ( ! push_generic_stack ( &argv_stack, NULL, 0 ) ) {
169                         argv = ( char ** ) argv_stack.ptr;
170                         argv[argc] = NULL;
171                         if ( argc > 0 )
172                                 rc = execv ( argv[0], argv );
173                 }
174         }
175         
176         free_generic_stack ( &argv_stack, 1 );
177         return rc;
178 }
179
180 /**
181  * The "echo" command
182  *
183  * @v argc              Argument count
184  * @v argv              Argument list
185  * @ret rc              Exit code
186  */
187 static int echo_exec ( int argc, char **argv ) {
188         int i;
189
190         for ( i = 1 ; i < argc ; i++ ) {
191                 printf ( "%s%s", ( ( i == 1 ) ? "" : " " ), argv[i] );
192         }
193         printf ( "\n" );
194         return 0;
195 }
196
197 /** "echo" command */
198 struct command echo_command __command = {
199         .name = "echo",
200         .exec = echo_exec,
201 };