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.
 
 
 
 
 
 

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