| 1 |  | /******************************************************************** | 
    
    | 2 |  | * Description: interp_o_word.cc | 
    
    | 3 |  | * | 
    
    | 4 |  | * | 
    
    | 5 |  | * Author: Kenneth Lerman | 
    
    | 6 |  | * License: GPL Version 2 | 
    
    | 7 |  | * System: Linux | 
    
    | 8 |  | * | 
    
    | 9 |  | * Copyright 2005 All rights reserved. | 
    
    | 10 |  | * | 
    
    | 11 |  | * Last change: Michael Haberler 7/2011 | 
    
    | 12 |  | * | 
    
    | 13 |  | ********************************************************************/ | 
    
    | 14 |  |  | 
    
    | 15 |  | #include <boost/python/list.hpp> | 
    
    | 16 |  | #include <boost/python/tuple.hpp> | 
    
    | 17 |  | #include <boost/python/dict.hpp> | 
    
    | 18 |  | #include <unistd.h> | 
    
    | 19 |  | #include <stdio.h> | 
    
    | 20 |  | #include <stdlib.h> | 
    
    | 21 |  | #include <math.h> | 
    
    | 22 |  | #include <string.h> | 
    
    | 23 |  | #include <ctype.h> | 
    
    | 24 |  | #include <errno.h> | 
    
    | 25 |  | #include <sys/types.h> | 
    
    | 26 |  | #include <sys/stat.h> | 
    
    | 27 |  | #include <dirent.h> | 
    
    | 28 |  | #include "rs274ngc.hh" | 
    
    | 29 |  | #include "rs274ngc_return.hh" | 
    
    | 30 |  | #include "interp_return.hh" | 
    
    | 31 |  | #include "interp_internal.hh" | 
    
    | 32 |  | #include "rs274ngc_interp.hh" | 
    
    | 33 |  | #include "python_plugin.hh" | 
    
    | 34 |  | #include "interp_python.hh" | 
    
    | 35 |  |  | 
    
    | 36 |  | namespace bp = boost::python; | 
    
    | 37 |  |  | 
    
    | 38 |  | //======================================================================== | 
    
    | 39 |  | // Functions for control stuff (O-words) | 
    
    | 40 |  | //======================================================================== | 
    
    | 41 |  |  | 
    
    | 42 |  | /* | 
    
    | 43 |  |   Given the root of a directory tree and a file name, | 
    
    | 44 |  |   find the path to the file, if any. | 
    
    | 45 |  | */ | 
    
    | 46 |  |  | 
    
    | 47 | 1 | int Interp::findFile( // ARGUMENTS | 
    
    | 48 |  | 		     char *direct,  // the directory to start looking in | 
    
    | 49 |  | 		     char *target,  // the name of the file to find | 
    
    | 50 |  | 		     char *foundFileDirect) // where to store the result | 
    
    | 51 |  | { | 
    
    | 52 |  |     FILE *file; | 
    
    | 53 |  |     DIR *aDir; | 
    
    | 54 |  |     struct dirent *aFile; | 
    
    | 55 |  |     char targetPath[PATH_MAX+1]; | 
    
    | 56 |  |  | 
    
    | 57 | 1 |     snprintf(targetPath, PATH_MAX, "%s/%s", direct, target); | 
    
    | 58 | 1 |     file = fopen(targetPath, "r"); | 
    
    | 59 | 1 |     if (file) { | 
    
    | 60 |  |         strncpy(foundFileDirect, direct, PATH_MAX); | 
    
    | 61 |  |         fclose(file); | 
    
    | 62 |  |         return INTERP_OK; | 
    
    | 63 |  |     } | 
    
    | 64 | 1 |     aDir = opendir(direct); | 
    
    | 65 | 1 |     if (!aDir) { | 
    
    | 66 | 1 | 	ERS(NCE_FILE_NOT_OPEN); | 
    
    | 67 |  |     } | 
    
    | 68 |  |  | 
    
    | 69 |  |     while ((aFile = readdir(aDir))) { | 
    
    | 70 |  |         if (aFile->d_type == DT_DIR && | 
    
    | 71 |  | 	    (0 != strcmp(aFile->d_name, "..")) && | 
    
    | 72 |  | 	    (0 != strcmp(aFile->d_name, "."))) { | 
    
    | 73 |  |  | 
    
    | 74 |  |             char path[PATH_MAX+1]; | 
    
    | 75 |  |             snprintf(path, PATH_MAX, "%s/%s", direct, aFile->d_name); | 
    
    | 76 |  |             if (INTERP_OK == findFile(path, target, foundFileDirect)) { | 
    
    | 77 |  | 	        closedir(aDir); | 
    
    | 78 |  |                 return INTERP_OK; | 
    
    | 79 |  |             } | 
    
    | 80 |  |         } | 
    
    | 81 |  |     } | 
    
    | 82 |  |     closedir(aDir); | 
    
    | 83 |  |     ERS(NCE_FILE_NOT_OPEN); | 
    
    | 84 |  | } | 
    
    | 85 |  |  | 
    
    | 86 |  |  | 
    
    | 87 |  | /* | 
    
    | 88 |  |  *  this now uses STL maps for offset access | 
    
    | 89 |  |  */ | 
    
    | 90 | 94 | int Interp::control_save_offset(block_pointer block,        /* pointer to a block of RS274/NGC instructions */ | 
    
    | 91 |  | 				setup_pointer settings)     /* pointer to machine settings */ | 
    
    | 92 |  | { | 
    
    | 93 |  |     static char name[] = "control_save_offset"; | 
    
    | 94 | 94 |     offset_pointer op = NULL; | 
    
    | 95 |  |  | 
    
    | 96 | 94 |     logOword("Entered:%s for o_name:|%s|", name, block->o_name); | 
    
    | 97 |  |  | 
    
    | 98 | 94 |     if (control_find_oword(block, settings, &op) == INTERP_OK) { | 
    
    | 99 |  | 	// already exists | 
    
    | 100 |  | 	ERS(_("File:%s line:%d redefining sub: o|%s| already defined in file:%s"), | 
    
    | 101 |  | 	    settings->filename, settings->sequence_number, | 
    
    | 102 |  | 	    block->o_name, | 
    
    | 103 |  | 	    op->filename); | 
    
    | 104 |  |     } | 
    
    | 105 |  |     offset new_offset; | 
    
    | 106 |  |  | 
    
    | 107 | 94 |     new_offset.type = block->o_type; | 
    
    | 108 | 94 |     new_offset.offset = block->offset; | 
    
    | 109 | 94 |     new_offset.filename = strstore(settings->filename); | 
    
    | 110 | 94 |     new_offset.repeat_count = -1; | 
    
    | 111 |  |     // the sequence number has already been bumped, so save | 
    
    | 112 |  |     // the proper value | 
    
    | 113 | 94 |     new_offset.sequence_number = settings->sequence_number - 1; | 
    
    | 114 | 94 |     settings->offset_map[block->o_name] = new_offset; | 
    
    | 115 | 94 |     return INTERP_OK; | 
    
    | 116 |  | } | 
    
    | 117 |  |  | 
    
    | 118 |  |  | 
    
    | 119 | 1450 | int Interp::control_find_oword(block_pointer block,      // pointer to block | 
    
    | 120 |  | 			       setup_pointer settings,   // pointer to machine settings | 
    
    | 121 |  | 			       offset_pointer *op)       // pointer to offset descriptor | 
    
    | 122 |  | { | 
    
    | 123 |  |     static char name[] = "control_find_oword"; | 
    
    | 124 |  |     offset_map_iterator it; | 
    
    | 125 |  |  | 
    
    | 126 | 2900 |     it = settings->offset_map.find(block->o_name); | 
    
    | 127 | 2900 |     if (it != settings->offset_map.end()) { | 
    
    | 128 | 1305 | 	*op =  &it->second; | 
    
    | 129 | 1305 | 	return INTERP_OK; | 
    
    | 130 |  |     } else { | 
    
    | 131 | 145 | 	logOword("%s: Unknown oword name: |%s|", name, block->o_name); | 
    
    | 132 | 145 | 	ERS(NCE_UNKNOWN_OWORD_NUMBER); | 
    
    | 133 |  |     } | 
    
    | 134 |  | } | 
    
    | 135 |  |  | 
    
    | 136 |  | const char *o_ops[] = { | 
    
    | 137 |  |     "O_none", | 
    
    | 138 |  |     "O_sub", | 
    
    | 139 |  |     "O_endsub", | 
    
    | 140 |  |     "O_call", | 
    
    | 141 |  |     "O_do", | 
    
    | 142 |  |     "O_while", | 
    
    | 143 |  |     "O_if", | 
    
    | 144 |  |     "O_elseif", | 
    
    | 145 |  |     "O_else", | 
    
    | 146 |  |     "O_endif", | 
    
    | 147 |  |     "O_break", | 
    
    | 148 |  |     "O_continue", | 
    
    | 149 |  |     "O_endwhile", | 
    
    | 150 |  |     "O_return", | 
    
    | 151 |  |     "O_repeat", | 
    
    | 152 |  |     "O_endrepeat", | 
    
    | 153 |  |     "O_continue_call", | 
    
    | 154 |  |     "O_pyreturn", | 
    
    | 155 |  | }; | 
    
    | 156 |  |  | 
    
    | 157 |  | const char *call_statenames[] = { | 
    
    | 158 |  |     "CS_NORMAL", | 
    
    | 159 |  |     "CS_REEXEC_PROLOG", | 
    
    | 160 |  |     "CS_REEXEC_PYBODY", | 
    
    | 161 |  |     "CS_REEXEC_EPILOG", | 
    
    | 162 |  |     "CS_REEXEC_PYOSUB", | 
    
    | 163 |  | }; | 
    
    | 164 |  |  | 
    
    | 165 |  | const char *call_typenames[] = { | 
    
    | 166 |  |     "CT_NGC_OWORD_SUB", | 
    
    | 167 |  |     "CT_PYTHON_OWORD_SUB", | 
    
    | 168 |  |     "CT_REMAP", | 
    
    | 169 |  | }; | 
    
    | 170 |  |  | 
    
    | 171 | 363 | int Interp::execute_call(setup_pointer settings, | 
    
    | 172 |  | 			 context_pointer current_frame, | 
    
    | 173 |  | 			 int call_type) | 
    
    | 174 |  | { | 
    
    | 175 | 363 |     int status = INTERP_OK; | 
    
    | 176 |  |     int i; | 
    
    | 177 |  |     bp::list plist; | 
    
    | 178 |  |  | 
    
    | 179 | 363 |     context_pointer previous_frame = &settings->sub_context[settings->call_level-1]; | 
    
    | 180 |  |  | 
    
    | 181 | 363 |     block_pointer eblock = &EXECUTING_BLOCK(*settings); | 
    
    | 182 |  |  | 
    
    | 183 | 363 |     logOword("execute_call %s type=%s state=%s cl=%d rl=%d", | 
    
    | 184 |  | 	     current_frame->subName, | 
    
    | 185 |  | 	     call_typenames[call_type], | 
    
    | 186 |  | 	     call_statenames[settings->call_state], | 
    
    | 187 |  | 	     settings->call_level,settings->remap_level); | 
    
    | 188 |  |  | 
    
    | 189 | 363 |     switch (call_type) { | 
    
    | 190 |  |  | 
    
    | 191 |  |     case CT_NGC_OWORD_SUB: | 
    
    | 192 |  | 	// copy parameters from context | 
    
    | 193 |  | 	// save old values of parameters | 
    
    | 194 |  | 	// save current file position in context | 
    
    | 195 |  | 	// if we were skipping, no longer | 
    
    | 196 | 356 | 	if (settings->skipping_o) { | 
    
    | 197 |  | 	    logOword("case O_call -- no longer skipping to:|%s|", | 
    
    | 198 |  | 		     settings->skipping_o); | 
    
    | 199 |  | 	    settings->skipping_o = NULL; | 
    
    | 200 |  | 	} | 
    
    | 201 | 10680 | 	for(i = 0; i < INTERP_SUB_PARAMS; i++)	{ | 
    
    | 202 |  | 	    previous_frame->saved_params[i] = | 
    
    | 203 | 10680 | 		settings->parameters[i + INTERP_FIRST_SUBROUTINE_PARAM]; | 
    
    | 204 |  | 	    settings->parameters[i + INTERP_FIRST_SUBROUTINE_PARAM] = | 
    
    | 205 | 10680 | 		eblock->params[i]; | 
    
    | 206 |  | 	} | 
    
    | 207 |  |  | 
    
    | 208 |  | 	// if the previous file was NULL, mark positon as -1 so as not to | 
    
    | 209 |  | 	// reopen it on return. | 
    
    | 210 | 356 | 	if (settings->file_pointer == NULL) { | 
    
    | 211 | 2 | 	    previous_frame->position = -1; | 
    
    | 212 |  | 	} else { | 
    
    | 213 | 354 | 	    previous_frame->position = ftell(settings->file_pointer); | 
    
    | 214 |  | 	} | 
    
    | 215 |  |  | 
    
    | 216 |  | 	// save return location | 
    
    | 217 | 356 | 	previous_frame->filename = strstore(settings->filename); | 
    
    | 218 | 356 | 	previous_frame->sequence_number = settings->sequence_number; | 
    
    | 219 | 356 | 	logOword("saving return location[cl=%d]: %s:%d offset=%ld", | 
    
    | 220 |  | 		 settings->call_level-1, | 
    
    | 221 |  | 		 previous_frame->filename, | 
    
    | 222 |  | 		 previous_frame->sequence_number, | 
    
    | 223 |  | 		 previous_frame->position); | 
    
    | 224 |  |  | 
    
    | 225 | 356 | 	if (FEATURE(OWORD_N_ARGS)) { | 
    
    | 226 |  | 	    // let any  Oword sub know the number of parameters | 
    
    | 227 |  | 	    CHP(add_named_param("n_args", PA_READONLY)); | 
    
    | 228 |  | 	    CHP(store_named_param(settings, "n_args", | 
    
    | 229 |  | 				  (double )eblock->param_cnt, | 
    
    | 230 |  | 				  OVERRIDE_READONLY)); | 
    
    | 231 |  | 	} | 
    
    | 232 |  |  | 
    
    | 233 |  | 	// transfer control | 
    
    | 234 | 356 | 	if (control_back_to(eblock, settings) == INTERP_ERROR) { | 
    
    | 235 |  | 	    settings->call_level--; | 
    
    | 236 |  | 	    ERS(NCE_UNABLE_TO_OPEN_FILE,eblock->o_name); | 
    
    | 237 |  | 	    return INTERP_ERROR; | 
    
    | 238 |  | 	} | 
    
    | 239 |  | 	break; | 
    
    | 240 |  |  | 
    
    | 241 |  |     case CT_PYTHON_OWORD_SUB: | 
    
    | 242 | 7 | 	switch (settings->call_state) { | 
    
    | 243 |  | 	case CS_NORMAL: | 
    
    | 244 | 7 | 	    settings->return_value = 0.0; | 
    
    | 245 | 7 | 	    settings->value_returned = 0; | 
    
    | 246 | 7 | 	    previous_frame->sequence_number = settings->sequence_number; | 
    
    | 247 | 7 | 	    previous_frame->filename = strstore(settings->filename); | 
    
    | 248 | 7 | 	    plist.append(*settings->pythis); // self | 
    
    | 249 | 4 | 	    for(int i = 0; i < eblock->param_cnt; i++) | 
    
    | 250 | 4 | 		plist.append(eblock->params[i]); // positonal args | 
    
    | 251 | 14 | 	    current_frame->pystuff.impl->tupleargs = bp::tuple(plist); | 
    
    | 252 | 14 | 	    current_frame->pystuff.impl->kwargs = bp::dict(); | 
    
    | 253 |  |  | 
    
    | 254 |  | 	case CS_REEXEC_PYOSUB: | 
    
    | 255 | 7 | 	    if (settings->call_state ==  CS_REEXEC_PYOSUB) | 
    
    | 256 |  | 		CHP(read_inputs(settings)); | 
    
    | 257 |  | 	    status = pycall(settings, current_frame, OWORD_MODULE, | 
    
    | 258 |  | 			    current_frame->subName, | 
    
    | 259 | 7 | 			    settings->call_state == CS_NORMAL ? PY_OWORDCALL : PY_FINISH_OWORDCALL); | 
    
    | 260 | 7 | 	    CHKS(status == INTERP_ERROR, "pycall(%s.%s) failed", OWORD_MODULE, current_frame->subName) ; | 
    
    | 261 | 7 | 	    switch (status = handler_returned(settings, current_frame, current_frame->subName, true)) { | 
    
    | 262 |  | 	    case INTERP_EXECUTE_FINISH: | 
    
    | 263 |  | 		settings->call_state = CS_REEXEC_PYOSUB; | 
    
    | 264 |  | 		break; | 
    
    | 265 |  | 	    default: | 
    
    | 266 | 7 | 		settings->call_state = CS_NORMAL; | 
    
    | 267 | 7 | 		settings->sequence_number = previous_frame->sequence_number; | 
    
    | 268 | 7 | 		CHP(status); | 
    
    | 269 |  | 		// M73 auto-restore is of dubious value in a Python subroutine | 
    
    | 270 | 7 | 		CHP(leave_context(settings,false)); | 
    
    | 271 |  | 	    } | 
    
    | 272 |  | 	    break; | 
    
    | 273 |  | 	} | 
    
    | 274 |  | 	break; | 
    
    | 275 |  |  | 
    
    | 276 |  |     case CT_REMAP: | 
    
    | 277 |  | 	block_pointer cblock = &CONTROLLING_BLOCK(*settings); | 
    
    | 278 |  | 	remap_pointer remap = cblock->executing_remap; | 
    
    | 279 |  |  | 
    
    | 280 |  | 	switch (settings->call_state) { | 
    
    | 281 |  | 	case CS_NORMAL: | 
    
    | 282 |  | 	    if (remap->remap_py || remap->prolog_func || remap->epilog_func) { | 
    
    | 283 |  | 		CHKS(!PYUSABLE, "%s (remapped) uses Python functions, but the Python plugin is not available", | 
    
    | 284 |  | 		     remap->name); | 
    
    | 285 |  | 		plist.append(*settings->pythis);   //self | 
    
    | 286 |  | 		current_frame->pystuff.impl->tupleargs = bp::tuple(plist); | 
    
    | 287 |  | 		current_frame->pystuff.impl->kwargs = bp::dict(); | 
    
    | 288 |  | 	    } | 
    
    | 289 |  | 	    if (remap->argspec && (strchr(remap->argspec, '@') == NULL)) { | 
    
    | 290 |  | 		// add_parameters will decorate kwargs as per argspec | 
    
    | 291 |  | 		// if named local parameters specified | 
    
    | 292 |  | 		CHP(add_parameters(settings, cblock, NULL)); | 
    
    | 293 |  | 	    } | 
    
    | 294 |  | 	    // fall through | 
    
    | 295 |  |  | 
    
    | 296 |  | 	case CS_REEXEC_PROLOG: | 
    
    | 297 |  | 	    if (remap->prolog_func) { | 
    
    | 298 |  | 		status = pycall(settings, current_frame, REMAP_MODULE,remap->prolog_func, | 
    
    | 299 |  | 				settings->call_state == CS_NORMAL ? PY_PROLOG : PY_FINISH_PROLOG); | 
    
    | 300 |  | 		CHKS(status == INTERP_ERROR, "pycall(%s.%s) failed", REMAP_MODULE, remap->prolog_func); | 
    
    | 301 |  | 		switch (status = handler_returned(settings, current_frame, current_frame->subName, false)) { | 
    
    | 302 |  | 		case INTERP_EXECUTE_FINISH: | 
    
    | 303 |  | 		    settings->call_state = CS_REEXEC_PROLOG; | 
    
    | 304 |  | 		    return status; | 
    
    | 305 |  | 		default: | 
    
    | 306 |  | 		    settings->call_state  = CS_NORMAL; | 
    
    | 307 |  | 		    //settings->sequence_number = previous_frame->sequence_number; | 
    
    | 308 |  | 		    CHP(status); | 
    
    | 309 |  | 		} | 
    
    | 310 |  | 	    } | 
    
    | 311 |  | 	    // fall through | 
    
    | 312 |  |  | 
    
    | 313 |  | 	case CS_REEXEC_PYBODY: | 
    
    | 314 |  | 	    if (remap->remap_py) { | 
    
    | 315 |  | 		status = pycall(settings, current_frame, REMAP_MODULE, remap->remap_py, | 
    
    | 316 |  | 				settings->call_state == CS_NORMAL ? PY_BODY : PY_FINISH_BODY); | 
    
    | 317 |  | 		CHP(status); | 
    
    | 318 |  | 		switch (status = handler_returned(settings, current_frame, current_frame->subName, false)) { | 
    
    | 319 |  | 		case INTERP_EXECUTE_FINISH: | 
    
    | 320 |  | 		    settings->call_state = CS_REEXEC_PYBODY; | 
    
    | 321 |  | 		    return status; | 
    
    | 322 |  | 		default: | 
    
    | 323 |  | 		    settings->call_state = CS_NORMAL; | 
    
    | 324 |  | 		    settings->sequence_number = previous_frame->sequence_number; | 
    
    | 325 |  | 		    CHP(status); | 
    
    | 326 |  | 		    // epilog is not supported on python body -  makes no sense | 
    
    | 327 |  | 		    CHP(leave_context(settings,false)); | 
    
    | 328 |  | 		    ERP(remap_finished(-cblock->phase)); | 
    
    | 329 |  | 		} | 
    
    | 330 |  | 	    } | 
    
    | 331 |  |  | 
    
    | 332 |  | 	    // call the NGC remap procedure | 
    
    | 333 |  | 	    assert(settings->call_state == CS_NORMAL); | 
    
    | 334 |  | 	    if (remap->remap_ngc) { | 
    
    | 335 |  | 		CHP(execute_call(settings, current_frame, | 
    
    | 336 |  | 				 CT_NGC_OWORD_SUB)); | 
    
    | 337 |  | 	    } | 
    
    | 338 |  | 	} | 
    
    | 339 |  |     } | 
    
    | 340 | 363 |     return status; | 
    
    | 341 |  | } | 
    
    | 342 |  |  | 
    
    | 343 |  | // this is executed only for NGC subs, either normal ones or part of a remap | 
    
    | 344 |  | // subs whose name is a Py callable are handled inline in execute_call() | 
    
    | 345 |  | // since there is no corresponding O_return/O_endsub to execute. | 
    
    | 346 | 392 | int Interp::execute_return(setup_pointer settings, context_pointer current_frame,int call_type) | 
    
    | 347 |  | { | 
    
    | 348 | 392 |     int status = INTERP_OK; | 
    
    | 349 |  |  | 
    
    | 350 | 392 |     logOword("execute_return %s type=%s state=%s", | 
    
    | 351 |  | 	     current_frame->subName, | 
    
    | 352 |  | 	     call_typenames[call_type], | 
    
    | 353 |  | 	     call_statenames[settings->call_state]); | 
    
    | 354 |  |  | 
    
    | 355 | 392 |     block_pointer cblock = &CONTROLLING_BLOCK(*settings); | 
    
    | 356 | 392 |     block_pointer eblock = &EXECUTING_BLOCK(*settings); | 
    
    | 357 | 392 |     context_pointer previous_frame = &settings->sub_context[settings->call_level - 1]; | 
    
    | 358 |  |  | 
    
    | 359 |  |     // if level is not zero, in a call | 
    
    | 360 |  |     // otherwise in a defn | 
    
    | 361 |  |     // if we were skipping, no longer | 
    
    | 362 | 392 |     if (settings->skipping_o && (eblock->o_type == O_endsub)) { | 
    
    | 363 | 37 | 	logOword("case O_%s -- no longer skipping to:|%s|", | 
    
    | 364 |  | 		 (eblock->o_type == O_endsub) ? "endsub" : "return", | 
    
    | 365 |  | 		 settings->skipping_o); | 
    
    | 366 | 37 | 	settings->skipping_o = NULL; | 
    
    | 367 |  |     } | 
    
    | 368 |  |  | 
    
    | 369 | 392 |     switch (call_type) { | 
    
    | 370 |  |  | 
    
    | 371 |  |     case CT_REMAP: | 
    
    | 372 |  | 	switch (settings->call_state) { | 
    
    | 373 |  | 	case CS_NORMAL: | 
    
    | 374 |  |    	case CS_REEXEC_EPILOG: | 
    
    | 375 |  | 	    if (cblock->executing_remap && cblock->executing_remap->epilog_func) { | 
    
    | 376 |  | 		if (settings->call_state ==  CS_REEXEC_EPILOG) | 
    
    | 377 |  | 		    CHP(read_inputs(settings)); | 
    
    | 378 |  | 		status = pycall(settings, current_frame, REMAP_MODULE, | 
    
    | 379 |  | 	    			cblock->executing_remap->epilog_func, | 
    
    | 380 |  | 				settings->call_state == CS_NORMAL ? PY_EPILOG : PY_FINISH_EPILOG); | 
    
    | 381 |  | 		CHP(status); | 
    
    | 382 |  | 		switch (status = handler_returned(settings, current_frame, current_frame->subName, false)) { | 
    
    | 383 |  | 		case INTERP_EXECUTE_FINISH: | 
    
    | 384 |  | 		    settings->call_state = CS_REEXEC_EPILOG; | 
    
    | 385 |  | 		    eblock->call_type = CT_REMAP; | 
    
    | 386 |  | 		    CHP(status); | 
    
    | 387 |  | 		default: | 
    
    | 388 |  | 		    settings->call_state = CS_NORMAL; | 
    
    | 389 |  | 		    settings->sequence_number = previous_frame->sequence_number; | 
    
    | 390 |  | 		    CHP(status); | 
    
    | 391 |  | 		    // leave_context() is done by falling through into CT_NGC_OWORD_SUB code | 
    
    | 392 |  | 		} | 
    
    | 393 |  | 	    } | 
    
    | 394 |  | 	} | 
    
    | 395 |  | 	// fall through to normal NGC return handling | 
    
    | 396 |  |     case CT_NGC_OWORD_SUB: | 
    
    | 397 | 392 | 	if (settings->call_level != 0) { | 
    
    | 398 |  |  | 
    
    | 399 |  | 	    // restore subroutine parameters. | 
    
    | 400 | 10620 | 	    for(int i = 0; i < INTERP_SUB_PARAMS; i++) { | 
    
    | 401 | 10620 | 		settings->parameters[i+INTERP_FIRST_SUBROUTINE_PARAM] = | 
    
    | 402 | 10620 | 		    previous_frame->saved_params[i]; | 
    
    | 403 |  | 	    } | 
    
    | 404 |  |  | 
    
    | 405 |  | 	    // file at this level was marked as closed, so dont reopen. | 
    
    | 406 | 354 | 	    if (previous_frame->position == -1) { | 
    
    | 407 | 2 | 		settings->file_pointer = NULL; | 
    
    | 408 | 2 | 		strcpy(settings->filename, ""); | 
    
    | 409 |  | 	    } else { | 
    
    | 410 | 352 | 		if(settings->file_pointer == NULL) { | 
    
    | 411 |  | 		    ERS(NCE_FILE_NOT_OPEN); | 
    
    | 412 |  | 		} | 
    
    | 413 |  | 		//!!!KL must open the new file, if changed | 
    
    | 414 | 352 | 		if (0 != strcmp(settings->filename, previous_frame->filename))  { | 
    
    | 415 | 2 | 		    fclose(settings->file_pointer); | 
    
    | 416 | 2 | 		    settings->file_pointer = fopen(previous_frame->filename, "r"); | 
    
    | 417 | 2 | 		    if (settings->file_pointer == NULL)  { | 
    
    | 418 | 1 | 			ERS(NCE_CANNOT_REOPEN_FILE, | 
    
    | 419 |  | 			    previous_frame->filename, | 
    
    | 420 |  | 			    strerror(errno)); | 
    
    | 421 |  | 		    } | 
    
    | 422 | 1 | 		    strcpy(settings->filename, previous_frame->filename); | 
    
    | 423 |  | 		} | 
    
    | 424 | 351 | 		fseek(settings->file_pointer, previous_frame->position, SEEK_SET); | 
    
    | 425 | 351 | 		settings->sequence_number = previous_frame->sequence_number; | 
    
    | 426 | 351 | 		logOword("endsub/return: %s:%d pos=%ld", | 
    
    | 427 |  | 			 settings->filename,previous_frame->sequence_number, | 
    
    | 428 |  | 			 previous_frame->position); | 
    
    | 429 |  |  | 
    
    | 430 |  | 	    } | 
    
    | 431 |  | 	    // cleanups on return: | 
    
    | 432 | 353 | 	    CHP(leave_context(settings, true)); | 
    
    | 433 |  |  | 
    
    | 434 |  | 	    // if this was a remap frame we're done | 
    
    | 435 | 353 | 	    if (current_frame->context_status & REMAP_FRAME) { | 
    
    | 436 |  | 		CHP(remap_finished(-cblock->phase)); | 
    
    | 437 |  | 	    } | 
    
    | 438 |  |  | 
    
    | 439 |  |  | 
    
    | 440 | 353 | 	    settings->sub_name = 0; | 
    
    | 441 | 353 | 	    if (previous_frame->subName)  { | 
    
    | 442 | 353 | 		settings->sub_name = previous_frame->subName; | 
    
    | 443 |  | 	    } else { | 
    
    | 444 |  | 		settings->sub_name = NULL; | 
    
    | 445 |  | 	    } | 
    
    | 446 |  | 	} else { // call_level == 0 | 
    
    | 447 |  | 	    // a definition | 
    
    | 448 | 38 | 	    if (eblock->o_type == O_endsub) { | 
    
    | 449 | 37 | 		CHKS((settings->defining_sub != 1), NCE_NOT_IN_SUBROUTINE_DEFN); | 
    
    | 450 |  | 		// no longer skipping or defining | 
    
    | 451 | 37 | 		if (settings->skipping_o)  { | 
    
    | 452 |  | 		    logOword("case O_endsub in defn -- no longer skipping to:|%s|", | 
    
    | 453 |  | 			     settings->skipping_o); | 
    
    | 454 |  | 		    settings->skipping_o = NULL; | 
    
    | 455 |  | 		} | 
    
    | 456 | 37 | 		settings->defining_sub = 0; | 
    
    | 457 | 37 | 		settings->sub_name = NULL; | 
    
    | 458 |  | 	    } | 
    
    | 459 |  | 	} | 
    
    | 460 |  |     } | 
    
    | 461 | 391 |     return status; | 
    
    | 462 |  | } | 
    
    | 463 |  |  | 
    
    | 464 |  | // | 
    
    | 465 |  | // TESTME!!! MORE THOROUGHLY !!!KL | 
    
    | 466 |  | // | 
    
    | 467 |  | // In the past, calls had to be to predefined subs | 
    
    | 468 |  | // | 
    
    | 469 |  | // Now they don't. Do things in the following sequence: | 
    
    | 470 |  | // 1 -- if o_word is already defined, just go back to it, else | 
    
    | 471 |  | // 2 -- if there is a file with the name of the o_word, | 
    
    | 472 |  | //             open it and start skipping (as in 3, below) | 
    
    | 473 |  | // 3 -- skip to the o_word (will be an error if not found) | 
    
    | 474 |  | // | 
    
    | 475 | 431 | int Interp::control_back_to( block_pointer block, // pointer to block | 
    
    | 476 |  | 			     setup_pointer settings)   // pointer to machine settings | 
    
    | 477 |  | { | 
    
    | 478 |  |     static char name[] = "control_back_to"; | 
    
    | 479 |  |     char newFileName[PATH_MAX+1]; | 
    
    | 480 |  |     FILE *newFP; | 
    
    | 481 |  |     offset_map_iterator it; | 
    
    | 482 |  |     offset_pointer op; | 
    
    | 483 |  |  | 
    
    | 484 | 431 |     logOword("Entered:%s %s", name,block->o_name); | 
    
    | 485 |  |  | 
    
    | 486 | 862 |     it = settings->offset_map.find(block->o_name); | 
    
    | 487 | 862 |     if (it != settings->offset_map.end()) { | 
    
    | 488 | 425 | 	op = &it->second; | 
    
    | 489 | 425 | 	if ((settings->filename[0] != 0) & | 
    
    | 490 |  | 	    (settings->file_pointer == NULL))  { | 
    
    | 491 |  | 	    ERS(NCE_FILE_NOT_OPEN); | 
    
    | 492 |  | 	} | 
    
    | 493 | 425 | 	if (0 != strcmp(settings->filename, | 
    
    | 494 | 425 | 			op->filename)) { | 
    
    | 495 |  | 	    // open the new file... | 
    
    | 496 | 1 | 	    newFP = fopen(op->filename, "r"); | 
    
    | 497 |  | 	    // set the line number | 
    
    | 498 | 1 | 	    settings->sequence_number = 0; | 
    
    | 499 | 1 |             strncpy(settings->filename, op->filename, sizeof(settings->filename)); | 
    
    | 500 | 1 |             if (settings->filename[sizeof(settings->filename)-1] != '\0') { | 
    
    | 501 |  |                 fclose(settings->file_pointer); | 
    
    | 502 |  |                 logOword("filename too long: %s", op->filename); | 
    
    | 503 |  |                 ERS(NCE_UNABLE_TO_OPEN_FILE, op->filename); | 
    
    | 504 |  |             } | 
    
    | 505 |  |  | 
    
    | 506 | 1 | 	    if (newFP) { | 
    
    | 507 |  | 		// close the old file... | 
    
    | 508 | 1 | 		if (settings->file_pointer) // only close if it was open | 
    
    | 509 |  | 		    fclose(settings->file_pointer); | 
    
    | 510 | 1 | 		settings->file_pointer = newFP; | 
    
    | 511 |  | 	    } else { | 
    
    | 512 |  | 		logOword("Unable to open file: %s", settings->filename); | 
    
    | 513 |  | 		ERS(NCE_UNABLE_TO_OPEN_FILE,settings->filename); | 
    
    | 514 |  | 	    } | 
    
    | 515 |  | 	} | 
    
    | 516 | 425 | 	if (settings->file_pointer) { // only seek if it was open | 
    
    | 517 |  | 	    fseek(settings->file_pointer, | 
    
    | 518 | 425 | 		  op->offset, SEEK_SET); | 
    
    | 519 |  | 	} | 
    
    | 520 | 425 | 	settings->sequence_number = op->sequence_number; | 
    
    | 521 | 425 | 	return INTERP_OK; | 
    
    | 522 |  |     } | 
    
    | 523 | 6 |     newFP = find_ngc_file(settings, block->o_name, newFileName); | 
    
    | 524 |  |  | 
    
    | 525 | 6 |     if (newFP) { | 
    
    | 526 | 5 | 	logOword("fopen: |%s| OK", newFileName); | 
    
    | 527 | 5 | 	settings->sequence_number = 0; | 
    
    | 528 |  |  | 
    
    | 529 |  | 	// close the old file... | 
    
    | 530 | 5 | 	if (settings->file_pointer) | 
    
    | 531 | 4 | 	    fclose(settings->file_pointer); | 
    
    | 532 | 5 | 	settings->file_pointer = newFP; | 
    
    | 533 | 5 |         strncpy(settings->filename, newFileName, sizeof(settings->filename)); | 
    
    | 534 | 5 |         if (settings->filename[sizeof(settings->filename)-1] != '\0') { | 
    
    | 535 |  |             logOword("new filename '%s' is too long (max len %zu)\n", newFileName, sizeof(settings->filename)-1); | 
    
    | 536 |  |             settings->filename[sizeof(settings->filename)-1] = '\0'; // oh well, truncate the filename | 
    
    | 537 |  |         } | 
    
    | 538 |  |     } else { | 
    
    | 539 | 1 | 	char *dirname = getcwd(NULL, 0); | 
    
    | 540 | 1 | 	logOword("fopen: |%s| failed CWD:|%s|", newFileName, | 
    
    | 541 |  | 		 dirname); | 
    
    | 542 | 1 | 	free(dirname); | 
    
    | 543 |  |     } | 
    
    | 544 |  |  | 
    
    | 545 | 6 |     settings->skipping_o = block->o_name; // start skipping | 
    
    | 546 | 6 |     settings->skipping_to_sub = block->o_name; // start skipping | 
    
    | 547 | 6 |     settings->skipping_start = settings->sequence_number; | 
    
    | 548 | 6 |     return INTERP_OK; | 
    
    | 549 |  | } | 
    
    | 550 |  |  | 
    
    | 551 |  |  | 
    
    | 552 | 7 | int Interp::handler_returned( setup_pointer settings,  context_pointer active_frame, | 
    
    | 553 |  | 			      const char *name, bool osub) | 
    
    | 554 |  | { | 
    
    | 555 | 7 |     int status = INTERP_OK; | 
    
    | 556 |  |  | 
    
    | 557 | 7 |     switch (active_frame->pystuff.impl->py_return_type) { | 
    
    | 558 |  |     case RET_YIELD: | 
    
    | 559 |  | 	// yield <integer> was executed | 
    
    | 560 |  | 	CHP(active_frame->pystuff.impl->py_returned_int); | 
    
    | 561 |  | 	break; | 
    
    | 562 |  |  | 
    
    | 563 |  |     case RET_STOPITERATION:  // a bare 'return' in a generator - treat as INTERP_OK | 
    
    | 564 |  |     case RET_NONE: | 
    
    | 565 |  | 	break; | 
    
    | 566 |  |  | 
    
    | 567 |  |     case RET_DOUBLE: | 
    
    | 568 | 3 | 	if (osub) { // float values are ok for osubs | 
    
    | 569 | 3 | 	    settings->return_value = active_frame->pystuff.impl->py_returned_double; | 
    
    | 570 | 3 | 	    settings->value_returned = 1; | 
    
    | 571 |  | 	} else { | 
    
    | 572 |  | 	    ERS("handler_returned: %s returned double: %f - invalid", | 
    
    | 573 |  | 			name, active_frame->pystuff.impl->py_returned_double); | 
    
    | 574 |  | 	} | 
    
    | 575 | 3 | 	break; | 
    
    | 576 |  |  | 
    
    | 577 |  |     case RET_INT: | 
    
    | 578 |  | 	if (osub) { // let's be liberal with types - widen to double return value | 
    
    | 579 |  | 	    settings->return_value = (double) active_frame->pystuff.impl->py_returned_int; | 
    
    | 580 |  | 	    settings->value_returned = 1; | 
    
    | 581 |  | 	} else | 
    
    | 582 |  | 	    return active_frame->pystuff.impl->py_returned_int; | 
    
    | 583 |  |  | 
    
    | 584 |  |     case RET_ERRORMSG: | 
    
    | 585 |  | 	status = INTERP_ERROR; | 
    
    | 586 |  | 	break; | 
    
    | 587 |  |  | 
    
    | 588 |  |     } | 
    
    | 589 | 7 |     return status; | 
    
    | 590 |  | } | 
    
    | 591 |  |  | 
    
    | 592 |  |  | 
    
    | 593 |  | // prepare a new call frame. | 
    
    | 594 | 363 | int Interp::enter_context(setup_pointer settings, block_pointer block) | 
    
    | 595 |  | { | 
    
    | 596 | 363 |     logOword("enter_context cl=%d->%d type=%s", | 
    
    | 597 |  | 	     settings->call_level, settings->call_level+1, | 
    
    | 598 |  | 	     call_typenames[block->call_type]); | 
    
    | 599 |  |  | 
    
    | 600 | 363 |     settings->call_level++; | 
    
    | 601 | 363 |     if (settings->call_level >= INTERP_SUB_ROUTINE_LEVELS) { | 
    
    | 602 |  | 	ERS(NCE_TOO_MANY_SUBROUTINE_LEVELS); | 
    
    | 603 |  |     } | 
    
    | 604 | 363 |     context_pointer frame = &settings->sub_context[settings->call_level]; | 
    
    | 605 |  |     // mark frame for finishing remap | 
    
    | 606 | 363 |     frame->context_status = (block->call_type  == CT_REMAP) ? REMAP_FRAME : 0; | 
    
    | 607 | 363 |     frame->subName = block->o_name; | 
    
    | 608 | 363 |     frame->pystuff.impl->py_returned_int = 0; | 
    
    | 609 | 363 |     frame->pystuff.impl->py_returned_double = 0.0; | 
    
    | 610 | 363 |     frame->pystuff.impl->py_return_type = -1; | 
    
    | 611 | 363 |     frame->call_type = block->call_type; // distinguish call frames: oword,python,remap | 
    
    | 612 | 363 |     return INTERP_OK; | 
    
    | 613 |  | } | 
    
    | 614 |  |  | 
    
    | 615 | 360 | int Interp::leave_context(setup_pointer settings, bool restore) | 
    
    | 616 |  | { | 
    
    | 617 | 360 |     context_pointer leaving_frame = &settings->sub_context[settings->call_level]; | 
    
    | 618 |  |  | 
    
    | 619 | 360 |    if (settings->call_level < 1) { | 
    
    | 620 |  | 	ERS(NCE_CALL_STACK_UNDERRUN); | 
    
    | 621 |  |     } | 
    
    | 622 | 360 |     logOword("leave_context cl=%d->%d  type=%s state=%s" , | 
    
    | 623 |  | 	     settings->call_level, settings->call_level-1, | 
    
    | 624 |  | 	     call_typenames[leaving_frame->call_type], | 
    
    | 625 |  | 	     call_statenames[settings->call_state]); | 
    
    | 626 |  |  | 
    
    | 627 | 360 |     free_named_parameters(leaving_frame); | 
    
    | 628 | 360 |     leaving_frame->subName = NULL; | 
    
    | 629 | 360 |     settings->call_level--;  // drop back | 
    
    | 630 |  |  | 
    
    | 631 | 360 |     if (restore && ((leaving_frame->context_status & | 
    
    | 632 |  | 		     (CONTEXT_RESTORE_ON_RETURN|CONTEXT_VALID)) == | 
    
    | 633 |  | 		    (CONTEXT_RESTORE_ON_RETURN|CONTEXT_VALID))) { | 
    
    | 634 |  | 	// a valid previous context was marked by an M73 as auto-restore | 
    
    | 635 |  |  | 
    
    | 636 |  |     	// NB: this means an M71 invalidate context will prevent an | 
    
    | 637 |  |     	// auto-restore on return/endsub | 
    
    | 638 |  |     	CHP(restore_settings(settings, settings->call_level + 1)); | 
    
    | 639 |  |     } | 
    
    | 640 |  |     return INTERP_OK; | 
    
    | 641 |  | } | 
    
    | 642 |  |  | 
    
    | 643 |  | /************************************************************************/ | 
    
    | 644 |  | /* convert_control_functions | 
    
    | 645 |  |  | 
    
    | 646 |  | Returned Value: int (INTERP_OK) | 
    
    | 647 |  | Side effects: | 
    
    | 648 |  |    Changes the flow of control. | 
    
    | 649 |  |  | 
    
    | 650 |  | Called by: execute | 
    
    | 651 |  |  | 
    
    | 652 |  | Calls: control_skip_to | 
    
    | 653 |  |        control_back_to | 
    
    | 654 |  |        control_save_offset | 
    
    | 655 |  | */ | 
    
    | 656 |  |  | 
    
    | 657 | 3413 | int Interp::convert_control_functions(block_pointer block, // pointer to a block of RS274/NGC instructions | 
    
    | 658 |  | 				      setup_pointer settings)  // pointer to machine settings | 
    
    | 659 |  | { | 
    
    | 660 | 3413 |     int status = INTERP_OK; | 
    
    | 661 |  |     context_pointer current_frame; | 
    
    | 662 | 3413 |     offset_pointer op = NULL; | 
    
    | 663 |  |  | 
    
    | 664 | 3413 |     logOword("convert_control_functions %s", o_ops[block->o_type]); | 
    
    | 665 |  |  | 
    
    | 666 |  |     // must skip if skipping | 
    
    | 667 | 3413 |     if (settings->skipping_o && (0 != strcmp(settings->skipping_o, block->o_name)))  { | 
    
    | 668 | 840 | 	logOword("skipping to line: |%s|", settings->skipping_o); | 
    
    | 669 |  | 	return INTERP_OK; | 
    
    | 670 |  |     } | 
    
    | 671 | 2573 |     if (settings->skipping_to_sub && (block->o_type != O_sub)) { | 
    
    | 672 | 1 | 	logOword("skipping to sub: |%s|", settings->skipping_to_sub); | 
    
    | 673 |  | 	return INTERP_OK; | 
    
    | 674 |  |     } | 
    
    | 675 |  |  | 
    
    | 676 |  |     // if skipping_o was set, we are now on a line which contains that O-word. | 
    
    | 677 |  |     // if skipping_to_sub was set, we are now on the 'O-name sub' definition line. | 
    
    | 678 |  |  | 
    
    | 679 | 2572 |     switch (block->o_type) { | 
    
    | 680 |  |  | 
    
    | 681 |  |     case O_none: | 
    
    | 682 |  | 	// not an error because we use this to signal that we | 
    
    | 683 |  | 	// are not evaluating functions | 
    
    | 684 |  | 	break; | 
    
    | 685 |  |  | 
    
    | 686 |  |     case O_sub: | 
    
    | 687 |  | 	// if the level is not zero, this is a call | 
    
    | 688 |  | 	// not the definition | 
    
    | 689 |  | 	// if we were skipping, no longer | 
    
    | 690 | 393 | 	if (settings->skipping_o) { | 
    
    | 691 | 6 | 	    logOword("sub(o_|%s|) was skipping to here", settings->skipping_o); | 
    
    | 692 |  | 	    // skipping to a sub means that we must define this now | 
    
    | 693 | 6 | 	    CHP(control_save_offset( block, settings)); | 
    
    | 694 | 6 | 	    logOword("no longer skipping to:|%s|", settings->skipping_o); | 
    
    | 695 | 6 | 	    settings->skipping_o = NULL; // this IS our block number | 
    
    | 696 |  | 	} | 
    
    | 697 | 393 | 	settings->skipping_to_sub = NULL; // this IS our block number | 
    
    | 698 |  |  | 
    
    | 699 | 393 | 	if (settings->call_level != 0) { | 
    
    | 700 | 356 | 	    logOword("call:%f:%f:%f", | 
    
    | 701 |  | 		     settings->parameters[1], | 
    
    | 702 |  | 		     settings->parameters[2], | 
    
    | 703 |  | 		     settings->parameters[3]); | 
    
    | 704 |  | 	} else { | 
    
    | 705 |  | 	    // a definition. We're on the O<name> sub line. | 
    
    | 706 | 37 | 	    logOword("started a subroutine defn: %s",block->o_name); | 
    
    | 707 | 37 | 	    CHKS((settings->defining_sub == 1), NCE_NESTED_SUBROUTINE_DEFN); | 
    
    | 708 | 37 | 	    CHP(control_save_offset( block, settings)); | 
    
    | 709 |  |  | 
    
    | 710 |  | 	    // start skipping to the corresponding ensub. | 
    
    | 711 | 37 | 	    settings->skipping_o = block->o_name; | 
    
    | 712 | 37 | 	    settings->skipping_start = settings->sequence_number; | 
    
    | 713 | 37 | 	    settings->defining_sub = 1; | 
    
    | 714 | 37 | 	    settings->sub_name = block->o_name; | 
    
    | 715 | 37 | 	    logOword("will now skip to: |%s|", settings->sub_name); | 
    
    | 716 |  | 	} | 
    
    | 717 |  | 	break; | 
    
    | 718 |  |  | 
    
    | 719 |  |     case O_endsub: | 
    
    | 720 |  |     case O_return: | 
    
    | 721 |  |  | 
    
    | 722 | 430 | 	if  ((settings->call_level == 0) && | 
    
    | 723 | 38 | 	     (settings->sub_name == NULL)) { | 
    
    | 724 |  | 	    // detect a standalone 'o<label> return|endsub' | 
    
    | 725 |  | 	    OERR(_("%d: not in a subroutine definition: '%s'"), | 
    
    | 726 |  | 		 settings->sequence_number, settings->linetext); | 
    
    | 727 |  | 	} | 
    
    | 728 |  |  | 
    
    | 729 |  | 	// proper label semantics (only refer to defined sub, within sub defn etc) | 
    
    | 730 |  | 	// is handled in read_o() for return & endsub | 
    
    | 731 | 392 | 	current_frame = &settings->sub_context[settings->call_level]; | 
    
    | 732 | 392 | 	CHP(execute_return(settings, current_frame, | 
    
    | 733 |  | 			   current_frame->call_type)); | 
    
    | 734 |  | 	break; | 
    
    | 735 |  |  | 
    
    | 736 |  |     case O_call: | 
    
    | 737 |  | 	// only enter new frame if not reexecuting a Python handler | 
    
    | 738 |  | 	// which returned INTERP_EXECUTE_FINISH | 
    
    | 739 | 363 | 	if (settings->call_state == CS_NORMAL) { | 
    
    | 740 | 363 | 	    CHP(enter_context(settings, block)); | 
    
    | 741 |  | 	} | 
    
    | 742 | 363 | 	current_frame = &settings->sub_context[settings->call_level]; | 
    
    | 743 | 363 | 	CHP(execute_call(settings, current_frame, | 
    
    | 744 |  | 			 current_frame->call_type)); | 
    
    | 745 |  | 	break; | 
    
    | 746 |  |  | 
    
    | 747 |  |       case O_do: | 
    
    | 748 |  | 	// if we were skipping, no longer | 
    
    | 749 | 75 | 	settings->skipping_o = NULL; | 
    
    | 750 |  | 	// save the loop point | 
    
    | 751 |  | 	// we hit this again on loop back -- so test first | 
    
    | 752 | 75 | 	if(INTERP_OK != control_find_oword(block, settings, &op)) // &index)) | 
    
    | 753 |  | 	    { | 
    
    | 754 |  | 		// save offset if not found in offset table | 
    
    | 755 | 9 | 		CHP(control_save_offset( block, settings)); | 
    
    | 756 |  | 	    } | 
    
    | 757 |  | 	break; | 
    
    | 758 |  |  | 
    
    | 759 |  |     case O_repeat: | 
    
    | 760 |  | 	if (control_find_oword(block, settings, &op) == INTERP_OK) { | 
    
    | 761 |  | 	    if (settings->sequence_number != (op->sequence_number + 1)) | 
    
    | 762 |  | 	        OERR(_("%d: duplicate O-word label: '%s' - defined in line %d"), | 
    
    | 763 |  | 		    settings->sequence_number, | 
    
    | 764 |  | 		    settings->linetext, op->sequence_number + 1); | 
    
    | 765 |  | 	} else | 
    
    | 766 |  | 	    CHP(control_save_offset(block, settings)); | 
    
    | 767 |  |  | 
    
    | 768 |  | 	// if we were skipping, no longer | 
    
    | 769 |  | 	settings->skipping_o = NULL; | 
    
    | 770 |  | 	status = control_find_oword(block, settings, &op); // &index); | 
    
    | 771 |  |  | 
    
    | 772 |  | 	// test if not already seen OR | 
    
    | 773 |  | 	// if seen and this is a repeat | 
    
    | 774 |  | 	if ((status != INTERP_OK) || | 
    
    | 775 |  | 	    (op->type == block->o_type))	{ | 
    
    | 776 |  | 	    // this is the beginning of a 'repeat' loop | 
    
    | 777 |  | 	    // add it to the table if not already there | 
    
    | 778 |  | 	    if(status != INTERP_OK) | 
    
    | 779 |  | 		CHP(control_save_offset( block, settings)); | 
    
    | 780 |  |  | 
    
    | 781 |  | 	    // note the repeat count.  it should only be calculated at the | 
    
    | 782 |  | 	    // start of the repeat loop. | 
    
    | 783 |  | 	    control_find_oword(block, settings, &op); // &index); | 
    
    | 784 |  | 	    if(op->repeat_count == -1) | 
    
    | 785 |  | 		op->repeat_count = | 
    
    | 786 |  | 		    round_to_int(settings->test_value); | 
    
    | 787 |  |  | 
    
    | 788 |  | 	    // are we still repeating? | 
    
    | 789 |  | 	    if(op->repeat_count > 0) { | 
    
    | 790 |  | 		// execute forward | 
    
    | 791 |  | 		logOword("executing forward: [%s] in 'repeat' test value-- %g", | 
    
    | 792 |  | 			 block->o_name, settings->test_value); | 
    
    | 793 |  | 		// one less repeat remains | 
    
    | 794 |  | 		op->repeat_count--; | 
    
    | 795 |  | 	    } else { | 
    
    | 796 |  | 		// skip forward | 
    
    | 797 |  | 		logOword("skipping forward: [%s] in 'repeat'", | 
    
    | 798 |  | 			 block->o_name); | 
    
    | 799 |  | 		settings->skipping_o = block->o_name; | 
    
    | 800 |  | 		settings->skipping_start = settings->sequence_number; | 
    
    | 801 |  | 		// cause the repeat count to be recalculated | 
    
    | 802 |  | 		// if we do this loop again | 
    
    | 803 |  | 		op->repeat_count = -1; | 
    
    | 804 |  | 	    } | 
    
    | 805 |  | 	} | 
    
    | 806 |  | 	break; | 
    
    | 807 |  |  | 
    
    | 808 |  |     case O_while: | 
    
    | 809 | 97 | 	if (control_find_oword(block, settings, &op) == INTERP_OK) { | 
    
    | 810 | 115 | 	    if ((op->type != O_do) && | 
    
    | 811 | 20 | 		(settings->sequence_number != (op->sequence_number + 1))) | 
    
    | 812 |  | 	        OERR(_("%d: duplicate O-word label: '%s' - defined in line %d"), | 
    
    | 813 |  | 		    settings->sequence_number, | 
    
    | 814 |  | 		    settings->linetext, op->sequence_number + 1); | 
    
    | 815 |  | 	} else | 
    
    | 816 |  | 	    // record only if this is a while/endwhile loop | 
    
    | 817 | 2 | 	    CHP(control_save_offset(block, settings)); | 
    
    | 818 |  |  | 
    
    | 819 |  | 	// if we were skipping, no longer | 
    
    | 820 | 97 | 	settings->skipping_o = NULL; | 
    
    | 821 | 97 | 	status = control_find_oword(block, settings, &op); //  &index); | 
    
    | 822 |  |  | 
    
    | 823 |  | 	// test if not already seen OR | 
    
    | 824 |  | 	// if seen and this is a while (alternative is that it is a do) | 
    
    | 825 | 194 | 	if ((status != INTERP_OK) || | 
    
    | 826 | 97 | 	    (op->type == block->o_type))  { | 
    
    | 827 |  |  | 
    
    | 828 |  | 	    // this is the beginning of a 'while' loop | 
    
    | 829 |  | 	    // add it to the table if not already there | 
    
    | 830 | 22 | 	    if (status != INTERP_OK) | 
    
    | 831 |  | 		CHP(control_save_offset( block, settings)); | 
    
    | 832 |  |  | 
    
    | 833 |  | 	    // test the condition | 
    
    | 834 | 22 | 	    if (settings->test_value != 0.0) // true - execute forward | 
    
    | 835 | 22 | 		logOword("executing forward: [%s] in 'while'", | 
    
    | 836 |  | 			 block->o_name); | 
    
    | 837 |  | 	    else  { | 
    
    | 838 |  | 		// false -  skip forward | 
    
    | 839 |  | 		logOword("skipping forward: [%s] in 'while'", | 
    
    | 840 |  | 			 block->o_name); | 
    
    | 841 |  | 		settings->skipping_o = block->o_name; | 
    
    | 842 |  | 		settings->skipping_start = settings->sequence_number; | 
    
    | 843 |  | 	    } | 
    
    | 844 |  | 	} else { | 
    
    | 845 |  | 	    // this is the end of a 'do' | 
    
    | 846 |  | 	    // test the condition | 
    
    | 847 | 75 | 	    if ((settings->test_value != 0.0) && !settings->doing_break) { | 
    
    | 848 |  | 		// true - loop on back | 
    
    | 849 | 55 | 		logOword("looping back to: [%s] in 'do while'", | 
    
    | 850 |  | 			 block->o_name); | 
    
    | 851 | 55 | 		CHP(control_back_to(block, settings)); | 
    
    | 852 |  | 	    } else { | 
    
    | 853 |  | 		// false | 
    
    | 854 | 20 | 		logOword("not looping back to: [%s] in 'do while'", | 
    
    | 855 |  | 			 block->o_name); | 
    
    | 856 | 20 | 		settings->doing_break = 0; | 
    
    | 857 |  | 	    } | 
    
    | 858 |  | 	} | 
    
    | 859 |  | 	break; | 
    
    | 860 |  |  | 
    
    | 861 |  |  | 
    
    | 862 |  |     case O_if: | 
    
    | 863 | 405 | 	if (control_find_oword(block, settings, &op) == INTERP_OK) { | 
    
    | 864 | 365 | 	    if (settings->sequence_number != (op->sequence_number + 1)) | 
    
    | 865 |  | 	        OERR(_("%d: duplicate O-word label - already defined in line %d: '%s'"), | 
    
    | 866 |  | 		    settings->sequence_number, op->sequence_number + 1, | 
    
    | 867 |  | 		    settings->linetext); | 
    
    | 868 |  | 	} else | 
    
    | 869 | 40 | 	    CHP(control_save_offset(block, settings)); | 
    
    | 870 |  |  | 
    
    | 871 | 405 | 	if (settings->test_value != 0.0) { | 
    
    | 872 |  | 	    //true | 
    
    | 873 | 278 | 	    logOword("executing forward: [%s] in 'if'", | 
    
    | 874 |  | 		     block->o_name); | 
    
    | 875 | 278 | 	    settings->skipping_o = NULL; | 
    
    | 876 | 278 | 	    settings->executed_if = 1; | 
    
    | 877 |  | 	} else { | 
    
    | 878 |  | 	    //false | 
    
    | 879 | 127 | 	    logOword("skipping forward: [%s] in 'if'", | 
    
    | 880 |  | 		     block->o_name); | 
    
    | 881 | 127 | 	    settings->skipping_o = block->o_name; | 
    
    | 882 | 127 | 	    settings->skipping_start = settings->sequence_number; | 
    
    | 883 | 127 | 	    settings->executed_if = 0; | 
    
    | 884 |  | 	} | 
    
    | 885 |  | 	break; | 
    
    | 886 |  |  | 
    
    | 887 |  |     case O_elseif: | 
    
    | 888 |  | 	if (control_find_oword(block, settings, &op) != INTERP_OK) | 
    
    | 889 |  | 	    OERR(_("%d: undefined O-word label: '%s'"), | 
    
    | 890 |  | 		settings->sequence_number, settings->linetext); | 
    
    | 891 |  | 	if (op->type != O_if) | 
    
    | 892 |  | 	    OERR(_("%d: no matching 'if' label: '%s' (found '%s' in line %d)"), | 
    
    | 893 |  | 		settings->sequence_number, settings->linetext, | 
    
    | 894 |  | 		o_ops[op->type] + 2, op->sequence_number + 1); | 
    
    | 895 |  |  | 
    
    | 896 |  | 	if ((settings->skipping_o) && | 
    
    | 897 |  | 	    (0 != strcmp(settings->skipping_o, block->o_name)))  { | 
    
    | 898 |  |  | 
    
    | 899 |  | 	    //!!!KL -- the if conditions here say that we were skipping | 
    
    | 900 |  | 	    //!!!KL but that the target o_name is not ours. | 
    
    | 901 |  | 	    //!!!KL so we should continue skipping -- that's not what | 
    
    | 902 |  | 	    //!!!KL the code below says. | 
    
    | 903 |  |  | 
    
    | 904 |  | 	    // mah: so? | 
    
    | 905 |  | #if 0 | 
    
    | 906 |  | 	    // we were not skipping -- start skipping | 
    
    | 907 |  | 	    logOword("start skipping forward: [%s] in 'elseif'", | 
    
    | 908 |  | 		     block->o_name); | 
    
    | 909 |  | 	    settings->skipping_o = block->o_name; | 
    
    | 910 |  | 	    settings->skipping_start = settings->sequence_number; | 
    
    | 911 |  | 	    return INTERP_OK; | 
    
    | 912 |  | #else | 
    
    | 913 |  | 	    // we were skipping -- continue skipping | 
    
    | 914 |  | 	    logOword("continue skipping forward: [%s] in 'elseif'", | 
    
    | 915 |  | 		     block->o_name); | 
    
    | 916 |  | 	    return INTERP_OK; | 
    
    | 917 |  | #endif | 
    
    | 918 |  | 	} | 
    
    | 919 |  |  | 
    
    | 920 |  | 	// we were skipping | 
    
    | 921 |  | 	// but were we ever not skipping | 
    
    | 922 |  | 	if (settings->executed_if) { | 
    
    | 923 |  | 	    // we have already executed, keep on skipping | 
    
    | 924 |  | 	    logOword("already executed, continue  " | 
    
    | 925 |  | 		     "skipping forward: [%s] in 'elseif'", | 
    
    | 926 |  | 		     block->o_name); | 
    
    | 927 |  | 	    settings->skipping_o = block->o_name; | 
    
    | 928 |  | 	    settings->skipping_start = settings->sequence_number; | 
    
    | 929 |  | 	    return INTERP_OK; | 
    
    | 930 |  | 	} | 
    
    | 931 |  |  | 
    
    | 932 |  | 	if (settings->test_value != 0.0) { | 
    
    | 933 |  | 	    //true -- start executing | 
    
    | 934 |  | 	    logOword("start executing forward: [%s] in 'elseif'", | 
    
    | 935 |  | 		     block->o_name); | 
    
    | 936 |  | 	    settings->skipping_o = NULL; | 
    
    | 937 |  | 	    settings->executed_if = 1; | 
    
    | 938 |  | 	} else { | 
    
    | 939 |  | 	    //false | 
    
    | 940 |  | 	    logOword("continue skipping forward: [%s] in 'elseif'", | 
    
    | 941 |  | 		     block->o_name); | 
    
    | 942 |  | 	} | 
    
    | 943 |  | 	break; | 
    
    | 944 |  |  | 
    
    | 945 |  |     case O_else: | 
    
    | 946 | 255 | 	if (control_find_oword(block, settings, &op) != INTERP_OK) | 
    
    | 947 |  | 	    OERR(_("%d: undefined O-word label: '%s'"), | 
    
    | 948 |  | 		settings->sequence_number, settings->linetext); | 
    
    | 949 | 255 | 	if (op->type != O_if) | 
    
    | 950 |  | 	    OERR(_("%d: no matching 'if' label: '%s' (found '%s' in line %d)"), | 
    
    | 951 |  | 		settings->sequence_number, settings->linetext, | 
    
    | 952 |  | 		o_ops[op->type] + 2, op->sequence_number + 1); | 
    
    | 953 |  |  | 
    
    | 954 |  | 	// were we ever not skipping | 
    
    | 955 | 255 | 	if (settings->executed_if) { | 
    
    | 956 |  | 	    // we have already executed, skip | 
    
    | 957 | 192 | 	    logOword("already executed, " | 
    
    | 958 |  | 		     "skipping forward: [%s] in 'else'", | 
    
    | 959 |  | 		     block->o_name); | 
    
    | 960 | 192 | 	    settings->skipping_o = block->o_name; | 
    
    | 961 | 192 | 	    settings->skipping_start = settings->sequence_number; | 
    
    | 962 | 192 | 	    return INTERP_OK; | 
    
    | 963 |  | 	} | 
    
    | 964 |  |  | 
    
    | 965 | 126 | 	if ((settings->skipping_o) && | 
    
    | 966 | 63 | 	    (0 == strcmp(settings->skipping_o, block->o_name))) { | 
    
    | 967 |  | 	    // we were skipping so stop skipping | 
    
    | 968 | 63 | 	    logOword("stop skipping forward: [%s] in 'else'", | 
    
    | 969 |  | 		     block->o_name); | 
    
    | 970 | 63 | 	    settings->executed_if = 1; | 
    
    | 971 | 63 | 	    settings->skipping_o = NULL; | 
    
    | 972 |  | 	} else	{ | 
    
    | 973 |  | 	    // we were not skipping -- so skip | 
    
    | 974 |  | 	    logOword("start skipping forward: [%s] in 'else'", | 
    
    | 975 |  | 		     block->o_name); | 
    
    | 976 |  | 	} | 
    
    | 977 |  | 	break; | 
    
    | 978 |  |  | 
    
    | 979 |  |     case O_endif: | 
    
    | 980 | 402 | 	if (control_find_oword(block, settings, &op) != INTERP_OK) | 
    
    | 981 |  | 	    OERR(_("%d: undefined O-word label: '%s'"), | 
    
    | 982 |  | 		settings->sequence_number, settings->linetext); | 
    
    | 983 | 402 | 	if (op->type != O_if) | 
    
    | 984 |  | 	    OERR(_("%d: no matching label: '%s' (found '%s' in line %d): '%s'"), | 
    
    | 985 |  | 		settings->sequence_number, block->o_name, | 
    
    | 986 |  | 		o_ops[op->type] + 2, op->sequence_number + 1, settings->linetext); | 
    
    | 987 |  |  | 
    
    | 988 |  | 	// stop skipping if we were | 
    
    | 989 | 402 | 	settings->skipping_o = NULL; | 
    
    | 990 | 402 | 	logOword("stop skipping forward: [%s] in 'endif'", | 
    
    | 991 |  | 		 block->o_name); | 
    
    | 992 |  | 	// the KEY -- outside if clearly must have executed | 
    
    | 993 |  | 	// or this would not have executed | 
    
    | 994 | 402 | 	settings->executed_if = 1; | 
    
    | 995 | 402 | 	break; | 
    
    | 996 |  |  | 
    
    | 997 |  |     case O_break: | 
    
    | 998 | 4 | 	if (control_find_oword(block, settings, &op) != INTERP_OK) | 
    
    | 999 |  | 	    OERR(_("%d: undefined O-word label: '%s'"), | 
    
    | 1000 |  | 		settings->sequence_number, settings->linetext); | 
    
    | 1001 | 4 | 	if ((op->type != O_while) && (op->type != O_do)) | 
    
    | 1002 |  | 	    OERR(_("%d: no matching while/do label: '%s' (found '%s' in line %d)"), | 
    
    | 1003 |  | 		settings->sequence_number, settings->linetext, | 
    
    | 1004 |  | 		o_ops[op->type] + 2, op->sequence_number + 1); | 
    
    | 1005 |  |  | 
    
    | 1006 |  |       // start skipping | 
    
    | 1007 | 4 |       settings->skipping_o = block->o_name; | 
    
    | 1008 | 4 |       settings->skipping_start = settings->sequence_number; | 
    
    | 1009 | 4 |       settings->doing_break = 1; | 
    
    | 1010 | 4 |       logOword("start skipping forward: [%s] in 'break'", | 
    
    | 1011 |  | 	      block->o_name); | 
    
    | 1012 |  |       break; | 
    
    | 1013 |  |  | 
    
    | 1014 |  |     case O_continue: | 
    
    | 1015 |  | 	if (control_find_oword(block, settings, &op) != INTERP_OK) | 
    
    | 1016 |  | 	    OERR(_("%d: undefined O-word label: '%s'"), | 
    
    | 1017 |  | 		settings->sequence_number, settings->linetext); | 
    
    | 1018 |  | 	if ((op->type != O_while) && (op->type != O_do)) | 
    
    | 1019 |  | 	    OERR(_("%d: no matching while/do label: '%s' (found '%s' in line %d)"), | 
    
    | 1020 |  | 		settings->sequence_number, settings->linetext, | 
    
    | 1021 |  | 		o_ops[op->type] + 2, op->sequence_number + 1); | 
    
    | 1022 |  |  | 
    
    | 1023 |  | 	// if already skipping, do nothing | 
    
    | 1024 |  | 	if ((settings->skipping_o) && | 
    
    | 1025 |  | 	    (0 == strcmp(settings->skipping_o, block->o_name))) { | 
    
    | 1026 |  | 	    logOword("already skipping: [%s] in 'continue'", | 
    
    | 1027 |  | 		     block->o_name); | 
    
    | 1028 |  | 	    return INTERP_OK; | 
    
    | 1029 |  | 	} | 
    
    | 1030 |  | 	// start skipping | 
    
    | 1031 |  | 	settings->skipping_o = block->o_name; | 
    
    | 1032 |  | 	settings->skipping_start = settings->sequence_number; | 
    
    | 1033 |  | 	settings->doing_continue = 1; | 
    
    | 1034 |  | 	logOword("start skipping forward: [%s] in 'continue'", | 
    
    | 1035 |  | 		 block->o_name); | 
    
    | 1036 |  | 	break; | 
    
    | 1037 |  |  | 
    
    | 1038 |  |     case O_endrepeat: | 
    
    | 1039 |  |     case O_endwhile: | 
    
    | 1040 | 21 | 	if (control_find_oword(block, settings, &op) != INTERP_OK) | 
    
    | 1041 |  | 	    OERR(_("%d: undefined O-word label: '%s'"), | 
    
    | 1042 |  | 		settings->sequence_number, settings->linetext); | 
    
    | 1043 | 21 | 	if (((block->o_type == O_endrepeat) && (op->type != O_repeat)) || | 
    
    | 1044 | 21 | 	    ((block->o_type == O_endwhile) && (op->type != O_while))) | 
    
    | 1045 |  | 	    OERR(_("%d: no matching label: '%s' (found '%s' in line %d)"), | 
    
    | 1046 |  | 		settings->sequence_number, settings->linetext, | 
    
    | 1047 |  | 		o_ops[op->type] + 2, op->sequence_number + 1); | 
    
    | 1048 |  |  | 
    
    | 1049 |  | 	// end of a while loop | 
    
    | 1050 | 21 | 	logOword("endwhile: skipping_o:%s", settings->skipping_o); | 
    
    | 1051 | 22 | 	if ((settings->skipping_o) && | 
    
    | 1052 | 1 | 	    (0 == strcmp(settings->skipping_o, block->o_name))) { | 
    
    | 1053 |  | 	    // we were skipping, so this is the end | 
    
    | 1054 | 1 | 	    settings->skipping_o = NULL; | 
    
    | 1055 |  |  | 
    
    | 1056 | 1 | 	    if (settings->doing_continue) { | 
    
    | 1057 |  | 		settings->doing_continue = 0; | 
    
    | 1058 |  |  | 
    
    | 1059 |  | 		// loop on back | 
    
    | 1060 |  | 		logOword("looping back (continue) to: [%s] in while/repeat", | 
    
    | 1061 |  | 			 block->o_name); | 
    
    | 1062 |  | 		CHP(control_back_to(block, settings)); | 
    
    | 1063 |  | 	    } else { | 
    
    | 1064 |  | 		// not doing continue, we are done | 
    
    | 1065 | 1 | 		logOword("falling thru the complete while/repeat: [%s]", | 
    
    | 1066 |  | 			 block->o_name); | 
    
    | 1067 |  | 		return INTERP_OK; | 
    
    | 1068 |  | 	    } | 
    
    | 1069 |  | 	} else { | 
    
    | 1070 |  | 	    // loop on back | 
    
    | 1071 | 20 | 	    logOword("looping back to: [%s] in 'endwhile/endrepeat'", | 
    
    | 1072 |  | 		     block->o_name); | 
    
    | 1073 | 20 | 	    CHP(control_back_to(block, settings)); | 
    
    | 1074 |  | 	} | 
    
    | 1075 |  | 	break; | 
    
    | 1076 |  |  | 
    
    | 1077 |  |     default: | 
    
    | 1078 |  | 	// FIXME !!!KL should probably be an error | 
    
    | 1079 |  | 	return INTERP_ERROR; | 
    
    | 1080 |  | 	break; | 
    
    | 1081 |  |     } | 
    
    | 1082 |  |     // return status; | 
    
    | 1083 |  |     return INTERP_OK; | 
    
    | 1084 | 207 | } | 
    
    | 1085 |  | //======================================================================== | 
    
    | 1086 |  | // End of functions for control stuff (O-words) | 
    
    | 1087 |  | //======================================================================== |