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.
 
 
 
 
 
 

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