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 :
176 : static void dev_vocab_control_struct_check( void)
177 166 : {
178 : char *ccs_messg;
179 :
180 166 : ccs_messg = safe_malloc(strlen(statbuf) + 32,
181 : "Device-Node control-structure check");
182 :
183 166 : strcpy( ccs_messg, statbuf );
184 166 : strupr( ccs_messg);
185 166 : strcat( ccs_messg, " encountered");
186 166 : announce_control_structs( WARNING, ccs_messg, 0 );
187 166 : free( ccs_messg);
188 166 : }
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 :
232 : void new_device_vocab( void )
233 83 : {
234 : device_node_t *new_node_data;
235 :
236 83 : 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 83 : if ( current_device_node == &top_level_dev_node )
244 : {
245 75 : tokenization_error(INFO, NEW_DEV_MSG_START "\n", statbuf );
246 : }else{
247 8 : tokenization_error(INFO, NEW_DEV_MSG_START
248 : " Suspending definitions of parent-device node", statbuf );
249 8 : started_at( current_device_node->ifile_name,
250 : current_device_node->line_no );
251 : }
252 :
253 : /* Now to business... */
254 83 : new_node_data = safe_malloc( sizeof(device_node_t),
255 : "creating new-device vocab data" );
256 83 : new_node_data->parent_node = current_device_node;
257 83 : new_node_data->ifile_name = strdup(iname);
258 83 : new_node_data->line_no = lineno;
259 83 : new_node_data->tokens_vocab = NULL;
260 :
261 83 : current_device_node = new_node_data;
262 :
263 83 : current_definitions = &(current_device_node->tokens_vocab);
264 83 : }
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 :
298 : void delete_device_vocab( void )
299 424 : {
300 424 : reset_tic_vocab( current_definitions, NULL );
301 :
302 424 : if ( current_device_node != &top_level_dev_node )
303 : {
304 83 : device_node_t *temp_node = current_device_node;
305 83 : current_device_node = current_device_node->parent_node;
306 83 : free( temp_node->ifile_name );
307 83 : free(temp_node);
308 : }
309 :
310 424 : current_definitions = &(current_device_node->tokens_vocab);
311 424 : }
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 :
349 : void finish_device_vocab( void )
350 83 : {
351 : bool at_top_level;
352 :
353 83 : 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 83 : at_top_level = BOOLVAL( current_device_node == &top_level_dev_node );
360 83 : if ( at_top_level )
361 : {
362 9 : tokenization_error( TKERROR,
363 : "Encountered %s without corresponding NEW-DEVICE. "
364 : "Resetting definitions since start of tokenization.\n",
365 : statbuf );
366 : }else{
367 74 : tokenization_error(INFO,
368 : "Encountered %s. Resetting definitions of device node",
369 : statbuf );
370 74 : started_at( current_device_node->ifile_name,
371 : current_device_node->line_no );
372 : }
373 :
374 : /* Now to business... */
375 83 : delete_device_vocab();
376 :
377 : /* Did we just get to the top-level device-node vocabulary
378 : * when we weren't before?
379 : */
380 83 : if ( INVERSE(at_top_level) )
381 : {
382 74 : if ( current_device_node == &top_level_dev_node )
383 : {
384 70 : tokenization_error(INFO,
385 : "Resuming definitions since start of tokenization.\n" );
386 : }else{
387 4 : tokenization_error(INFO,
388 : "Resuming definitions of parent device-node" );
389 4 : started_at( current_device_node->ifile_name,
390 : current_device_node->line_no );
391 : }
392 : }
393 83 : }
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 :
457 : char *in_what_node(device_node_t *the_node)
458 137 : {
459 137 : bool top_node = BOOLVAL( the_node == &top_level_dev_node);
460 137 : bool curr_node = BOOLVAL( the_node == current_device_node);
461 137 : bool known_node = BOOLVAL( top_node || curr_node );
462 137 : bool no_line = BOOLVAL( the_node->line_no == 0);
463 :
464 137 : show_where = INVERSE( no_line );
465 137 : show_which = known_node;
466 137 : in_what_line = the_node->line_no;
467 137 : in_what_file = the_node->ifile_name;
468 :
469 137 : 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 137 : 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 :
513 : void show_node_start( void)
514 286 : {
515 286 : if ( show_where)
516 : {
517 110 : if ( show_which )
518 : {
519 97 : just_where_started( in_what_file, in_what_line);
520 : }else{
521 13 : just_started_at( in_what_file, in_what_line);
522 : }
523 110 : show_where = FALSE;
524 : }
525 286 : }
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 :
562 : bool exists_in_ancestor( char *m_name)
563 319 : {
564 : tic_hdr_t *found;
565 319 : bool retval = FALSE;
566 319 : if ( current_device_node != NULL )
567 : {
568 319 : device_node_t *grandpa = current_device_node->parent_node;
569 :
570 319 : if ( scope_is_global ) grandpa = current_device_node;
571 :
572 40 : for ( ; grandpa != NULL; grandpa = grandpa->parent_node )
573 : {
574 98 : found = lookup_tic_entry( m_name, grandpa->tokens_vocab);
575 98 : if ( found != NULL )
576 : {
577 58 : retval = TRUE;
578 58 : break;
579 : }
580 : }
581 319 : if ( grandpa != NULL )
582 : {
583 58 : char as_what_buf[32] = "";
584 58 : if ( as_a_what( found->fword_defr, as_what_buf) )
585 : {
586 49 : strcat( as_what_buf, " ");
587 : }
588 58 : tokenization_error(INFO, "%s is defined %s%s", m_name,
589 : as_what_buf, in_what_node( grandpa) );
590 58 : show_node_start();
591 : }
592 : }
593 :
594 319 : return(retval );
595 : }
|