00001 /* 00002 * OpenBIOS - free your system! 00003 * ( FCode tokenizer ) 00004 * 00005 * This program is part of a free implementation of the IEEE 1275-1994 00006 * Standard for Boot (Initialization Configuration) Firmware. 00007 * 00008 * Copyright (C) 2001-2005 Stefan Reinauer, <stepan@openbios.org> 00009 * 00010 * This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; version 2 of the License. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU General Public License 00020 * along with this program; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA 00022 * 00023 */ 00024 00025 /* ************************************************************************** 00026 * 00027 * Support routines for managing device-node vocabularies 00028 * 00029 * (C) Copyright 2005 IBM Corporation. All Rights Reserved. 00030 * Module Author: David L. Paktor dlpaktor@us.ibm.com 00031 * 00032 **************************************************************************** */ 00033 00034 /* ************************************************************************** 00035 * 00036 * The vocabulary that is created for a device-node must not remain 00037 * available outside of that node. Also, nodes may be nested, 00038 * child within parent. 00039 * An attempt within a child-node to access directly a method defined 00040 * in the parent must be flagged as an error. (Consider what would 00041 * happen if the method in the parent-node used instance data, and 00042 * the child-node has an instance of its own.) 00043 * The correct way is to invoke the method via "$call-parent" or the like. 00044 * 00045 * We will, however, allow the user to specify a group of exceptions, 00046 * words whose scope will be "global" within the tokenization. 00047 * When "global" scope is initiated, definitions will be made to 00048 * the "core" vocabulary until "device" scope is resumed. 00049 * That will (mostly) all be handled in dictionary.c 00050 * 00051 **************************************************************************** */ 00052 00053 00054 /* ************************************************************************** 00055 * 00056 * Functions Exported: 00057 * new_device_vocab Create the new device-node's data-structure 00058 * delete_device_vocab Remove device-node's data-structure 00059 * finish_device_vocab Remove struct and give messages when 00060 * device is "finish"ed. 00061 * exists_in_ancestor Issue a Message if the given word exists 00062 * in an ancestor of the current dev-node. 00063 * 00064 **************************************************************************** */ 00065 00066 /* ************************************************************************** 00067 * 00068 * Still to be done: 00069 * Add a pair of fields to the data-structure for the Input File and 00070 * Line Number where "finish-device" occurred. When a device-node 00071 * is "finish"ed, do not delete it, but instead fill in those 00072 * fields and the move the node to a separate linked-list. 00073 * When looking whether a word exists in an ancestor-node, also 00074 * check whether it was in a device-node that was finished and 00075 * print both where it was started and where it was finished. 00076 * 00077 **************************************************************************** */ 00078 00079 00080 00081 #include <stdio.h> 00082 #include <stdlib.h> 00083 #include <string.h> 00084 #include <errno.h> 00085 00086 #include "devnode.h" 00087 #include "errhandler.h" 00088 #include "scanner.h" 00089 #include "vocabfuncts.h" 00090 #include "flowcontrol.h" 00091 #include "stream.h" 00092 #include "ticvocab.h" 00093 00094 00095 /* ************************************************************************** 00096 * 00097 * Tokenization starts with an implicit "new-device" in effect. 00098 * The top-level device-node is never removed. 00099 * 00100 * Initialize it here 00101 * 00102 **************************************************************************** */ 00103 char default_top_dev_ifile_name[] = "Start of tokenization"; 00104 00105 static device_node_t top_level_dev_node = { 00106 NULL , /* parent_node */ 00107 default_top_dev_ifile_name , /* ifile_name. 00108 * Something to show, Just In Case 00109 */ 00110 0 , /* line_no */ 00111 NULL , /* tokens_vocab */ 00112 }; 00113 00114 /* ************************************************************************** 00115 * 00116 * Global Variables Exported. 00117 * Pointers to: 00118 * current_device_node data-structure of current device-node 00119 * current_definitions vocab into which to add def'ns. 00120 * 00121 **************************************************************************** */ 00122 00123 device_node_t *current_device_node = &top_level_dev_node; 00124 tic_hdr_t **current_definitions = &(top_level_dev_node.tokens_vocab); 00125 00126 00127 /* ************************************************************************** 00128 * 00129 * Internal Static Variables 00130 * These are used to support the routines in_what_node() 00131 * and show_node_start() , which are used to facilitate 00132 * certain kinds of Messaging, as described later. 00133 * 00134 * in_what_buffr Buffer for the in_what_node() string 00135 * show_where TRUE if the string needs to be followed-up 00136 * show_which TRUE if follow-up should be just_where_started() 00137 * rather than just_started_at() 00138 * in_what_line Line Number to use in the follow-up 00139 * in_what_file File Name to use in the follow-up 00140 * 00141 **************************************************************************** */ 00142 00143 static char in_what_buffr[50]; /* Ought to be more than enough. */ 00144 static bool show_where = FALSE; 00145 static bool show_which; 00146 static int in_what_line; 00147 static char *in_what_file; 00148 00149 00150 /* ************************************************************************** 00151 * 00152 * Function name: dev_vocab_control_struct_check 00153 * Synopsis: Issue Warnings for unresolved flow-control constructs 00154 * at start or end of a device-node. 00155 * 00156 * Inputs: 00157 * Parameters: NONE 00158 * Global Variables: 00159 * statbuf The command being processed. 00160 * 00161 * Outputs: 00162 * Returned Value: NONE 00163 * Printout: 00164 * Handled by announce_control_structs() routine 00165 * 00166 * Error Detection: 00167 * Handled by announce_control_structs() routine 00168 * 00169 * Process Explanation: 00170 * Set up a buffer with the error message, based on statbuf 00171 * and pass it to announce_control_structs() 00172 * Release it when done. 00173 * 00174 **************************************************************************** */ 00175 00176 static void dev_vocab_control_struct_check( void) 00177 { 00178 char *ccs_messg; 00179 00180 ccs_messg = safe_malloc(strlen(statbuf) + 32, 00181 "Device-Node control-structure check"); 00182 00183 strcpy( ccs_messg, statbuf ); 00184 strupr( ccs_messg); 00185 strcat( ccs_messg, " encountered"); 00186 announce_control_structs( WARNING, ccs_messg, 0 ); 00187 free( ccs_messg); 00188 } 00189 00190 00191 00192 /* ************************************************************************** 00193 * 00194 * Function name: new_device_vocab 00195 * Synopsis: Create and initialize the data-structure for a 00196 * new device-node when a "new-device" is created, 00197 * with messages as appropriate. 00198 * 00199 * Inputs: 00200 * Parameters: NONE 00201 * Global Variables: 00202 * statbuf The word that was just read. 00203 * iname Current Input-File Name 00204 * lineno Current line-number 00205 * 00206 * Outputs: 00207 * Returned Value: NONE 00208 * Global Variables: 00209 * current_device_node Will point to the new data-structure 00210 * Memory Allocated 00211 * Space for the new device_node_t data-structure 00212 * Space for a copy of the current input file name 00213 * When Freed? 00214 * By delete_device_vocab(), when the device-node is "finish"ed. 00215 * Printout: 00216 * Advisory message. 00217 * 00218 * Error Detection: 00219 * In immediate-execution mode, Control Structures that have not 00220 * been completed are questionable; Issue WARNINGS via the 00221 * dev_vocab_control_struct_check() routine. 00222 * 00223 * Process Explanation: 00224 * This routine is called when "new-device" is invoked, but only 00225 * if we are in immediate-execution mode. 00226 * Later on, in ERROR- or INFOrmative messages, we will want to 00227 * be able to refer to the file and line-number in which this 00228 * was encountered, so we include them in the structure. 00229 * 00230 **************************************************************************** */ 00231 00232 void new_device_vocab( void ) 00233 { 00234 device_node_t *new_node_data; 00235 00236 dev_vocab_control_struct_check(); 00237 00238 /* Advisory message will mention previous device-node 00239 * if there was one. Either way starts out the same: 00240 */ 00241 #define NEW_DEV_MSG_START "Encountered %s. Starting new device-node." 00242 00243 if ( current_device_node == &top_level_dev_node ) 00244 { 00245 tokenization_error(INFO, NEW_DEV_MSG_START "\n", statbuf ); 00246 }else{ 00247 tokenization_error(INFO, NEW_DEV_MSG_START 00248 " Suspending definitions of parent-device node", statbuf ); 00249 started_at( current_device_node->ifile_name, 00250 current_device_node->line_no ); 00251 } 00252 00253 /* Now to business... */ 00254 new_node_data = safe_malloc( sizeof(device_node_t), 00255 "creating new-device vocab data" ); 00256 new_node_data->parent_node = current_device_node; 00257 new_node_data->ifile_name = strdup(iname); 00258 new_node_data->line_no = lineno; 00259 new_node_data->tokens_vocab = NULL; 00260 00261 current_device_node = new_node_data; 00262 00263 current_definitions = &(current_device_node->tokens_vocab); 00264 } 00265 00266 00267 /* ************************************************************************** 00268 * 00269 * Function name: delete_device_vocab 00270 * Synopsis: Remove the vocabularies of the current device-node, 00271 * along with its data-structure, when the device 00272 * is "finish"ed; do not print messages. 00273 * Do not remove the top-level device-node data-struct. 00274 * 00275 * Associated FORTH words: FINISH_DEVICE (interpretive state) 00276 * END0 END1 00277 * Associated Tokenizer directives: RESET-SYMBOLS (in "Normal" mode) 00278 * FCODE-END 00279 * 00280 * Inputs: 00281 * Parameters: NONE 00282 * Global Variables: 00283 * current_device_node Points to current device's struct 00284 * Leads to chain of dev-node structs 00285 * 00286 * Outputs: 00287 * Returned Value: 00288 * Global Variables: 00289 * current_device_node Parent-device's struct becomes current 00290 * Memory Freed 00291 * All that was allocated for the tokens and the definers 00292 * vocabs in the current device-node 00293 * The copy of the input file name, except the top-level 00294 * The current_device_node data-structure, except the top-level 00295 * 00296 **************************************************************************** */ 00297 00298 void delete_device_vocab( void ) 00299 { 00300 reset_tic_vocab( current_definitions, NULL ); 00301 00302 if ( current_device_node != &top_level_dev_node ) 00303 { 00304 device_node_t *temp_node = current_device_node; 00305 current_device_node = current_device_node->parent_node; 00306 free( temp_node->ifile_name ); 00307 free(temp_node); 00308 } 00309 00310 current_definitions = &(current_device_node->tokens_vocab); 00311 } 00312 00313 /* ************************************************************************** 00314 * 00315 * Function name: finish_device_vocab 00316 * Synopsis: Remove the device-node data-structure and all its 00317 * vocabularies when the device is "finish"ed, 00318 * with appropriate messages. 00319 * Do not remove the top-level device node data-struct. 00320 * 00321 * Associated FORTH word: FINISH_DEVICE 00322 * 00323 * Inputs: 00324 * Parameters: NONE 00325 * Global Variables: 00326 * current_device_node Current device's struct pointer 00327 * 00328 * Outputs: 00329 * Returned Value: NONE 00330 * Global Variables: 00331 * current_device_node Parent-device's struct becomes current 00332 * Printout: 00333 * Advisory message. 00334 * 00335 * Error Detection: 00336 * If current_device_node is already pointing at the top-level 00337 * device node, it means there was no corresponding NEW-DEVICE 00338 * Issue an ERROR. 00339 * In immediate-execution mode, Control Structures that have not 00340 * been completed are questionable; Issue WARNINGS via the 00341 * dev_vocab_control_struct_check() routine. 00342 * 00343 * Process Explanation: 00344 * This routine is called when "finish-device" is invoked, but only 00345 * if we are in immediate-execution mode. 00346 * 00347 **************************************************************************** */ 00348 00349 void finish_device_vocab( void ) 00350 { 00351 bool at_top_level; 00352 00353 dev_vocab_control_struct_check(); 00354 00355 /* We never remove the top-level device-node vocabulary, 00356 * so we need to test whether we're about to. 00357 */ 00358 00359 at_top_level = BOOLVAL( current_device_node == &top_level_dev_node ); 00360 if ( at_top_level ) 00361 { 00362 tokenization_error( TKERROR, 00363 "Encountered %s without corresponding NEW-DEVICE. " 00364 "Resetting definitions since start of tokenization.\n", 00365 statbuf ); 00366 }else{ 00367 tokenization_error(INFO, 00368 "Encountered %s. Resetting definitions of device node", 00369 statbuf ); 00370 started_at( current_device_node->ifile_name, 00371 current_device_node->line_no ); 00372 } 00373 00374 /* Now to business... */ 00375 delete_device_vocab(); 00376 00377 /* Did we just get to the top-level device-node vocabulary 00378 * when we weren't before? 00379 */ 00380 if ( INVERSE(at_top_level) ) 00381 { 00382 if ( current_device_node == &top_level_dev_node ) 00383 { 00384 tokenization_error(INFO, 00385 "Resuming definitions since start of tokenization.\n" ); 00386 }else{ 00387 tokenization_error(INFO, 00388 "Resuming definitions of parent device-node" ); 00389 started_at( current_device_node->ifile_name, 00390 current_device_node->line_no ); 00391 } 00392 } 00393 } 00394 00395 00396 /* ************************************************************************** 00397 * 00398 * Function name: in_what_node 00399 * Synopsis: Format a string for use in a Message that might 00400 * identify the start of the given device-node. 00401 * 00402 * Inputs: 00403 * Parameters: 00404 * the_node The device-node vocabulary about which 00405 * to construct the identifying phrase. 00406 * Local Static Variables: 00407 * in_what_buffr Buffer in which to format the string. 00408 * Global Variables: 00409 * current_definitions Device-node vocabulary currently 00410 * in effect. 00411 * 00412 * Outputs: 00413 * Returned Value: Pointer to buffer w/ formatted string 00414 * Local Static Variables: 00415 * in_what_buffr Will contain the formatted string. 00416 * show_where TRUE if the string needs to be followed-up 00417 * (i.e., did not contain a terminating 00418 * new-line) by just_where_started() 00419 * or by just_started_at() 00420 * show_which TRUE if the follow-up call should be 00421 * to just_where_started() rather 00422 * than to just_started_at() 00423 * in_what_line Copy of line_no field from the_node 00424 * in_what_file Copy of ifile_name field from the_node 00425 * 00426 * Process Explanation: 00427 * Calling routine must ascertain that Global-scope is not in effect. 00428 * The returned phrase can be used as a string argument in a Message. 00429 * Set show_where TRUE if the_node->line_no is non-zero. 00430 * Set show_which TRUE if the_node is either the Current or the 00431 * Top-Level device-node 00432 * If the originating line-number in the given Node structure is zero, 00433 * the returned phrase will contain a terminating new-line. 00434 * (This only happens if the given Node is the top-level Node, 00435 * and it's the Current Node, and the "official" starting-point 00436 * hasn't yet been established by an "FCode-Starter" such as 00437 * FCODE-VERSION2 . Once that command has been given, even 00438 * definitions that were made prior to it belong to the Node 00439 * that started there.) 00440 * Otherwise, show_where is returned TRUE, and show_which becomes 00441 * relevant. If the given node is the Current or the Top-Level 00442 * node, text about the originating file-name and line-number 00443 * merely describes a node that is already uniquely identified, 00444 * so the message appended to the buffer will have the phrase 00445 * "which began" (which introduces what is known in grammar as 00446 * an Appositive Subordinate Clause) and show_which will be 00447 * returned TRUE. If the given node is not uniquely identifiable 00448 * without the file- and line- phrase, then the Subordinate Clause 00449 * is Indicative, and should be introduced with "that" (and no 00450 * comma); in that case, we will return show_which as FALSE. 00451 * After the calling routine displays the message in which the 00452 * returned phrase is used, it must call show_node_start() 00453 * to display the followe-up message, if any. 00454 * 00455 **************************************************************************** */ 00456 00457 char *in_what_node(device_node_t *the_node) 00458 { 00459 bool top_node = BOOLVAL( the_node == &top_level_dev_node); 00460 bool curr_node = BOOLVAL( the_node == current_device_node); 00461 bool known_node = BOOLVAL( top_node || curr_node ); 00462 bool no_line = BOOLVAL( the_node->line_no == 0); 00463 00464 show_where = INVERSE( no_line ); 00465 show_which = known_node; 00466 in_what_line = the_node->line_no; 00467 in_what_file = the_node->ifile_name; 00468 00469 sprintf( in_what_buffr, "in the%s device-node%s", 00470 INVERSE( known_node ) ? "" 00471 : top_node ? " top-level" : " current" , 00472 00473 no_line ? ".\n" 00474 : known_node ? ", which began" : "" ); 00475 00476 00477 return( in_what_buffr); 00478 } 00479 00480 00481 /* ************************************************************************** 00482 * 00483 * Function name: show_node_start 00484 * Synopsis: Follow-up to the in_what_node() call. Print out, 00485 * if applicable, the text about the originating 00486 * file-name and line-number 00487 * 00488 * Inputs: 00489 * Parameters: NONE 00490 * Local Static Variables: 00491 * show_where Nothing to do if not TRUE 00492 * show_which TRUE if should call just_where_started() 00493 * rather than just_started_at() 00494 * in_what_line Line Number to use in the follow-up 00495 * in_what_file File Name to use in the follow-up 00496 * 00497 * Outputs: 00498 * Returned Value: NONE 00499 * Local Static Variables: 00500 * show_where Force to FALSE 00501 * Printout: 00502 * Follow-up to the in_what_node() call. Applicable text 00503 * about the originating file-name and line-number. 00504 * 00505 * Process Explanation: 00506 * By forcing show_where to FALSE after this is called, we 00507 * can safely allow routines that might or might not have 00508 * called in_what_node() to call this routine, without 00509 * needing any additional "bookkeeping". 00510 * 00511 **************************************************************************** */ 00512 00513 void show_node_start( void) 00514 { 00515 if ( show_where) 00516 { 00517 if ( show_which ) 00518 { 00519 just_where_started( in_what_file, in_what_line); 00520 }else{ 00521 just_started_at( in_what_file, in_what_line); 00522 } 00523 show_where = FALSE; 00524 } 00525 } 00526 00527 00528 00529 /* ************************************************************************** 00530 * 00531 * Function name: exists_in_ancestor 00532 * Synopsis: Issue a Message and return an indication if 00533 * the given word exists in an ancestor of 00534 * the current device-node. 00535 * Used for additional error-message information. 00536 * 00537 * 00538 * Inputs: 00539 * Parameters: 00540 * m_name "Method" name 00541 * Global Variables: 00542 * current_device_node Leads to chain of dev-node data-structs 00543 * scope_is_global TRUE if "global" scope is in effect 00544 * 00545 * Outputs: 00546 * Returned Value: TRUE if word found 00547 * Printout: 00548 * If m_name exists in an ancestor-node, print an ADVISORY 00549 * giving the location where the ancestor originated. 00550 * 00551 * Error Detection: 00552 * None here. Calling routine detected error; see below. 00553 * 00554 * Process Explanation: 00555 * This routine was called as the result of detecting an error: 00556 * viz., m_name was not found in either the current node 00557 * or the base vocabulary. (Except: If "global" scope is 00558 * in effect, we didn't search the current device-node). 00559 * 00560 **************************************************************************** */ 00561 00562 bool exists_in_ancestor( char *m_name) 00563 { 00564 tic_hdr_t *found; 00565 bool retval = FALSE; 00566 if ( current_device_node != NULL ) 00567 { 00568 device_node_t *grandpa = current_device_node->parent_node; 00569 00570 if ( scope_is_global ) grandpa = current_device_node; 00571 00572 for ( ; grandpa != NULL; grandpa = grandpa->parent_node ) 00573 { 00574 found = lookup_tic_entry( m_name, grandpa->tokens_vocab); 00575 if ( found != NULL ) 00576 { 00577 retval = TRUE; 00578 break; 00579 } 00580 } 00581 if ( grandpa != NULL ) 00582 { 00583 char as_what_buf[32] = ""; 00584 if ( as_a_what( found->fword_defr, as_what_buf) ) 00585 { 00586 strcat( as_what_buf, " "); 00587 } 00588 tokenization_error(INFO, "%s is defined %s%s", m_name, 00589 as_what_buf, in_what_node( grandpa) ); 00590 show_node_start(); 00591 } 00592 } 00593 00594 return(retval ); 00595 }