ChipMaster's bwBASIC This also includes history going back to v2.10. *WARN* some binary files might have been corrupted by CRLF.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1541 lines
35 KiB

  1. /***************************************************************
  2. bwbasic.c Main Program File
  3. for Bywater BASIC Interpreter
  4. Copyright (c) 1993, Ted A. Campbell
  5. Bywater Software
  6. "I was no programmer, neither was I a
  7. programmer's son; but I was an herdman
  8. and a gatherer of sycomore fruit."
  9. - Amos 7:14b AV, slightly adapted
  10. email: tcamp@delphi.com
  11. Copyright and Permissions Information:
  12. All U.S. and international rights are claimed by the author,
  13. Ted A. Campbell.
  14. This software is released under the terms of the GNU General
  15. Public License (GPL), which is distributed with this software
  16. in the file "COPYING". The GPL specifies the terms under
  17. which users may copy and use the software in this distribution.
  18. A separate license is available for commercial distribution,
  19. for information on which you should contact the author.
  20. ***************************************************************/
  21. /*---------------------------------------------------------------*/
  22. /* NOTE: Modifications marked "JBV" were made by Jon B. Volkoff, */
  23. /* 11/1995 (eidetics@cerf.net). */
  24. /*---------------------------------------------------------------*/
  25. #include <stdio.h>
  26. #include <ctype.h>
  27. #include <math.h>
  28. #include "bwbasic.h"
  29. #include "bwb_mes.h"
  30. #if HAVE_SIGNAL
  31. #include <signal.h>
  32. #endif
  33. #if HAVE_LONGJUMP
  34. #include <setjmp.h>
  35. #endif
  36. char *bwb_ebuf; /* error buffer */
  37. static char *read_line;
  38. int bwb_trace = FALSE;
  39. FILE *errfdevice; /* output device for error messages */
  40. #if HAVE_LONGJUMP
  41. jmp_buf mark;
  42. #endif
  43. static int program_run = 0; /* has the command-line program been run? */
  44. int bwb_curtask = 0; /* current task */
  45. struct bwb_variable *ed; /* BWB.EDITOR$ variable */
  46. struct bwb_variable *fi; /* BWB.FILES$ variable */
  47. struct bwb_variable *pr; /* BWB.PROMPT$ variable */
  48. struct bwb_variable *im; /* BWB.IMPLEMENTATION$ variable */
  49. struct bwb_variable *co; /* BWB.COLORS variable */
  50. #if PARACT
  51. struct bwb_task *bwb_tasks[ TASKS ]; /* table of task pointers */
  52. #else
  53. char progfile[ MAXARGSIZE ]; /* program file */
  54. int rescan = TRUE; /* program needs to be rescanned */
  55. int number = 0; /* current line number */
  56. struct bwb_line *bwb_l; /* current line pointer */
  57. struct bwb_line bwb_start; /* starting line marker */
  58. struct bwb_line bwb_end; /* ending line marker */
  59. struct bwb_line *data_line; /* current line to read data */
  60. int data_pos = 0; /* position in data_line */
  61. struct bwb_variable var_start; /* variable list start marker */
  62. struct bwb_variable var_end; /* variable list end marker */
  63. struct bwb_function fnc_start; /* function list start marker */
  64. struct bwb_function fnc_end; /* function list end marker */
  65. struct fslte fslt_start; /* function-sub-label lookup table start marker */
  66. struct fslte fslt_end; /* function-sub-label lookup table end marker */
  67. int exsc = -1; /* EXEC stack counter */
  68. int expsc = 0; /* expression stack counter */
  69. int xtxtsc = 0; /* eXecute TeXT stack counter */
  70. struct exse *excs; /* EXEC stack */
  71. struct exp_ese *exps; /* Expression stack */
  72. struct xtxtsl *xtxts; /* Execute Text stack */
  73. #endif
  74. /* Prototypes for functions visible only to this file */
  75. #if ANSI_C
  76. extern int is_ln( char *buffer );
  77. #else
  78. extern int is_ln();
  79. #endif
  80. /***************************************************************
  81. FUNCTION: bwb_init()
  82. DESCRIPTION: This function initializes bwBASIC.
  83. ***************************************************************/
  84. void
  85. #if ANSI_C
  86. bwb_init( int argc, char **argv )
  87. #else
  88. bwb_init( argc, argv )
  89. int argc;
  90. char **argv;
  91. #endif
  92. {
  93. static FILE *input = NULL;
  94. register int n;
  95. #if PROFILE
  96. struct bwb_variable *v;
  97. #endif
  98. #if REDIRECT_STDERR
  99. FILE *newerr;
  100. #endif
  101. #if PROFILE
  102. FILE *profile;
  103. #endif
  104. #if PARACT
  105. #else
  106. static char start_buf[] = "\0";
  107. static char end_buf[] = "\0";
  108. #endif
  109. errfdevice = stderr;
  110. #if INTENSIVE_DEBUG
  111. prn_xprintf( stderr, "Memory Allocation Statistics:\n" );
  112. prn_xprintf( stderr, "----------------------------\n" );
  113. #if PARACT
  114. sprintf( bwb_ebuf, "task structure: %ld bytes\n",
  115. (long) sizeof( struct bwb_task ) );
  116. prn_xprintf( stderr, bwb_ebuf );
  117. getchar();
  118. #endif
  119. #endif
  120. /* set all task pointers to NULL */
  121. #if PARACT
  122. for ( n = 0; n < TASKS; ++n )
  123. {
  124. bwb_tasks[ n ] = NULL;
  125. }
  126. #else
  127. /* Memory allocation */
  128. /* eXecute TeXT stack */
  129. /* Revised to CALLOC pass-thru call by JBV */
  130. if ( ( xtxts = CALLOC( XTXTSTACKSIZE, sizeof( struct xtxtsl ), "bwb_init") ) == NULL )
  131. {
  132. #if PROG_ERRORS
  133. bwb_error( "in bwb_init(): failed to find memory for xtxts" );
  134. #else
  135. bwb_error( err_getmem );
  136. #endif
  137. }
  138. /* expression stack */
  139. /* Revised to CALLOC pass-thru call by JBV */
  140. if ( ( exps = CALLOC( ESTACKSIZE, sizeof( struct exp_ese ), "bwb_init") ) == NULL )
  141. {
  142. #if PROG_ERRORS
  143. bwb_error( "in bwb_init(): failed to find memory for exps" );
  144. #else
  145. bwb_error( err_getmem );
  146. #endif
  147. }
  148. /* EXEC stack */
  149. /* Revised to CALLOC pass-thru call by JBV */
  150. if ( ( excs = CALLOC( EXECLEVELS, sizeof( struct exse ), "bwb_init") ) == NULL )
  151. {
  152. #if PROG_ERRORS
  153. bwb_error( "in bwb_init(): failed to find memory for excs" );
  154. #else
  155. bwb_error( err_getmem );
  156. #endif
  157. }
  158. /* initialize tables of variables, functions */
  159. bwb_start.number = 0;
  160. bwb_start.next = &bwb_end;
  161. bwb_end.number = MAXLINENO + 1;
  162. bwb_end.next = &bwb_end;
  163. bwb_start.buffer = start_buf;
  164. bwb_end.buffer = end_buf;
  165. data_line = &bwb_start;
  166. data_pos = 0;
  167. exsc = -1;
  168. expsc = 0;
  169. xtxtsc = 0;
  170. bwb_start.position = 0;
  171. bwb_l = &bwb_start;
  172. var_init( 0 );
  173. fnc_init( 0 );
  174. fslt_init( 0 );
  175. #endif
  176. /* character buffers */
  177. /* Revised to CALLOC pass-thru call by JBV */
  178. if ( ( bwb_ebuf = CALLOC( MAXSTRINGSIZE + 1, sizeof(char), "bwb_init") ) == NULL )
  179. {
  180. #if PROG_ERRORS
  181. bwb_error( "in bwb_init(): failed to find memory for bwb_ebuf" );
  182. #else
  183. bwb_error( err_getmem );
  184. #endif
  185. }
  186. /* Revised to CALLOC pass-thru call by JBV */
  187. if ( ( read_line = CALLOC( MAXREADLINESIZE + 1, sizeof(char), "bwb_init") ) == NULL )
  188. {
  189. #if PROG_ERRORS
  190. bwb_error( "in bwb_init(): failed to find memory for read_line" );
  191. #else
  192. bwb_error( err_getmem );
  193. #endif
  194. }
  195. #if PARACT
  196. /* request task 0 as current (base) task */
  197. bwb_curtask = bwb_newtask( 0 );
  198. if ( bwb_curtask == -1 )
  199. {
  200. return; /* error message has already been called*/
  201. }
  202. #endif
  203. #if TEST_BSTRING
  204. for ( n = 0; n < ESTACKSIZE; ++n )
  205. {
  206. sprintf( CURTASK exps[ n ].sval.name, "<Exp stack bstring %d>", n );
  207. }
  208. #endif
  209. /* assign memory for the device table */
  210. #if COMMON_CMDS
  211. /* Revised to CALLOC pass-thru call by JBV */
  212. if ( ( dev_table = CALLOC( DEF_DEVICES, sizeof( struct dev_element ), "bwb_init") ) == NULL )
  213. {
  214. #if PROG_ERRORS
  215. bwb_error( "in bwb_init(): failed to find memory for dev_table" );
  216. #else
  217. bwb_error( err_getmem );
  218. #endif
  219. bwx_terminate();
  220. }
  221. /* initialize all devices to DEVMODE_AVAILABLE */
  222. for ( n = 0; n < DEF_DEVICES; ++n )
  223. {
  224. dev_table[ n ].mode = DEVMODE_AVAILABLE;
  225. dev_table[ n ].reclen = -1;
  226. dev_table[ n ].cfp = NULL;
  227. dev_table[ n ].buffer = NULL;
  228. dev_table[ n ].width = DEF_WIDTH;
  229. dev_table[ n ].col = 1;
  230. }
  231. #endif /* COMMON_CMDS */
  232. /* initialize preset variables */
  233. ed = var_find( DEFVNAME_EDITOR );
  234. ed->preset = TRUE;
  235. ed->common = TRUE;
  236. str_ctob( var_findsval( ed, ed->array_pos ), DEF_EDITOR );
  237. fi = var_find( DEFVNAME_FILES );
  238. fi->preset = TRUE;
  239. fi->common = TRUE;
  240. str_ctob( var_findsval( fi, fi->array_pos ), DEF_FILES );
  241. pr = var_find( DEFVNAME_PROMPT );
  242. pr->preset = TRUE;
  243. pr->common = TRUE;
  244. str_ctob( var_findsval( pr, pr->array_pos ), PROMPT );
  245. im = var_find( DEFVNAME_IMPL );
  246. im->preset = TRUE;
  247. im->common = TRUE;
  248. str_ctob( var_findsval( im, im->array_pos ), IMP_IDSTRING );
  249. co = var_find( DEFVNAME_COLORS );
  250. co->preset = TRUE;
  251. co->common = TRUE;
  252. * var_findnval( co, co->array_pos ) = (bnumber) DEF_COLORS;
  253. /* Signon message */
  254. bwx_signon();
  255. /* Redirect stderr if specified */
  256. #if REDIRECT_STDERR
  257. newerr = freopen( ERRFILE, "w", stderr );
  258. if ( newerr == NULL )
  259. {
  260. sprintf( bwb_ebuf, "Failed to redirect error messages to file <%s>\n",
  261. ERRFILE );
  262. errfdevice = stdout;
  263. prn_xprintf( errfdevice, bwb_ebuf );
  264. }
  265. else
  266. {
  267. sprintf( bwb_ebuf, "NOTE: Error messages are redirected to file <%s>\n",
  268. ERRFILE );
  269. prn_xprintf( errfdevice, bwb_ebuf );
  270. errfdevice = stderr;
  271. }
  272. #else
  273. errfdevice = stdout;
  274. #endif
  275. /* if there is a profile.bas, execute it */
  276. #if PROFILE
  277. if ( ( profile = fopen( PROFILENAME, "r" )) != NULL )
  278. {
  279. bwb_fload( profile ); /* load profile */
  280. bwb_run( &CURTASK bwb_start ); /* run profile */
  281. /* profile must be run immediately, not by scheduler */
  282. while ( CURTASK exsc > -1 )
  283. {
  284. bwb_execline();
  285. }
  286. /* mark all profiled variables as preset */
  287. for ( v = CURTASK var_start.next; v != &CURTASK var_end; v = v->next )
  288. {
  289. #if INTENSIVE_DEBUG
  290. sprintf( bwb_ebuf, "in bwb_init(): marked variable <%s> preset TRUE",
  291. v->name );
  292. bwb_debug( bwb_ebuf );
  293. #endif
  294. v->preset = TRUE;
  295. }
  296. bwb_new( &CURTASK bwb_start ); /* remove profile from memory */
  297. }
  298. #endif
  299. #if INTENSIVE_DEBUG
  300. sprintf( bwb_ebuf, "in main(): Ready to save jump MARKER" );
  301. bwb_debug( bwb_ebuf );
  302. #endif
  303. /* set a buffer for jump: program execution returns to this point
  304. in case of a jump (error, interrupt, or finish program) */
  305. #if INTERACTIVE
  306. #if HAVE_SIGNAL
  307. signal( SIGINT, break_mes );
  308. #endif
  309. #if HAVE_LONGJUMP
  310. setjmp( mark );
  311. #endif
  312. #if INTENSIVE_DEBUG
  313. sprintf( bwb_ebuf, "in bwb_init(): Return from jump MARKER, program run <%d>",
  314. program_run + 1 );
  315. bwb_debug( bwb_ebuf );
  316. getchar();
  317. #endif
  318. /* if INTERACTIVE is FALSE, then we must have a program file */
  319. #else
  320. if ( argc < 2 )
  321. {
  322. bwb_error( err_noprogfile );
  323. }
  324. #endif /* INTERACTIVE */
  325. /* check to see if there is a program file: but do this only the first
  326. time around! */
  327. ++program_run;
  328. if (( argc > 1 ) && ( program_run == 1 ))
  329. {
  330. strcpy( CURTASK progfile, argv[ 1 ] ); /* JBV */
  331. if ( ( input = fopen( CURTASK progfile, "r" )) == NULL ) /* JBV */
  332. {
  333. strcat( CURTASK progfile, ".bas" );
  334. if ( ( input = fopen( CURTASK progfile, "r" )) == NULL )
  335. {
  336. CURTASK progfile[ 0 ] = 0;
  337. sprintf( bwb_ebuf, err_openfile, argv[ 1 ] );
  338. bwb_error( bwb_ebuf );
  339. }
  340. }
  341. if ( input != NULL )
  342. {
  343. /* strcpy( CURTASK progfile, argv[ 1 ] ); */ /* Removed by JBV */
  344. #if INTENSIVE_DEBUG
  345. sprintf( bwb_ebuf, "in main(): progfile is <%s>.", CURTASK progfile );
  346. bwb_debug( bwb_ebuf );
  347. #endif
  348. bwb_fload( input );
  349. bwb_run( &CURTASK bwb_start );
  350. }
  351. }
  352. }
  353. /***************************************************************
  354. FUNCTION: bwb_interact()
  355. DESCRIPTION: This function gets a line from the user
  356. and processes it.
  357. ***************************************************************/
  358. #if INTERACTIVE
  359. int
  360. #if ANSI_C
  361. bwb_interact( void )
  362. #else
  363. bwb_interact()
  364. #endif
  365. {
  366. char tbuf[ MAXSTRINGSIZE + 1 ]; /* JBV */
  367. #if INTENSIVE_DEBUG
  368. sprintf( bwb_ebuf, "in bwb_interact(): ready to read from keyboard" );
  369. bwb_debug( bwb_ebuf );
  370. #endif
  371. /* take input from keyboard */
  372. bwb_gets( read_line );
  373. bwb_stripcr( read_line ); /* JBV */
  374. /* If there is no line number, execute the line as received */
  375. if ( is_ln( read_line ) == FALSE )
  376. {
  377. bwb_xtxtline( read_line );
  378. }
  379. /*-----------------------------------------------------------------*/
  380. /* Another possibility: if read_line is a numeric constant, delete */
  381. /* the indicated line number (JBV) */
  382. /*-----------------------------------------------------------------*/
  383. else if ( is_numconst( read_line ) == TRUE )
  384. {
  385. strcpy(tbuf, read_line);
  386. sprintf(read_line, "delete %s\0", tbuf);
  387. bwb_xtxtline( read_line );
  388. }
  389. /* If there is a line number, add the line to the file in memory */
  390. else
  391. {
  392. bwb_ladd( read_line, TRUE );
  393. #if INTENSIVE_DEBUG
  394. bwb_debug( "Return from bwb_ladd()" );
  395. #endif
  396. }
  397. return TRUE;
  398. }
  399. #endif /* INTERACTIVE == TRUE */
  400. /***************************************************************
  401. FUNCTION: bwb_fload()
  402. DESCRIPTION: This function loads a BASIC program
  403. file into memory given a FILE pointer.
  404. ***************************************************************/
  405. int
  406. #if ANSI_C
  407. bwb_fload( FILE *file )
  408. #else
  409. bwb_fload( file )
  410. FILE *file;
  411. #endif
  412. {
  413. while ( feof( file ) == FALSE )
  414. {
  415. read_line[ 0 ] = '\0';
  416. fgets( read_line, MAXREADLINESIZE, file );
  417. if ( file == stdin )
  418. {
  419. * prn_getcol( stdout ) = 1; /* reset column */
  420. }
  421. bwb_stripcr( read_line );
  422. /* be sure that this is not EOF with a NULL line */
  423. if (( feof( file ) == FALSE ) || ( strlen( read_line ) > 0 ))
  424. {
  425. bwb_ladd( read_line, FALSE );
  426. }
  427. }
  428. /* close file stream */
  429. fclose( file );
  430. return TRUE;
  431. }
  432. /***************************************************************
  433. FUNCTION: bwb_ladd()
  434. DESCRIPTION: This function adds a new line (in the
  435. buffer) to the program in memory.
  436. ***************************************************************/
  437. int
  438. #if ANSI_C
  439. bwb_ladd( char *buffer, int replace )
  440. #else
  441. bwb_ladd( buffer, replace )
  442. char *buffer;
  443. int replace;
  444. #endif
  445. {
  446. struct bwb_line *l, *previous, *p;
  447. static char *s_buffer;
  448. static int init = FALSE;
  449. static int prev_num = 0;
  450. char *newbuffer;
  451. #if INTENSIVE_DEBUG
  452. sprintf( bwb_ebuf, "in bwb_ladd(): add line <%s>",
  453. buffer );
  454. bwb_debug( bwb_ebuf );
  455. #endif
  456. /* get memory for temporary buffer if necessary */
  457. if ( init == FALSE )
  458. {
  459. init = TRUE;
  460. /* Revised to CALLOC pass-thru call by JBV */
  461. if ( ( s_buffer = CALLOC( (size_t) MAXSTRINGSIZE + 1, sizeof( char ), "bwb_ladd" )) == NULL )
  462. {
  463. #if PROG_ERRORS
  464. bwb_error( "in bwb_ladd(): failed to find memory for s_buffer" );
  465. #else
  466. bwb_error( err_getmem );
  467. #endif
  468. return FALSE;
  469. }
  470. }
  471. #if INTENSIVE_DEBUG
  472. sprintf( bwb_ebuf, "in bwb_ladd(): s_buffer initialized " );
  473. bwb_debug( bwb_ebuf );
  474. #endif
  475. /* get memory for this line */
  476. /* Revised to CALLOC pass-thru call by JBV */
  477. if ( ( l = (struct bwb_line *) CALLOC( (size_t) 1, sizeof( struct bwb_line ), "bwb_ladd")) == NULL )
  478. {
  479. #if PROG_ERRORS
  480. bwb_error( "in bwb_ladd(): failed to find memory for new line" );
  481. #else
  482. bwb_error( err_getmem );
  483. #endif
  484. return FALSE;
  485. }
  486. #if INTENSIVE_DEBUG
  487. sprintf( bwb_ebuf, "in bwb_ladd(): got memory." );
  488. bwb_debug( bwb_ebuf );
  489. #endif
  490. /* note that line is not yet marked and the program must be rescanned */
  491. l->marked = FALSE;
  492. CURTASK rescan = TRUE; /* program needs to be scanned again */
  493. l->xnum = FALSE;
  494. /* get the first element and test for a line number */
  495. adv_element( buffer, &( l->position ), s_buffer );
  496. /* set line number in line structure */
  497. if ( is_numconst( s_buffer ) == TRUE )
  498. {
  499. l->number = atoi( s_buffer );
  500. #if INTENSIVE_DEBUG
  501. sprintf( bwb_ebuf, "in bwb_ladd(): line is numbered, number is <%d>",
  502. l->number );
  503. bwb_debug( bwb_ebuf );
  504. #endif
  505. prev_num = l->number;
  506. l->xnum = TRUE;
  507. ++( l->position );
  508. newbuffer = &( buffer[ l->position ] );
  509. /* allocate memory and assign buffer to line buffer */
  510. ln_asbuf( l, newbuffer );
  511. }
  512. /* There is not a line number */
  513. else
  514. {
  515. #if INTENSIVE_DEBUG
  516. sprintf( bwb_ebuf, "in bwb_ladd(): line is not numbered, using prev <%d>",
  517. prev_num );
  518. bwb_debug( bwb_ebuf );
  519. #endif
  520. newbuffer = buffer;
  521. /* allocate memory and assign buffer to line buffer */
  522. ln_asbuf( l, newbuffer );
  523. l->xnum = FALSE;
  524. l->number = prev_num;
  525. }
  526. /* find the place of the current line */
  527. for ( previous = &CURTASK bwb_start; previous != &CURTASK bwb_end; previous = previous->next )
  528. {
  529. /* replace a previously existing line */
  530. if ( ( l->xnum == (char) TRUE ) /* Better recast this one (JBV) */
  531. && ( previous->number == l->number )
  532. && ( replace == TRUE )
  533. )
  534. {
  535. #if INTENSIVE_DEBUG
  536. sprintf( bwb_ebuf, "in bwb_ladd(): writing to previous number <%d>",
  537. l->number );
  538. bwb_debug( bwb_ebuf );
  539. #endif
  540. /* allocate memory and assign buffer to line buffer */
  541. ln_asbuf( previous, newbuffer );
  542. /* free the current line */
  543. /* Revised to FREE pass-thru calls by JBV */
  544. /* if (l->buffer != NULL) FREE( l->buffer, "bwb_ladd" ); */
  545. /* FREE( l, "bwb_ladd" ); */
  546. bwb_freeline( l ); /* JBV */
  547. /* and return */
  548. return TRUE;
  549. }
  550. /* add after previously existing line: this is to allow unnumbered
  551. lines that follow in sequence after a previously numbered line */
  552. else if (( previous->number == l->number )
  553. && ( replace == FALSE )
  554. )
  555. {
  556. #if INTENSIVE_DEBUG
  557. sprintf( bwb_ebuf, "in bwb_ladd(): adding doubled number <%d>",
  558. l->number );
  559. bwb_debug( bwb_ebuf);
  560. #endif
  561. /* if there are multiple instances of this particular line number,
  562. then it is incumbent upon us to find the very last one */
  563. for ( p = previous; p->number == l->number; p = p->next )
  564. {
  565. #if INTENSIVE_DEBUG
  566. bwb_debug( "in bwb_ladd(): advancing..." );
  567. #endif
  568. previous = p;
  569. }
  570. l->next = previous->next;
  571. previous->next = l;
  572. return TRUE;
  573. }
  574. /* add a new line */
  575. else if ( ( previous->number < l->number )
  576. && ( previous->next->number > l->number ))
  577. {
  578. l->next = previous->next;
  579. previous->next = l;
  580. #if INTENSIVE_DEBUG
  581. sprintf( bwb_ebuf, "in bwb_ladd(): added new line <%d> buffer <%s>",
  582. l->number, l->buffer );
  583. bwb_debug( bwb_ebuf );
  584. #endif
  585. return TRUE;
  586. }
  587. }
  588. /* attempt to link line number has failed; free memory */
  589. /* Revised to FREE pass-thru calls by JBV */
  590. /* if (l->buffer != NULL) FREE( l->buffer, "bwb_ladd" ); */
  591. /* FREE( l, "bwb_ladd" ); */
  592. bwb_freeline( l ); /* JBV */
  593. sprintf( bwb_ebuf, ERR_LINENO );
  594. bwb_error( bwb_ebuf );
  595. #if INTENSIVE_DEBUG
  596. sprintf( bwb_ebuf, "in bwb_ladd(): attempt to add line has failed" );
  597. bwb_debug( bwb_ebuf );
  598. #endif
  599. return FALSE;
  600. }
  601. /***************************************************************
  602. FUNCTION: bwb_xtxtline()
  603. DESCRIPTION: This function executes a text line, i.e.,
  604. places it in memory and then relinquishes
  605. control.
  606. ***************************************************************/
  607. struct bwb_line *
  608. #if ANSI_C
  609. bwb_xtxtline( char *buffer )
  610. #else
  611. bwb_xtxtline( buffer )
  612. char *buffer;
  613. #endif
  614. {
  615. struct bwb_line *c;
  616. char *p;
  617. int loop;
  618. #if INTENSIVE_DEBUG
  619. sprintf( bwb_ebuf, "in bwb_xtxtline(): received <%s>", buffer );
  620. bwb_debug( bwb_ebuf );
  621. #endif
  622. /* increment xtxt stack counter */
  623. if ( CURTASK xtxtsc >= XTXTSTACKSIZE )
  624. {
  625. sprintf( bwb_ebuf, "Exceeded maximum xtxt stack <%d>",
  626. CURTASK xtxtsc );
  627. return &CURTASK bwb_end;
  628. }
  629. ++CURTASK xtxtsc;
  630. /* advance past whitespace */
  631. p = buffer;
  632. loop = TRUE;
  633. while( loop == TRUE )
  634. {
  635. switch( *p )
  636. {
  637. case '\0': /* end of string */
  638. #if INTENSIVE_DEBUG
  639. sprintf( bwb_ebuf, "Null command line received." );
  640. bwb_debug( bwb_ebuf );
  641. #endif
  642. --CURTASK xtxtsc;
  643. return &CURTASK bwb_end;
  644. case ' ': /* whitespace */
  645. case '\t':
  646. ++p;
  647. break;
  648. default:
  649. loop = FALSE;
  650. break;
  651. }
  652. }
  653. #if INTENSIVE_DEBUG
  654. sprintf( bwb_ebuf, "in bwb_xtxtline(): ready to get memory" );
  655. bwb_debug( bwb_ebuf );
  656. #endif
  657. /* Removed by JBV (no longer needed, done by ln_asbuf) */
  658. /* if ( CURTASK xtxts[ CURTASK xtxtsc ].l.buffer != NULL )
  659. { */
  660. /* #if INTENSIVE_DEBUG */
  661. /* sprintf( bwb_ebuf, "in bwb_xtxtline(): freeing buffer memory" );
  662. bwb_debug( bwb_ebuf ); */
  663. /* #endif */
  664. /* Revised to FREE pass-thru call by JBV */
  665. /* FREE( CURTASK xtxts[ CURTASK xtxtsc ].l.buffer, "bwb_xtxtline" );
  666. } */
  667. /* copy the whole line to the line structure buffer */
  668. ln_asbuf( &( CURTASK xtxts[ CURTASK xtxtsc ].l ), buffer );
  669. #if INTENSIVE_DEBUG
  670. sprintf( bwb_ebuf, "in bwb_xtxtline(): copied to line buffer <%s>.",
  671. CURTASK xtxts[ CURTASK xtxtsc ].l.buffer );
  672. bwb_debug( bwb_ebuf );
  673. #endif
  674. /* set line number in line structure */
  675. CURTASK xtxts[ CURTASK xtxtsc ].l.number = 0;
  676. CURTASK xtxts[ CURTASK xtxtsc ].l.marked = FALSE;
  677. /* execute the line as BASIC command line */
  678. CURTASK xtxts[ CURTASK xtxtsc ].l.next = &CURTASK bwb_end;
  679. c = &( CURTASK xtxts[ CURTASK xtxtsc ].l );
  680. c->position = 0;
  681. #if THEOLDWAY
  682. do
  683. {
  684. c = bwb_xline( c );
  685. }
  686. while( c != &CURTASK bwb_end );
  687. #endif
  688. bwb_incexec(); /* increment EXEC stack */
  689. bwb_setexec( c, 0, EXEC_NORM ); /* and set current line in it */
  690. /* decrement xtxt stack counter ??? */
  691. --CURTASK xtxtsc;
  692. return c;
  693. }
  694. /***************************************************************
  695. FUNCTION: bwb_incexec()
  696. DESCRIPTION: This function increments the EXEC
  697. stack counter.
  698. ***************************************************************/
  699. #if ANSI_C
  700. extern void
  701. bwb_incexec( void )
  702. {
  703. #else
  704. void
  705. bwb_incexec()
  706. {
  707. #endif
  708. ++CURTASK exsc;
  709. if ( CURTASK exsc >= EXECLEVELS )
  710. {
  711. --CURTASK exsc;
  712. #if PROG_ERRORS
  713. sprintf( bwb_ebuf, "in bwb_incexec(): incremented EXEC stack past max <%d>",
  714. EXECLEVELS );
  715. bwb_error( bwb_ebuf );
  716. #else
  717. bwb_error( err_overflow );
  718. #endif
  719. }
  720. CURTASK excs[ CURTASK exsc ].while_line = NULL;
  721. CURTASK excs[ CURTASK exsc ].wend_line = NULL;
  722. CURTASK excs[ CURTASK exsc ].n_cvs = 0;
  723. CURTASK excs[ CURTASK exsc ].local_variable = NULL;
  724. }
  725. /***************************************************************
  726. FUNCTION: bwb_decexec()
  727. DESCRIPTION: This function decrements the EXEC
  728. stack counter.
  729. ***************************************************************/
  730. #if ANSI_C
  731. extern void
  732. bwb_decexec( void )
  733. {
  734. #else
  735. void
  736. bwb_decexec()
  737. {
  738. #endif
  739. /* decrement the exec stack counter */
  740. --CURTASK exsc;
  741. if ( CURTASK exsc < -1 )
  742. {
  743. CURTASK exsc = -1;
  744. #if PROG_ERRORS
  745. sprintf( bwb_ebuf, "in bwb_decexec(): decremented EXEC stack past min <-1>" );
  746. bwb_error( bwb_ebuf );
  747. #else
  748. bwb_error( err_overflow );
  749. #endif
  750. }
  751. /* check for EXEC_ON and decrement recursively */
  752. if ( CURTASK excs[ CURTASK exsc ].code == EXEC_ON )
  753. {
  754. /* Revised to FREE pass-thru calls by JBV */
  755. /* FREE( CURTASK excs[ CURTASK exsc ].while_line->buffer, "bwb_decexec" ); */
  756. /* FREE( CURTASK excs[ CURTASK exsc ].while_line, "bwb_decexec" ); */
  757. bwb_freeline( CURTASK excs[ CURTASK exsc ].while_line ); /* JBV */
  758. bwb_decexec();
  759. }
  760. }
  761. /***************************************************************
  762. FUNCTION: bwb_setexec()
  763. DESCRIPTION: This function sets the line and position
  764. for the next call to bwb_execline();
  765. ***************************************************************/
  766. #if ANSI_C
  767. extern int
  768. bwb_setexec( struct bwb_line *l, int position, int code )
  769. {
  770. #else
  771. int
  772. bwb_setexec( l, position, code )
  773. struct bwb_line *l;
  774. int position;
  775. int code;
  776. {
  777. #endif
  778. CURTASK excs[ CURTASK exsc ].line = l;
  779. CURTASK excs[ CURTASK exsc ].position = position;
  780. CURTASK excs[ CURTASK exsc ].code = code;
  781. return TRUE;
  782. }
  783. /***************************************************************
  784. FUNCTION: bwb_mainloop()
  785. DESCRIPTION: This C function performs one iteration
  786. of the interpreter. In a non-preemptive
  787. scheduler, this function should be called
  788. by the scheduler, not by bwBASIC code.
  789. ***************************************************************/
  790. void
  791. #if ANSI_C
  792. bwb_mainloop( void )
  793. #else
  794. bwb_mainloop()
  795. #endif
  796. {
  797. if ( CURTASK exsc > -1 )
  798. {
  799. bwb_execline(); /* execute one line of program */
  800. }
  801. #if INTERACTIVE
  802. else
  803. {
  804. bwb_interact(); /* get user interaction */
  805. }
  806. #endif
  807. }
  808. /***************************************************************
  809. FUNCTION: bwb_execline()
  810. DESCRIPTION: This function executes a single line of
  811. a program in memory. This function is
  812. called by bwb_mainloop().
  813. ***************************************************************/
  814. void
  815. #if ANSI_C
  816. bwb_execline( void )
  817. #else
  818. bwb_execline()
  819. #endif
  820. {
  821. struct bwb_line *r, *l;
  822. l = CURTASK excs[ CURTASK exsc ].line;
  823. /* if the line is &CURTASK bwb_end, then break out of EXEC loops */
  824. if ( l == &CURTASK bwb_end )
  825. {
  826. CURTASK exsc = -1;
  827. return;
  828. }
  829. /* Check for wacko line numbers */
  830. #if INTENSIVE_DEBUG
  831. if ( l->number < -1 )
  832. {
  833. #if PROG_ERRORS
  834. sprintf( bwb_ebuf, "in bwb_execline(): received line number <%d> < -1",
  835. l->number );
  836. bwb_error( bwb_ebuf );
  837. #else
  838. bwb_error( err_syntax );
  839. #endif
  840. return;
  841. }
  842. if ( l->number > MAXLINENO )
  843. {
  844. #if PROG_ERRORS
  845. sprintf( bwb_ebuf, "in bwb_execline(): received line number <%d> > MAX <%d>",
  846. l->number, MAXLINENO );
  847. bwb_error( bwb_ebuf );
  848. #else
  849. bwb_error( err_syntax );
  850. #endif
  851. return;
  852. }
  853. #endif
  854. #if INTENSIVE_DEBUG
  855. sprintf( bwb_ebuf, "in bwb_execline(): buffer <%s>",
  856. &( l->buffer[ l->position ] ) );
  857. bwb_debug( bwb_ebuf );
  858. #endif
  859. /* Print line number if trace is on */
  860. if ( bwb_trace == TRUE )
  861. {
  862. #if INTENSIVE_DEBUG
  863. sprintf( bwb_ebuf, "[ %d ]", l->number );
  864. prn_xprintf( errfdevice, bwb_ebuf );
  865. #else
  866. if ( l->number > 0 )
  867. {
  868. sprintf( bwb_ebuf, "[ %d ]", l->number );
  869. prn_xprintf( errfdevice, bwb_ebuf );
  870. }
  871. #endif
  872. }
  873. /* Set current line for error/break handling */
  874. CURTASK number = l->number;
  875. CURTASK bwb_l = l;
  876. /* advance beyond whitespace */
  877. adv_ws( l->buffer, &( l->position ) );
  878. /* advance past segment delimiter and warn */
  879. #if MULTISEG_LINES
  880. if ( l->buffer[ l->position ] == ':' )
  881. {
  882. ++( l->position );
  883. adv_ws( l->buffer, &( l->position ) );
  884. }
  885. l->marked = FALSE;
  886. #else
  887. #if PROG_ERRORS
  888. if ( l->buffer[ l->position ] == ':' )
  889. {
  890. ++( l->position );
  891. adv_ws( l->buffer, &( l->position ) );
  892. sprintf( bwb_ebuf, "Enable MULTISEG_LINES for multi-segmented lines",
  893. VERSION );
  894. bwb_error( bwb_ebuf );
  895. }
  896. #endif
  897. #endif
  898. /* set positions in buffer */
  899. #if MULTISEG_LINES
  900. if ( ( l->marked != TRUE ) || ( l->position > l->startpos ))
  901. {
  902. line_start( l->buffer, &( l->position ), &( l->lnpos ), &( l->lnum ),
  903. &( l->cmdpos ), &( l->cmdnum ), &( l->startpos ) );
  904. l->marked = TRUE;
  905. }
  906. else
  907. {
  908. #if INTENSIVE_DEBUG
  909. sprintf( bwb_ebuf, "in bwb_execline(): line <%d> is already marked",
  910. l->number );
  911. bwb_debug( bwb_ebuf );
  912. #endif
  913. }
  914. l->position = l->startpos;
  915. #else /* not MULTISEG_LINES */
  916. line_start( l->buffer, &( l->position ), &( l->lnpos ), &( l->lnum ),
  917. &( l->cmdpos ), &( l->cmdnum ), &( l->startpos ) );
  918. if ( l->position < l->startpos )
  919. {
  920. l->position = l->startpos;
  921. }
  922. #endif
  923. /* if there is a BASIC command in the line, execute it here */
  924. if ( l->cmdnum > -1 )
  925. {
  926. #if INTENSIVE_DEBUG
  927. sprintf( bwb_ebuf, "in bwb_execline(): executing <%s>", l->buffer );
  928. bwb_debug( bwb_ebuf );
  929. #endif
  930. /* execute the command vector */
  931. r = bwb_cmdtable[ l->cmdnum ].vector ( l );
  932. }
  933. /* No BASIC command; try to execute it as a shell command */
  934. #if COMMAND_SHELL
  935. else
  936. {
  937. #if INTENSIVE_DEBUG
  938. sprintf( bwb_ebuf, "Breaking out to shell, line num <%d> buf <%s> cmd <%d> pos <%d>",
  939. l->number, &( l->buffer[ l->position ] ), l->cmdnum, l->position );
  940. bwb_debug( bwb_ebuf );
  941. getchar();
  942. #endif
  943. bwx_shell( l );
  944. bwb_setexec( l->next, 0, CURTASK excs[ CURTASK exsc ].code );
  945. return;
  946. }
  947. #else /* COMMAND_SHELL == FALSE */
  948. else
  949. {
  950. bwb_error( err_uc );
  951. }
  952. #endif
  953. /* check for end of line: if so, advance to next line and return */
  954. adv_ws( r->buffer, &( r->position ) );
  955. switch( r->buffer[ r->position ] )
  956. {
  957. case '\n':
  958. case '\r':
  959. case '\0':
  960. #if INTENSIVE_DEBUG
  961. sprintf( bwb_ebuf, "in bwb_execline(): detected end of line" );
  962. bwb_debug( bwb_ebuf );
  963. #endif
  964. r->next->position = 0;
  965. bwb_setexec( r->next, 0, CURTASK excs[ CURTASK exsc ].code );
  966. return; /* and return */
  967. }
  968. /* else reset with the value in r */
  969. bwb_setexec( r, r->position, CURTASK excs[ CURTASK exsc ].code );
  970. #if INTENSIVE_DEBUG
  971. sprintf( bwb_ebuf, "in bwb_execline(): exit setting line number <%d>",
  972. r->number );
  973. bwb_debug( bwb_ebuf );
  974. #endif
  975. }
  976. /***************************************************************
  977. FUNCTION: ln_asbuf()
  978. DESCRIPTION: This function allocates memory and copies
  979. a null-terminated string to a line buffer.
  980. ***************************************************************/
  981. int
  982. #if ANSI_C
  983. ln_asbuf( struct bwb_line *l, char *s )
  984. #else
  985. ln_asbuf( l, s )
  986. struct bwb_line *l;
  987. char *s;
  988. #endif
  989. {
  990. /* Reinstated by JBV */
  991. /* #if DONTDOTHIS */ /* but why not? */
  992. if ( l->buffer != NULL )
  993. {
  994. /* Revised to FREE pass-thru call by JBV */
  995. FREE( l->buffer, "ln_asbuf" );
  996. l->buffer = NULL; /* JBV */
  997. }
  998. /* #endif */
  999. /* Revised to CALLOC pass-thru call by JBV */
  1000. if ( ( l->buffer = CALLOC( strlen( s ) + 2, sizeof( char ), "ln_asbuf") )
  1001. == NULL )
  1002. {
  1003. #if PROG_ERRORS
  1004. bwb_error( "in ln_asbuf(): failed to find memory for new line" );
  1005. #else
  1006. bwb_error( err_getmem );
  1007. #endif
  1008. return FALSE;
  1009. }
  1010. /* copy the whole line to the line structure buffer */
  1011. strcpy( l->buffer, s );
  1012. #if INTENSIVE_DEBUG
  1013. sprintf( bwb_ebuf, "in ln_asbuf(): allocated buffer <%s>", l->buffer );
  1014. bwb_debug( bwb_ebuf );
  1015. #endif
  1016. /* strip CR from the buffer */
  1017. bwb_stripcr( l->buffer );
  1018. #if INTENSIVE_DEBUG
  1019. sprintf( bwb_ebuf, "in ln_asbuf(): stripped CRs" );
  1020. bwb_debug( bwb_ebuf );
  1021. #endif
  1022. return TRUE;
  1023. }
  1024. /***************************************************************
  1025. FUNCTION: bwb_gets()
  1026. DESCRIPTION: This function reads a single line from
  1027. the specified buffer.
  1028. ***************************************************************/
  1029. int
  1030. #if ANSI_C
  1031. bwb_gets( char *buffer )
  1032. #else
  1033. bwb_gets( buffer )
  1034. char *buffer;
  1035. #endif
  1036. {
  1037. struct bwb_variable *v;
  1038. char tbuf[ MAXSTRINGSIZE + 1 ];
  1039. #if PARACT
  1040. char ubuf[ MAXSTRINGSIZE + 1 ];
  1041. #endif
  1042. CURTASK number = 0;
  1043. v = var_find( DEFVNAME_PROMPT );
  1044. str_btoc( tbuf, var_getsval( v ) );
  1045. #if PARACT
  1046. sprintf( ubuf, "TASK %d %s", bwb_curtask, tbuf );
  1047. strcpy( tbuf, ubuf );
  1048. #endif
  1049. bwx_input( tbuf, buffer );
  1050. return TRUE;
  1051. }
  1052. /***************************************************************
  1053. FUNCTION: break_mes()
  1054. DESCRIPTION: This function is called (a) by a SIGINT
  1055. signal or (b) by error-handling routines.
  1056. It prints an error message then calls
  1057. break_handler() to handle the program
  1058. interruption.
  1059. ***************************************************************/
  1060. void
  1061. #if ANSI_C
  1062. break_mes( int x )
  1063. #else
  1064. break_mes( x )
  1065. int x;
  1066. #endif
  1067. {
  1068. static char *tmp_buffer;
  1069. static int init = FALSE;
  1070. /* get memory for temporary buffer if necessary */
  1071. if ( init == FALSE )
  1072. {
  1073. init = TRUE;
  1074. /* Revised to CALLOC pass-thru call by JBV */
  1075. if ( ( tmp_buffer = CALLOC( MAXSTRINGSIZE + 1, sizeof( char ), "break_mes")) == NULL )
  1076. {
  1077. #if PROG_ERRORS
  1078. bwb_error( "in break_mes(): failed to find memory for tmp_buffer" );
  1079. #else
  1080. bwb_error( err_getmem );
  1081. #endif
  1082. }
  1083. }
  1084. CURTASK expsc = 0;
  1085. sprintf( tmp_buffer, "\r%s %d\n", MES_BREAK, CURTASK number );
  1086. prn_xprintf( errfdevice, tmp_buffer );
  1087. break_handler();
  1088. }
  1089. /***************************************************************
  1090. FUNCTION: break_handler()
  1091. DESCRIPTION: This function is called by break_mes()
  1092. and handles program interruption by break
  1093. (or by the STOP command).
  1094. ***************************************************************/
  1095. void
  1096. #if ANSI_C
  1097. break_handler( void )
  1098. #else
  1099. break_handler()
  1100. #endif
  1101. {
  1102. #if INTERACTIVE /* INTERACTIVE: reset counters and jump back to mark */
  1103. /* reset all stack counters */
  1104. CURTASK exsc = -1;
  1105. CURTASK expsc = 0;
  1106. CURTASK xtxtsc = 0;
  1107. err_gosubl[ 0 ] = '\0';
  1108. /* reset the break handler */
  1109. #if HAVE_SIGNAL
  1110. signal( SIGINT, break_mes );
  1111. #endif
  1112. #if HAVE_LONGJUMP
  1113. longjmp( mark, -1 );
  1114. #else /* HAVE_LONGJUMP = FALSE; no jump available; terminate */
  1115. bwx_terminate();
  1116. #endif
  1117. #else /* nonINTERACTIVE: terminate immediately */
  1118. bwx_terminate();
  1119. #endif
  1120. }
  1121. /***************************************************************
  1122. FUNCTION: is_ln()
  1123. DESCRIPTION: This function determines whether a program
  1124. line (in buffer) begins with a line number.
  1125. ***************************************************************/
  1126. int
  1127. #if ANSI_C
  1128. is_ln( char *buffer )
  1129. #else
  1130. is_ln( buffer )
  1131. char *buffer;
  1132. #endif
  1133. {
  1134. static int position;
  1135. position = 0;
  1136. adv_ws( buffer, &position );
  1137. switch( buffer[ position ] )
  1138. {
  1139. case '0':
  1140. case '1':
  1141. case '2':
  1142. case '3':
  1143. case '4':
  1144. case '5':
  1145. case '6':
  1146. case '7':
  1147. case '8':
  1148. case '9':
  1149. return TRUE;
  1150. default:
  1151. return FALSE;
  1152. }
  1153. }
  1154. /***************************************************************
  1155. FUNCTION: CALLOC()
  1156. DESCRIPTION: Pass-thru function to calloc() for debugging
  1157. purposes. Added by JBV 10/95
  1158. ***************************************************************/
  1159. void *
  1160. #if ANSI_C
  1161. CALLOC( size_t nelem, size_t elsize, char *str )
  1162. #else
  1163. CALLOC( nelem, elsize, str )
  1164. size_t nelem;
  1165. size_t elsize;
  1166. char *str;
  1167. #endif
  1168. {
  1169. void *ptr;
  1170. ptr = calloc(nelem, elsize);
  1171. /* printf("%x %x\n", ptr, mallocblksize(ptr)); */
  1172. return ptr;
  1173. }
  1174. /***************************************************************
  1175. FUNCTION: FREE()
  1176. DESCRIPTION: Pass-thru function to free() for debugging
  1177. purposes. Added by JBV 10/95
  1178. ***************************************************************/
  1179. void
  1180. #if ANSI_C
  1181. FREE( void *ptr, char *str )
  1182. #else
  1183. FREE( ptr, str )
  1184. void *ptr;
  1185. char *str;
  1186. #endif
  1187. {
  1188. /* printf("%x\n", ptr); */
  1189. free(ptr);
  1190. }