[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Help-bash] Query on _evalfile function in bash-4.3/builtins/evalfil
From: |
Eduardo A . Bustamante López |
Subject: |
Re: [Help-bash] Query on _evalfile function in bash-4.3/builtins/evalfile.c |
Date: |
Thu, 19 Feb 2015 22:50:59 -0600 |
User-agent: |
Mutt/1.5.23 (2014-03-12) |
On Fri, Feb 20, 2015 at 08:15:02AM +0530, Vijay Karode wrote:
> Hi All,
>
> BASH is a wonderful shell and you guys are doing a wonderful job
> maintaining it.Hats Off to you all.
Hey! You'd be amazed to know that Chet Ramey manages to maintain bash alone,
hats off to him.
>
> From past few days I was getting curious as of how the shell actually
> works.So,started digging into the source code.Shell obviously does loads of
> things,but one of the things which it does after login program has given
> the control is to load the user specific profile file like
> .bash_profile.This is obviously not done via a fork and exec as we want the
> env to be set up in the parent and not in subshell.
> The builtin source command also does the same thing.It affects the current
> shell.
Nice to know :-)
Ok, I'll try my best at explaining. I have to confess that I'm not that
familiar with the codebase:
$ cat -n builtins/evalfile.c
1 /* evalfile.c - read and evaluate commands from a file or file
descriptor */
2
3 /* Copyright (C) 1996-2009 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Bash is distributed in the hope that it will be useful,
13 but 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 License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19 */
20
[...]
77
78 static int
79 _evalfile (filename, flags)
80 const char *filename;
81 int flags;
82 {
[...]
These variables are automatically setup by bash to be used in your
eval'ed file.
103 #if defined (ARRAY_VARS)
104 GET_ARRAY_FROM_VAR ("FUNCNAME", funcname_v, funcname_a);
105 GET_ARRAY_FROM_VAR ("BASH_SOURCE", bash_source_v, bash_source_a);
106 GET_ARRAY_FROM_VAR ("BASH_LINENO", bash_lineno_v, bash_lineno_a);
107 # if defined (DEBUGGER)
108 GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
109 GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
110 # endif
111 #endif
112
Open the file and check that it's a regular file
113 fd = open (filename, O_RDONLY);
114
115 if (fd < 0 || (fstat (fd, &finfo) == -1))
116 {
117 i = errno;
118 if (fd >= 0)
119 close (fd);
120 errno = i;
121
122 file_error_and_exit:
123 if (((flags & FEVAL_ENOENTOK) == 0) || errno != ENOENT)
124 file_error (filename);
125
126 if (flags & FEVAL_LONGJMP)
127 {
128 last_command_exit_value = 1;
129 jump_to_top_level (EXITPROG);
130 }
131
132 return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE
133 : ((errno == ENOENT) ? 0 : -1));
134 }
135
136 errfunc = ((flags & FEVAL_BUILTIN) ? builtin_error : internal_error);
137
138 if (S_ISDIR (finfo.st_mode))
139 {
140 (*errfunc) (_("%s: is a directory"), filename);
141 close (fd);
142 return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1);
143 }
144 else if ((flags & FEVAL_REGFILE) && S_ISREG (finfo.st_mode) == 0)
145 {
146 (*errfunc) (_("%s: not a regular file"), filename);
147 close (fd);
148 return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1);
149 }
150
Find out the size of the file, and store the whole file in memory.
151 file_size = (size_t)finfo.st_size;
152 /* Check for overflow with large files. */
153 if (file_size != finfo.st_size || file_size + 1 < file_size)
154 {
155 (*errfunc) (_("%s: file is too large"), filename);
156 close (fd);
157 return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1);
158 }
159
160 if (S_ISREG (finfo.st_mode) && file_size <= SSIZE_MAX)
161 {
162 string = (char *)xmalloc (1 + file_size);
163 nr = read (fd, string, file_size);
164 if (nr >= 0)
165 string[nr] = '\0';
166 }
167 else
168 nr = zmapfd (fd, &string, 0);
169
170 return_val = errno;
171 close (fd);
172 errno = return_val;
173
174 if (nr < 0) /* XXX was != file_size, not < 0 */
175 {
176 free (string);
177 goto file_error_and_exit;
178 }
179
180 if (nr == 0)
181 {
182 free (string);
183 return ((flags & FEVAL_BUILTIN) ? EXECUTION_SUCCESS : 1);
184 }
check_binary_file() tries to determine if the file is binary, by
checking for \0 bytes, IIRC
186 if ((flags & FEVAL_CHECKBINARY) &&
187 check_binary_file (string, (nr > 80) ? 80 : nr))
188 {
189 free (string);
190 (*errfunc) (_("%s: cannot execute binary file"), filename);
191 return ((flags & FEVAL_BUILTIN) ? EX_BINARY_FILE : -1);
192 }
193
194 i = strlen (string);
195 if (i < nr)
196 {
197 for (nnull = i = 0; i < nr; i++)
198 if (string[i] == '\0')
199 {
200 memmove (string+i, string+i+1, nr - i);
201 nr--;
202 /* Even if the `check binary' flag is not set, we want to
avoid
203 sourcing files with more than 256 null characters -- that
204 probably indicates a binary file. */
205 if ((flags & FEVAL_BUILTIN) && ++nnull > 256)
206 {
207 free (string);
208 (*errfunc) (_("%s: cannot execute binary file"),
filename);
209 return ((flags & FEVAL_BUILTIN) ? EX_BINARY_FILE : -1);
210 }
211 }
212 }
TBH I still don't understand the longjmp stuff, I think it's a kind of
goto but between very different parts of the code.
214 if (flags & FEVAL_UNWINDPROT)
215 {
216 begin_unwind_frame ("_evalfile");
217
218 unwind_protect_int (return_catch_flag);
219 unwind_protect_jmp_buf (return_catch);
220 if (flags & FEVAL_NONINT)
221 unwind_protect_int (interactive);
222 unwind_protect_int (sourcelevel);
223 }
224 else
225 {
226 COPY_PROCENV (return_catch, old_return_catch);
227 if (flags & FEVAL_NONINT)
228 old_interactive = interactive;
229 }
230
231 if (flags & FEVAL_NONINT)
232 interactive = 0;
233
234 return_catch_flag++;
235 sourcelevel++;
Sets the values of the special variables
237 #if defined (ARRAY_VARS)
238 array_push (bash_source_a, (char *)filename);
239 t = itos (executing_line_number ());
240 array_push (bash_lineno_a, t);
241 free (t);
242 array_push (funcname_a, "source"); /* not exactly right */
243 # if defined (DEBUGGER)
244 /* Have to figure out a better way to do this when `source' is
supplied
245 arguments */
246 if ((flags & FEVAL_NOPUSHARGS) == 0)
247 {
248 array_push (bash_argv_a, (char *)filename);
249 tt[0] = '1'; tt[1] = '\0';
250 array_push (bash_argc_a, tt);
251 }
252 # endif
253 #endif
254
Here it unleashes the parser beast.
271 else
272 result = parse_and_execute (string, filename, pflags);
The parser stuff is in parse.y, that's what has the bash grammar and all the
code to handle special cases while parsing. It is very complex. It's meant to
be used with the yacc tool.
Oh wait, actually, the code that calls the parser is: builtins/evalstring.c
$ cat -n builtins/evalstring.c
1 /* evalstring.c - evaluate a string as one or more shell commands. */
2
3 /* Copyright (C) 1996-2012 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Bash is distributed in the hope that it will be useful,
13 but 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 License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19 */
20
189 int
190 parse_and_execute (string, from_file, flags)
191 char *string;
192 const char *from_file;
193 int flags;
194 {
notice the function parse_command(), this one is in eval.c
298
299 if (parse_command () == 0)
300 {
301 if ((flags & SEVAL_PARSEONLY) || (interactive_shell == 0 &&
read_but_dont_execute))
302 {
303 last_result = EXECUTION_SUCCESS;
304 dispose_command (global_command);
305 global_command = (COMMAND *)NULL;
306 }
307 else if (command = global_command)
308 {
309 struct fd_bitmap *bitmap;
[...]
1 /* eval.c -- reading and evaluating commands. */
2
3 /* Copyright (C) 1996-2011 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Bash is distributed in the hope that it will be useful,
13 but 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 License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19 */
[...]
Ok, this one actually calls yyparse(), which is the YACC generated parser.
211 /* Call the YACC-generated parser and return the status of the parse.
212 Input is read from the current input stream (bash_input). yyparse
213 leaves the parsed command in the global variable GLOBAL_COMMAND.
214 This is where PROMPT_COMMAND is executed. */
215 int
216 parse_command ()
217 {
218 int r;
219 char *command_to_execute;
220
221 need_here_doc = 0;
222 run_pending_traps ();
223
224 /* Allow the execution of a random command just before the printing
225 of each primary prompt. If the shell variable PROMPT_COMMAND
226 is set then the value of it is the command to execute. */
227 if (interactive && bash_input.type != st_string)
228 {
229 command_to_execute = get_string_value ("PROMPT_COMMAND");
230 if (command_to_execute)
231 execute_variable_command (command_to_execute, "PROMPT_COMMAND");
232
233 if (running_under_emacs == 2)
234 send_pwd_to_eterm (); /* Yuck */
235 }
236
237 current_command_line_count = 0;
238 r = yyparse ();
239
240 if (need_here_doc)
241 gather_here_documents ();
242
243 return (r);
244 }
That's a brief overview of how it works. Hope it helps.