1 /*
2  *                     OpenBIOS - free your system!
3  *                         ( FCode tokenizer )
4  *
5  *  This program is part of a free implementation of the IEEE 1275-1994
6  *  Standard for Boot (Initialization Configuration) Firmware.
7  *
8  *  Copyright (C) 2001-2005 Stefan Reinauer, <stepan@openbios.org>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; version 2 of the License.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
22  *
23  */
24 
25 /* **************************************************************************
26  *
27  *      Support routines for managing device-node vocabularies
28  *
29  *      (C) Copyright 2005 IBM Corporation.  All Rights Reserved.
30  *      Module Author:  David L. Paktor    dlpaktor@us.ibm.com
31  *
32  **************************************************************************** */
33 
34 /* **************************************************************************
35  *
36  *      The vocabulary that is created for a device-node must not remain
37  *          available outside of that node.  Also, nodes may be nested,
38  *          child within parent.
39  *      An attempt within a child-node to access directly a method defined
40  *          in the parent must be flagged as an error.  (Consider what would
41  *          happen if the method in the parent-node used instance data, and
42  *          the child-node has an instance of its own.)
43  *      The correct way is to invoke the method via "$call-parent" or the like.
44  *
45  *      We will, however, allow the user to specify a group of exceptions,
46  *          words whose scope will be "global" within the tokenization.
47  *          When "global" scope is initiated, definitions will be made to
48  *          the "core" vocabulary until "device" scope is resumed.
49  *          That will (mostly) all be handled in  dictionary.c
50  *
51  **************************************************************************** */
52 
53 
54 /* **************************************************************************
55  *
56  *      Functions Exported:
57  *          new_device_vocab        Create the new device-node's data-structure
58  *          delete_device_vocab     Remove device-node's data-structure
59  *          finish_device_vocab     Remove struct and give messages when
60  *                                      device is "finish"ed.
61  *          exists_in_ancestor      Issue a Message if the given word exists
62  *                                      in an ancestor of the current dev-node.
63  *
64  **************************************************************************** */
65 
66 /* **************************************************************************
67  *
68  *      Still to be done:
69  *          Add a pair of fields to the data-structure for the Input File and
70  *              Line Number where "finish-device" occurred.  When a device-node
71  *              is "finish"ed, do not delete it, but instead fill in those
72  *              fields and the move the node to a separate linked-list.
73  *          When looking whether a word exists in an ancestor-node, also
74  *              check whether it was in a device-node that was finished and
75  *              print both where it was started and where it was finished.
76  *
77  **************************************************************************** */
78 
79 
80 
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <errno.h>
85 
86 #include "devnode.h"
87 #include "errhandler.h"
88 #include "scanner.h"
89 #include "vocabfuncts.h"
90 #include "flowcontrol.h"
91 #include "stream.h"
92 #include "ticvocab.h"
93 
94 
95 /* **************************************************************************
96  *
97  *      Tokenization starts with an implicit "new-device" in effect.
98  *          The top-level device-node is never removed.
99  *
100  *      Initialize it here
101  *
102  **************************************************************************** */
103 char default_top_dev_ifile_name[] = "Start of tokenization";
104 
105 static device_node_t top_level_dev_node = {
106      	NULL ,                          /*  parent_node    */
107 	default_top_dev_ifile_name ,	/*  ifile_name.
108 					 *     Something to show, Just In Case
109 					 */
110 	0 ,                             /*  line_no        */
111 	NULL ,                          /*  tokens_vocab   */
112 };
113 
114 /* **************************************************************************
115  *
116  *          Global Variables Exported.
117  *                                  Pointers to:
118  *     current_device_node      data-structure of current device-node
119  *     current_definitions      vocab into which to add def'ns.
120  *
121  **************************************************************************** */
122 
123 device_node_t *current_device_node = &top_level_dev_node;
124 tic_hdr_t **current_definitions = &(top_level_dev_node.tokens_vocab);
125 
126 
127 /* **************************************************************************
128  *
129  *          Internal Static Variables
130  *               These are used to support the routines  in_what_node()
131  *                   and  show_node_start() , which are used to facilitate
132  *                   certain kinds of Messaging, as described later.
133  *
134  *     in_what_buffr       Buffer for the  in_what_node()  string
135  *     show_where          TRUE if the string needs to be followed-up
136  *     show_which          TRUE if follow-up should be  just_where_started()
137  *                             rather than  just_started_at()
138  *     in_what_line        Line Number to use in the follow-up
139  *     in_what_file        File Name to use in the follow-up
140  *
141  **************************************************************************** */
142 
143 static char in_what_buffr[50];   /*  Ought to be more than enough.  */
144 static bool show_where = FALSE;
145 static bool show_which;
146 static int in_what_line;
147 static char *in_what_file;
148 
149 
150 /* **************************************************************************
151  *
152  *      Function name:  dev_vocab_control_struct_check
153  *      Synopsis:       Issue Warnings for unresolved flow-control constructs
154  *                          at start or end of a device-node.
155  *
156  *      Inputs:
157  *         Parameters:                     NONE
158  *         Global Variables:
159  *             statbuf                     The command being processed.
160  *
161  *      Outputs:
162  *         Returned Value:                 NONE
163  *         Printout:
164  *             Handled by  announce_control_structs() routine
165  *
166  *      Error Detection:
167  *          Handled by  announce_control_structs()  routine
168  *
169  *      Process Explanation:
170  *          Set up a buffer with the error message, based on  statbuf
171  *              and pass it to  announce_control_structs()
172  *          Release it when done.
173  *
174  **************************************************************************** */
175 
dev_vocab_control_struct_check(void)176 static void dev_vocab_control_struct_check( void)
177 {
178     char *ccs_messg;
179 
180     ccs_messg = safe_malloc(strlen(statbuf) + 32,
181         "Device-Node control-structure check");
182 
183     strcpy( ccs_messg, statbuf );
184     strupr( ccs_messg);
185     strcat( ccs_messg, " encountered");
186     announce_control_structs( WARNING, ccs_messg, 0 );
187     free( ccs_messg);
188 }
189 
190 
191 
192 /* **************************************************************************
193  *
194  *      Function name:  new_device_vocab
195  *      Synopsis:       Create and initialize the data-structure for a
196  *                      new device-node when a "new-device" is created,
197  *                      with messages as appropriate.
198  *
199  *      Inputs:
200  *         Parameters:                     NONE
201  *         Global Variables:
202  *             statbuf                     The word that was just read.
203  *             iname                       Current Input-File Name
204  *             lineno                      Current line-number
205  *
206  *      Outputs:
207  *         Returned Value:                 NONE
208  *         Global Variables:
209  *             current_device_node         Will point to the new data-structure
210  *         Memory Allocated
211  *             Space for the new  device_node_t  data-structure
212  *             Space for a copy of the current input file name
213  *         When Freed?
214  *             By delete_device_vocab(), when the device-node is "finish"ed.
215  *         Printout:
216  *             Advisory message.
217  *
218  *      Error Detection:
219  *          In immediate-execution mode, Control Structures that have not
220  *              been completed are questionable;  Issue WARNINGS via the
221  *               dev_vocab_control_struct_check()  routine.
222  *
223  *      Process Explanation:
224  *          This routine is called when "new-device" is invoked, but only
225  *              if we are in immediate-execution mode.
226  *          Later on, in ERROR- or INFOrmative messages, we will want to
227  *              be able to refer to the file and line-number in which this
228  *              was encountered, so we include them in the structure.
229  *
230  **************************************************************************** */
231 
new_device_vocab(void)232 void new_device_vocab( void )
233 {
234     device_node_t *new_node_data;
235 
236     dev_vocab_control_struct_check();
237 
238     /*  Advisory message will mention previous device-node
239      *      if there was one.  Either way starts out the same:
240      */
241 #define NEW_DEV_MSG_START  "Encountered %s.  Starting new device-node."
242 
243     if ( current_device_node == &top_level_dev_node )
244     {
245 	tokenization_error(INFO, NEW_DEV_MSG_START "\n", statbuf );
246     }else{
247 	tokenization_error(INFO, NEW_DEV_MSG_START
248 	    "  Suspending definitions of parent-device node", statbuf );
249 	started_at( current_device_node->ifile_name,
250 	     current_device_node->line_no );
251     }
252 
253     /*  Now to business...   */
254     new_node_data = safe_malloc( sizeof(device_node_t),
255         "creating new-device vocab data" );
256     new_node_data->parent_node = current_device_node;
257     new_node_data->ifile_name = strdup(iname);
258     new_node_data->line_no = lineno;
259     new_node_data->tokens_vocab = NULL;
260 
261     current_device_node = new_node_data;
262 
263     current_definitions = &(current_device_node->tokens_vocab);
264 }
265 
266 
267 /* **************************************************************************
268  *
269  *      Function name:  delete_device_vocab
270  *      Synopsis:       Remove the vocabularies of the current device-node,
271  *                          along with its data-structure, when the device
272  *                          is "finish"ed; do not print messages.
273  *                      Do not remove the top-level device-node data-struct.
274  *
275  *      Associated FORTH words:              FINISH_DEVICE  (interpretive state)
276  *                                           END0  END1
277  *      Associated Tokenizer directives:     RESET-SYMBOLS  (in "Normal" mode)
278  *                                           FCODE-END
279  *
280  *      Inputs:
281  *         Parameters:                    NONE
282  *         Global Variables:
283  *             current_device_node        Points to current device's struct
284  *                                            Leads to chain of dev-node structs
285  *
286  *      Outputs:
287  *         Returned Value:
288  *         Global Variables:
289  *             current_device_node        Parent-device's struct becomes current
290  *         Memory Freed
291  *             All that was allocated for the tokens and the definers
292  *                 vocabs in the current device-node
293  *             The copy of the input file name, except the top-level
294  *             The current_device_node data-structure, except the top-level
295  *
296  **************************************************************************** */
297 
delete_device_vocab(void)298 void delete_device_vocab( void )
299 {
300     reset_tic_vocab( current_definitions, NULL );
301 
302     if ( current_device_node != &top_level_dev_node )
303     {
304 	device_node_t *temp_node = current_device_node;
305 	current_device_node = current_device_node->parent_node;
306 	free( temp_node->ifile_name );
307 	free(temp_node);
308     }
309 
310     current_definitions = &(current_device_node->tokens_vocab);
311 }
312 
313 /* **************************************************************************
314  *
315  *      Function name:  finish_device_vocab
316  *      Synopsis:       Remove the device-node data-structure and all its
317  *                          vocabularies when the device is "finish"ed,
318  *                          with appropriate messages.
319  *                      Do not remove the top-level device node data-struct.
320  *
321  *      Associated FORTH word:                 FINISH_DEVICE
322  *
323  *      Inputs:
324  *         Parameters:                    NONE
325  *         Global Variables:
326  *             current_device_node        Current device's struct pointer
327  *
328  *      Outputs:
329  *         Returned Value:                NONE
330  *         Global Variables:
331  *             current_device_node        Parent-device's struct becomes current
332  *         Printout:
333  *             Advisory message.
334  *
335  *      Error Detection:
336  *          If current_device_node is already pointing at the top-level
337  *              device node, it means there was no corresponding NEW-DEVICE
338  *              Issue an ERROR.
339  *          In immediate-execution mode, Control Structures that have not
340  *              been completed are questionable;  Issue WARNINGS via the
341  *               dev_vocab_control_struct_check()  routine.
342  *
343  *      Process Explanation:
344  *          This routine is called when "finish-device" is invoked, but only
345  *              if we are in immediate-execution mode.
346  *
347  **************************************************************************** */
348 
finish_device_vocab(void)349 void finish_device_vocab( void )
350 {
351     bool at_top_level;
352 
353     dev_vocab_control_struct_check();
354 
355     /*   We never remove the top-level device-node vocabulary,
356      *       so we need to test whether we're about to.
357      */
358 
359     at_top_level = BOOLVAL( current_device_node == &top_level_dev_node );
360     if ( at_top_level )
361     {
362         tokenization_error( TKERROR,
363 	    "Encountered %s without corresponding NEW-DEVICE.  "
364 	    "Resetting definitions since start of tokenization.\n",
365 		statbuf );
366     }else{
367 	tokenization_error(INFO,
368 	    "Encountered %s.  Resetting definitions of device node",
369 		statbuf );
370 	started_at( current_device_node->ifile_name,
371 	     current_device_node->line_no );
372     }
373 
374     /*  Now to business...   */
375     delete_device_vocab();
376 
377     /*   Did we just get to the top-level device-node vocabulary
378      *       when we weren't before?
379      */
380     if ( INVERSE(at_top_level) )
381     {
382 	if ( current_device_node == &top_level_dev_node )
383 	{
384 	    tokenization_error(INFO,
385 		"Resuming definitions since start of tokenization.\n" );
386 	}else{
387 	    tokenization_error(INFO,
388 		"Resuming definitions of parent device-node" );
389 	    started_at( current_device_node->ifile_name,
390 		 current_device_node->line_no );
391 	}
392     }
393 }
394 
395 
396 /* **************************************************************************
397  *
398  *      Function name:  in_what_node
399  *      Synopsis:       Format a string for use in a Message that might
400  *                          identify the start of the given device-node.
401  *
402  *      Inputs:
403  *         Parameters:
404  *             the_node               The device-node vocabulary about which
405  *                                        to construct the identifying phrase.
406  *         Local Static Variables:
407  *             in_what_buffr          Buffer in which to format the string.
408  *         Global Variables:
409  *             current_definitions    Device-node vocabulary currently
410  *                                        in effect.
411  *
412  *      Outputs:
413  *         Returned Value:           Pointer to buffer w/ formatted string
414  *         Local Static Variables:
415  *             in_what_buffr         Will contain the formatted string.
416  *             show_where            TRUE if the string needs to be followed-up
417  *                                        (i.e., did not contain a terminating
418  *                                        new-line) by  just_where_started()
419  *                                        or by  just_started_at()
420  *             show_which            TRUE if the follow-up call should be
421  *                                       to  just_where_started()  rather
422  *                                       than to  just_started_at()
423  *             in_what_line          Copy of line_no field from the_node
424  *             in_what_file          Copy of ifile_name field from the_node
425  *
426  *      Process Explanation:
427  *          Calling routine must ascertain that Global-scope is not in effect.
428  *          The returned phrase can be used as a string argument in a Message.
429  *          Set  show_where  TRUE if the_node->line_no is non-zero.
430  *          Set  show_which  TRUE if the_node is either the Current or the
431  *              Top-Level device-node
432  *          If the originating line-number in the given Node structure is zero,
433  *              the returned phrase will contain a terminating new-line.
434  *              (This only happens if the given Node is the top-level Node,
435  *              and it's the Current Node, and the "official" starting-point
436  *              hasn't yet been established by an "FCode-Starter" such as
437  *               FCODE-VERSION2 .  Once that command has been given, even
438  *              definitions that were made prior to it belong to the Node
439  *              that started there.)
440  *          Otherwise,  show_where  is returned TRUE, and  show_which  becomes
441  *              relevant.  If the given node is the Current or the Top-Level
442  *              node, text about the originating file-name and line-number
443  *              merely describes a node that is already uniquely identified,
444  *              so the message appended to the buffer will have the phrase
445  *              "which began" (which introduces what is known in grammar as
446  *              an Appositive Subordinate Clause) and  show_which  will be
447  *              returned TRUE.  If the given node is not uniquely identifiable
448  *              without the file- and line- phrase, then the Subordinate Clause
449  *              is Indicative, and should be introduced with "that" (and no
450  *              comma); in that case, we will return  show_which  as FALSE.
451  *          After the calling routine displays the message in which the
452  *              returned phrase is used, it must call  show_node_start()
453  *              to display the followe-up message, if any.
454  *
455  **************************************************************************** */
456 
in_what_node(device_node_t * the_node)457 char *in_what_node(device_node_t *the_node)
458 {
459     bool top_node    = BOOLVAL( the_node == &top_level_dev_node);
460     bool curr_node   = BOOLVAL( the_node == current_device_node);
461     bool known_node  = BOOLVAL( top_node || curr_node );
462     bool no_line     = BOOLVAL( the_node->line_no == 0);
463 
464     show_where   = INVERSE( no_line  );
465     show_which   = known_node;
466     in_what_line = the_node->line_no;
467     in_what_file = the_node->ifile_name;
468 
469     sprintf( in_what_buffr, "in the%s device-node%s",
470 	INVERSE( known_node )  ? ""
471 	        :  top_node    ?    " top-level"   :  " current" ,
472 
473 	no_line                ?  ".\n"
474 	        :  known_node  ?  ", which began"  :   ""         );
475 
476 
477     return( in_what_buffr);
478 }
479 
480 
481 /* **************************************************************************
482  *
483  *      Function name:  show_node_start
484  *      Synopsis:       Follow-up to the  in_what_node()  call.  Print out,
485  *                          if applicable, the text about the originating
486  *                          file-name and line-number
487  *
488  *      Inputs:
489  *         Parameters:             NONE
490  *         Local Static Variables:
491  *             show_where          Nothing to do if not TRUE
492  *             show_which          TRUE if should call  just_where_started()
493  *                                     rather than  just_started_at()
494  *             in_what_line        Line Number to use in the follow-up
495  *             in_what_file        File Name to use in the follow-up
496  *
497  *      Outputs:
498  *         Returned Value:         NONE
499  *         Local Static Variables:
500  *             show_where          Force to FALSE
501  *         Printout:
502  *             Follow-up to  the in_what_node() call.  Applicable text
503  *                 about the originating file-name and line-number.
504  *
505  *      Process Explanation:
506  *          By forcing  show_where  to FALSE after this is called, we
507  *              can safely allow routines that might or might not have
508  *              called  in_what_node()  to call this routine, without
509  *              needing any additional "bookkeeping".
510  *
511  **************************************************************************** */
512 
show_node_start(void)513 void show_node_start( void)
514 {
515     if ( show_where)
516     {
517 	if ( show_which )
518 	{
519 	    just_where_started( in_what_file, in_what_line);
520 	}else{
521 	    just_started_at( in_what_file, in_what_line);
522 	}
523 	show_where = FALSE;
524     }
525 }
526 
527 
528 
529 /* **************************************************************************
530  *
531  *      Function name:  exists_in_ancestor
532  *      Synopsis:       Issue a Message and return an indication if
533  *                          the given word exists in an ancestor of
534  *                          the current device-node.
535  *                      Used for additional error-message information.
536  *
537  *
538  *      Inputs:
539  *         Parameters:
540  *             m_name                   "Method" name
541  *         Global Variables:
542  *             current_device_node      Leads to chain of dev-node data-structs
543  *             scope_is_global          TRUE if "global" scope is in effect
544  *
545  *      Outputs:
546  *         Returned Value:              TRUE if word found
547  *         Printout:
548  *             If  m_name  exists in an ancestor-node, print an ADVISORY
549  *                 giving the location where the ancestor originated.
550  *
551  *      Error Detection:
552  *          None here.  Calling routine detected error; see below.
553  *
554  *      Process Explanation:
555  *          This routine was called as the result of detecting an error:
556  *              viz.,  m_name  was not found in either the current node
557  *              or the base vocabulary.  (Except:  If "global" scope is
558  *              in effect, we didn't search the current device-node).
559  *
560  **************************************************************************** */
561 
exists_in_ancestor(char * m_name)562 bool exists_in_ancestor( char *m_name)
563 {
564     tic_hdr_t *found;
565     bool retval = FALSE;
566     if ( current_device_node != NULL )
567     {
568 	device_node_t *grandpa = current_device_node->parent_node;
569 
570 	if ( scope_is_global )    grandpa = current_device_node;
571 
572 	for ( ; grandpa != NULL; grandpa = grandpa->parent_node )
573 	{
574 	    found = lookup_tic_entry( m_name, grandpa->tokens_vocab);
575 	    if ( found != NULL )
576 	    {
577 		retval = TRUE;
578 		break;
579 	    }
580 	}
581 	if ( grandpa != NULL )
582 	{
583 	    char as_what_buf[AS_WHAT_BUF_SIZE] = "";
584 	    if ( as_a_what( found->fword_defr, as_what_buf) )
585 	    {
586 		strcat( as_what_buf, " ");
587 	    }
588 	    tokenization_error(INFO, "%s is defined %s%s", m_name,
589 		as_what_buf, in_what_node( grandpa) );
590 	    show_node_start();
591 	}
592     }
593 
594     return(retval );
595 }
596