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