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.
 
 
 
 
 
 

1704 lines
38 KiB

  1. /***************************************************************
  2. bwb_prn.c Print and Error-Handling Commands
  3. for Bywater BASIC Interpreter
  4. Copyright (c) 1993, Ted A. Campbell
  5. Bywater Software
  6. email: tcamp@delphi.com
  7. Copyright and Permissions Information:
  8. All U.S. and international rights are claimed by the author,
  9. Ted A. Campbell.
  10. This software is released under the terms of the GNU General
  11. Public License (GPL), which is distributed with this software
  12. in the file "COPYING". The GPL specifies the terms under
  13. which users may copy and use the software in this distribution.
  14. A separate license is available for commercial distribution,
  15. for information on which you should contact the author.
  16. ***************************************************************/
  17. #include <stdio.h>
  18. #include <ctype.h>
  19. #include <math.h>
  20. #include "bwbasic.h"
  21. #include "bwb_mes.h"
  22. /* Prototypes for functions visible only to this file */
  23. int prn_col = 1;
  24. static int prn_width = 80; /* default width for stdout */
  25. struct prn_fmt
  26. {
  27. int type; /* STRING, NUMBER, SINGLE, or NUMBER */
  28. int exponential; /* TRUE = use exponential notation */
  29. int right_justified; /* TRUE = right justified else left justified */
  30. int width; /* width of main section */
  31. int precision; /* width after decimal point */
  32. int commas; /* use commas every three steps */
  33. int sign; /* prefix sign to number */
  34. int money; /* prefix money sign to number */
  35. int fill; /* ASCII value for fill character, normally ' ' */
  36. int minus; /* postfix minus sign to number */
  37. };
  38. #if ANSI_C
  39. static int prn_cr( char *buffer, FILE *f );
  40. static struct prn_fmt *get_prnfmt( char *buffer, int *position, FILE *f );
  41. static int bwb_xerror( char *message );
  42. static int xxputc( FILE *f, char c );
  43. static int xxxputc( FILE *f, char c );
  44. static struct bwb_variable * bwb_esetovar( struct exp_ese *e );
  45. #else
  46. static int prn_cr();
  47. static struct prn_fmt *get_prnfmt();
  48. static int bwb_xerror();
  49. static int xxputc();
  50. static int xxxputc();
  51. static struct bwb_variable * bwb_esetovar();
  52. #endif
  53. /***************************************************************
  54. FUNCTION: bwb_print()
  55. DESCRIPTION: This function implements the BASIC PRINT
  56. command.
  57. SYNTAX: PRINT [# device-number,][USING format-string$;] expressions...
  58. ***************************************************************/
  59. #if ANSI_C
  60. struct bwb_line *
  61. bwb_print( struct bwb_line *l )
  62. #else
  63. struct bwb_line *
  64. bwb_print( l )
  65. struct bwb_line *l;
  66. #endif
  67. {
  68. FILE *fp;
  69. static int pos;
  70. int req_devnumber;
  71. struct exp_ese *v;
  72. static char *s_buffer; /* small, temporary buffer */
  73. static int init = FALSE;
  74. #if INTENSIVE_DEBUG
  75. sprintf( bwb_ebuf, "in bwb_print(): enter function" );
  76. bwb_debug( bwb_ebuf );
  77. #endif
  78. /* initialize buffers if necessary */
  79. if ( init == FALSE )
  80. {
  81. init = TRUE;
  82. if ( ( s_buffer = calloc( MAXSTRINGSIZE + 1, sizeof(char) ) ) == NULL )
  83. {
  84. #if PROG_ERRORS
  85. bwb_error( "in bwb_print(): failed to get memory for s_buffer" );
  86. #else
  87. bwb_error( err_getmem );
  88. #endif
  89. }
  90. }
  91. /* advance beyond whitespace and check for the '#' sign */
  92. adv_ws( l->buffer, &( l->position ) );
  93. #if COMMON_CMDS
  94. if ( l->buffer[ l->position ] == '#' )
  95. {
  96. ++( l->position );
  97. adv_element( l->buffer, &( l->position ), s_buffer );
  98. pos = 0;
  99. v = bwb_exp( s_buffer, FALSE, &pos );
  100. adv_ws( l->buffer, &( l->position ) );
  101. if ( l->buffer[ l->position ] == ',' )
  102. {
  103. ++( l->position );
  104. }
  105. else
  106. {
  107. #if PROG_ERRORS
  108. bwb_error( "in bwb_print(): no comma after #n" );
  109. #else
  110. bwb_error( err_syntax );
  111. #endif
  112. return bwb_zline( l );
  113. }
  114. req_devnumber = (int) exp_getnval( v );
  115. /* check the requested device number */
  116. if ( ( req_devnumber < 0 ) || ( req_devnumber >= DEF_DEVICES ))
  117. {
  118. #if PROG_ERRORS
  119. bwb_error( "in bwb_input(): Requested device number is out of range." );
  120. #else
  121. bwb_error( err_devnum );
  122. #endif
  123. return bwb_zline( l );
  124. }
  125. if (( dev_table[ req_devnumber ].mode == DEVMODE_CLOSED ) ||
  126. ( dev_table[ req_devnumber ].mode == DEVMODE_AVAILABLE ))
  127. {
  128. #if PROG_ERRORS
  129. bwb_error( "in bwb_input(): Requested device number is not open." );
  130. #else
  131. bwb_error( err_devnum );
  132. #endif
  133. return bwb_zline( l );
  134. }
  135. if ( dev_table[ req_devnumber ].mode != DEVMODE_OUTPUT )
  136. {
  137. #if PROG_ERRORS
  138. bwb_error( "in bwb_print(): Requested device is not open for OUTPUT." );
  139. #else
  140. bwb_error( err_devnum );
  141. #endif
  142. return bwb_zline( l );
  143. }
  144. #if INTENSIVE_DEBUG
  145. sprintf( bwb_ebuf, "in bwb_print(): device number is <%d>",
  146. req_devnumber );
  147. bwb_debug( bwb_ebuf );
  148. #endif
  149. /* look up the requested device in the device table */
  150. fp = dev_table[ req_devnumber ].cfp;
  151. }
  152. else
  153. {
  154. fp = stdout;
  155. }
  156. #else
  157. fp = stdout;
  158. #endif /* COMMON_CMDS */
  159. bwb_xprint( l, fp );
  160. return bwb_zline( l );
  161. }
  162. /***************************************************************
  163. FUNCTION: bwb_xprint()
  164. DESCRIPTION: This function implements the BASIC PRINT
  165. command, utilizing a specified file our
  166. output device.
  167. ***************************************************************/
  168. #if ANSI_C
  169. int
  170. bwb_xprint( struct bwb_line *l, FILE *f )
  171. #else
  172. int
  173. bwb_xprint( l, f )
  174. struct bwb_line *l;
  175. FILE *f;
  176. #endif
  177. {
  178. struct exp_ese *e;
  179. int loop;
  180. static int p;
  181. static int fs_pos;
  182. struct prn_fmt *format;
  183. static char *format_string;
  184. static char *output_string;
  185. static char *element;
  186. static char *prnbuf;
  187. static int init = FALSE;
  188. #if INTENSIVE_DEBUG || TEST_BSTRING
  189. bstring *b;
  190. #endif
  191. /* initialize buffers if necessary */
  192. if ( init == FALSE )
  193. {
  194. init = TRUE;
  195. if ( ( format_string = calloc( MAXSTRINGSIZE + 1, sizeof(char) ) ) == NULL )
  196. {
  197. #if PROG_ERRORS
  198. bwb_error( "in bwb_xprint(): failed to get memory for format_string" );
  199. #else
  200. bwb_error( err_getmem );
  201. #endif
  202. }
  203. if ( ( output_string = calloc( MAXSTRINGSIZE + 1, sizeof(char) ) ) == NULL )
  204. {
  205. #if PROG_ERRORS
  206. bwb_error( "in bwb_xprint(): failed to get memory for output_string" );
  207. #else
  208. bwb_error( err_getmem );
  209. #endif
  210. }
  211. if ( ( element = calloc( MAXSTRINGSIZE + 1, sizeof(char) ) ) == NULL )
  212. {
  213. #if PROG_ERRORS
  214. bwb_error( "in bwb_xprint(): failed to get memory for element buffer" );
  215. #else
  216. bwb_error( err_getmem );
  217. #endif
  218. }
  219. if ( ( prnbuf = calloc( MAXSTRINGSIZE + 1, sizeof(char) ) ) == NULL )
  220. {
  221. #if PROG_ERRORS
  222. bwb_error( "in bwb_xprint(): failed to get memory for prnbuf" );
  223. #else
  224. bwb_error( err_getmem );
  225. #endif
  226. }
  227. }
  228. /* Detect USING Here */
  229. fs_pos = -1;
  230. /* get "USING" in format_string */
  231. p = l->position;
  232. adv_element( l->buffer, &p, format_string );
  233. bwb_strtoupper( format_string );
  234. #if COMMON_CMDS
  235. /* check to be sure */
  236. if ( strcmp( format_string, CMD_XUSING ) == 0 )
  237. {
  238. l->position = p;
  239. adv_ws( l->buffer, &( l->position ) );
  240. /* now get the format string in format_string */
  241. e = bwb_exp( l->buffer, FALSE, &( l->position ) );
  242. if ( e->type == STRING )
  243. {
  244. /* copy the format string to buffer */
  245. str_btoc( format_string, exp_getsval( e ) );
  246. /* look for ';' after format string */
  247. fs_pos = 0;
  248. adv_ws( l->buffer, &( l->position ) );
  249. if ( l->buffer[ l->position ] == ';' )
  250. {
  251. ++l->position;
  252. adv_ws( l->buffer, &( l->position ) );
  253. }
  254. else
  255. {
  256. #if PROG_ERRORS
  257. bwb_error( "Failed to find \";\" after format string in PRINT USING" );
  258. #else
  259. bwb_error( err_syntax );
  260. #endif
  261. return FALSE;
  262. }
  263. #if INTENSIVE_DEBUG
  264. sprintf( bwb_ebuf, "in bwb_xprint(): Found USING, format string <%s>",
  265. format_string );
  266. bwb_debug( bwb_ebuf );
  267. #endif
  268. }
  269. else
  270. {
  271. #if PROG_ERRORS
  272. bwb_error( "Failed to find format string after PRINT USING" );
  273. #else
  274. bwb_error( err_syntax );
  275. #endif
  276. return FALSE;
  277. }
  278. }
  279. #endif /* COMMON_CMDS */
  280. /* if no arguments, simply print CR and return */
  281. adv_ws( l->buffer, &( l->position ) );
  282. switch( l->buffer[ l->position ] )
  283. {
  284. case '\0':
  285. case '\n':
  286. case '\r':
  287. case ':':
  288. prn_xprintf( f, "\n" );
  289. return TRUE;
  290. default:
  291. break;
  292. }
  293. /* LOOP THROUGH PRINT ELEMENTS */
  294. loop = TRUE;
  295. while( loop == TRUE )
  296. {
  297. /* resolve the string */
  298. e = bwb_exp( l->buffer, FALSE, &( l->position ) );
  299. #if INTENSIVE_DEBUG
  300. sprintf( bwb_ebuf, "in bwb_xprint(): op <%d> type <%d>",
  301. e->operation, e->type );
  302. bwb_debug( bwb_ebuf );
  303. #endif
  304. /* an OP_NULL probably indicates a terminating ';', but this
  305. will be detected later, so we can ignore it for now */
  306. if ( e->operation != OP_NULL )
  307. {
  308. #if TEST_BSTRING
  309. b = exp_getsval( e );
  310. sprintf( bwb_ebuf, "in bwb_xprint(): bstring name is <%s>",
  311. b->name );
  312. bwb_debug( bwb_ebuf );
  313. #endif
  314. str_btoc( element, exp_getsval( e ) );
  315. }
  316. else
  317. {
  318. element[ 0 ] = '\0';
  319. }
  320. #if INTENSIVE_DEBUG
  321. sprintf( bwb_ebuf, "in bwb_xprint(): element <%s>",
  322. element );
  323. bwb_debug( bwb_ebuf );
  324. #endif
  325. /* print with format if there is one */
  326. if (( fs_pos > -1 ) && ( strlen( element ) > 0 ))
  327. {
  328. #if COMMON_CMDS
  329. format = get_prnfmt( format_string, &fs_pos, f );
  330. #if INTENSIVE_DEBUG
  331. sprintf( bwb_ebuf, "in bwb_xprint(): format type <%d> width <%d>",
  332. format->type, format->width );
  333. bwb_debug( bwb_ebuf );
  334. #endif
  335. switch( format->type )
  336. {
  337. case STRING:
  338. if ( e->type != STRING )
  339. {
  340. #if PROG_ERRORS
  341. bwb_error( "Type mismatch in PRINT USING" );
  342. #else
  343. bwb_error( err_mismatch );
  344. #endif
  345. }
  346. sprintf( output_string, "%.*s", format->width,
  347. element );
  348. #if INTENSIVE_DEBUG
  349. sprintf( bwb_ebuf, "in bwb_xprint(): output string <%s>",
  350. output_string );
  351. bwb_debug( bwb_ebuf );
  352. #endif
  353. prn_xprintf( f, output_string );
  354. break;
  355. case NUMBER:
  356. if ( e->type == STRING )
  357. {
  358. #if PROG_ERRORS
  359. bwb_error( "Type mismatch in PRINT USING" );
  360. #else
  361. bwb_error( err_mismatch );
  362. #endif
  363. }
  364. if ( format->exponential == TRUE )
  365. {
  366. sprintf( output_string, "%e",
  367. exp_getnval( e ) );
  368. }
  369. else
  370. {
  371. sprintf( output_string, "%*.*f",
  372. format->width, format->precision, exp_getnval( e ) );
  373. }
  374. #if INTENSIVE_DEBUG
  375. sprintf( bwb_ebuf, "in bwb_xprint(): output number <%f> string <%s>",
  376. exp_getnval( e ), output_string );
  377. bwb_debug( bwb_ebuf );
  378. #endif
  379. prn_xprintf( f, output_string );
  380. break;
  381. default:
  382. #if PROG_ERRORS
  383. sprintf( bwb_ebuf, "in bwb_xprint(): get_prnfmt() returns unknown type <%c>",
  384. format->type );
  385. bwb_error( bwb_ebuf );
  386. #else
  387. bwb_error( err_mismatch );
  388. #endif
  389. break;
  390. }
  391. #endif /* COMMON_CMDS */
  392. }
  393. /* not a format string: use defaults */
  394. else if ( strlen( element ) > 0 )
  395. {
  396. switch( e->type )
  397. {
  398. case STRING:
  399. prn_xprintf( f, element );
  400. break;
  401. default:
  402. #if NUMBER_DOUBLE
  403. sprintf( prnbuf, " %.*lf", prn_precision( bwb_esetovar( e )),
  404. exp_getnval( e ) );
  405. #else
  406. sprintf( prnbuf, " %.*f", prn_precision( bwb_esetovar( e )),
  407. exp_getnval( e ) );
  408. #endif
  409. prn_xprintf( f, prnbuf );
  410. break;
  411. }
  412. }
  413. /* check the position to see if the loop should continue */
  414. adv_ws( l->buffer, &( l->position ) );
  415. switch( l->buffer[ l->position ] )
  416. {
  417. #if OLDSTUFF
  418. case ':': /* end of line segment */
  419. loop = FALSE;
  420. break;
  421. case '\0': /* end of buffer */
  422. case '\n':
  423. case '\r':
  424. loop = FALSE;
  425. break;
  426. #endif
  427. case ',': /* tab over */
  428. xputc( f, '\t' );
  429. ++l->position;
  430. adv_ws( l->buffer, &( l->position ) );
  431. break;
  432. case ';': /* concatenate strings */
  433. ++l->position;
  434. adv_ws( l->buffer, &( l->position ) );
  435. break;
  436. default:
  437. loop = FALSE;
  438. break;
  439. }
  440. } /* end of loop through print elements */
  441. /* call prn_cr() to print a CR if it is not overridden by a
  442. concluding ';' mark */
  443. prn_cr( l->buffer, f );
  444. return TRUE;
  445. } /* end of function bwb_xprint() */
  446. #if COMMON_CMDS
  447. /***************************************************************
  448. FUNCTION: get_prnfmt()
  449. DESCRIPTION: This function gets the PRINT USING
  450. format string, returning a structure
  451. to the format.
  452. ***************************************************************/
  453. #if ANSI_C
  454. static struct prn_fmt *
  455. get_prnfmt( char *buffer, int *position, FILE *f )
  456. #else
  457. static struct prn_fmt *
  458. get_prnfmt( buffer, position, f )
  459. char *buffer;
  460. int *position;
  461. FILE *f;
  462. #endif
  463. {
  464. static struct prn_fmt retstruct;
  465. int loop;
  466. /* set some defaults */
  467. retstruct.precision = 0;
  468. retstruct.type = FALSE;
  469. retstruct.exponential = FALSE;
  470. retstruct.right_justified = FALSE;
  471. retstruct.commas = FALSE;
  472. retstruct.sign = FALSE;
  473. retstruct.money = FALSE;
  474. retstruct.fill = ' ';
  475. retstruct.minus = FALSE;
  476. retstruct.width = 0;
  477. /* check for negative position */
  478. if ( *position < 0 )
  479. {
  480. return &retstruct;
  481. }
  482. /* advance past whitespace */
  483. adv_ws( buffer, position );
  484. /* check first character: a lost can be decided right here */
  485. loop = TRUE;
  486. while( loop == TRUE )
  487. {
  488. #if INTENSIVE_DEBUG
  489. sprintf( bwb_ebuf, "in get_prnfmt(): loop, buffer <%s>",
  490. &( buffer[ *position ] ) );
  491. bwb_debug( bwb_ebuf );
  492. #endif
  493. switch( buffer[ *position ] )
  494. {
  495. case ' ': /* end of this format segment */
  496. loop = FALSE;
  497. break;
  498. case '\0': /* end of format string */
  499. case '\n':
  500. case '\r':
  501. *position = -1;
  502. return &retstruct;
  503. case '_': /* print next character as literal */
  504. ++( *position );
  505. xputc( f, buffer[ *position ] );
  506. ++( *position );
  507. break;
  508. case '!':
  509. retstruct.type = STRING;
  510. retstruct.width = 1;
  511. return &retstruct;
  512. case '\\':
  513. #if INTENSIVE_DEBUG
  514. sprintf( bwb_ebuf, "in get_prnfmt(): found \\" );
  515. bwb_debug( bwb_ebuf );
  516. #endif
  517. retstruct.type = STRING;
  518. ++retstruct.width;
  519. ++( *position );
  520. for ( ; buffer[ *position ] == ' '; ++( *position ) )
  521. {
  522. ++retstruct.width;
  523. }
  524. if ( buffer[ *position ] == '\\' )
  525. {
  526. ++retstruct.width;
  527. ++( *position );
  528. }
  529. return &retstruct;
  530. case '$':
  531. ++( *position );
  532. retstruct.money = TRUE;
  533. if ( buffer[ *position ] == '$' )
  534. {
  535. ++( *position );
  536. }
  537. break;
  538. case '*':
  539. ++( *position );
  540. retstruct.fill = '*';
  541. if ( buffer[ *position ] == '*' )
  542. {
  543. ++( *position );
  544. }
  545. break;
  546. case '+':
  547. ++( *position );
  548. retstruct.sign = TRUE;
  549. break;
  550. case '#':
  551. retstruct.type = NUMBER; /* for now */
  552. ++( *position );
  553. for ( retstruct.width = 1; buffer[ *position ] == '#'; ++( *position ) )
  554. {
  555. ++retstruct.width;
  556. }
  557. if ( buffer[ *position ] == ',' )
  558. {
  559. retstruct.commas = TRUE;
  560. }
  561. if ( buffer[ *position ] == '.' )
  562. {
  563. retstruct.type = NUMBER;
  564. ++retstruct.width;
  565. ++( *position );
  566. for ( retstruct.precision = 0; buffer[ *position ] == '#'; ++( *position ) )
  567. {
  568. ++retstruct.precision;
  569. ++retstruct.width;
  570. }
  571. }
  572. if ( buffer[ *position ] == '-' )
  573. {
  574. retstruct.minus = TRUE;
  575. ++( *position );
  576. }
  577. return &retstruct;
  578. case '^':
  579. retstruct.type = NUMBER;
  580. retstruct.exponential = TRUE;
  581. for ( retstruct.width = 1; buffer[ *position ] == '^'; ++( *position ) )
  582. {
  583. ++retstruct.width;
  584. }
  585. return &retstruct;
  586. }
  587. } /* end of loop */
  588. return &retstruct;
  589. }
  590. #endif
  591. /***************************************************************
  592. FUNCTION: prn_cr()
  593. DESCRIPTION: This function outputs a carriage-return
  594. to a specified file or output device.
  595. ***************************************************************/
  596. #if ANSI_C
  597. static int
  598. prn_cr( char *buffer, FILE *f )
  599. #else
  600. static int
  601. prn_cr( buffer, f )
  602. char *buffer;
  603. FILE *f;
  604. #endif
  605. {
  606. register int c;
  607. int loop;
  608. /* find the end of the buffer */
  609. for ( c = 0; buffer[ c ] != '\0'; ++c )
  610. {
  611. }
  612. #if INTENSIVE_DEBUG
  613. sprintf( bwb_ebuf, "in prn_cr(): initial c is <%d>", c );
  614. bwb_debug( bwb_ebuf );
  615. #endif
  616. /* back up through any whitespace */
  617. loop = TRUE;
  618. while ( loop == TRUE )
  619. {
  620. switch( buffer[ c ] )
  621. {
  622. case ' ': /* if whitespace */
  623. case '\t':
  624. case 0:
  625. #if INTENSIVE_DEBUG
  626. sprintf( bwb_ebuf, "in prn_cr(): backup: c is <%d>, char <%c>[0x%x]",
  627. c, buffer[ c ], buffer[ c ] );
  628. bwb_debug( bwb_ebuf );
  629. #endif
  630. --c; /* back up */
  631. if ( c < 0 ) /* check position */
  632. {
  633. loop = FALSE;
  634. }
  635. break;
  636. default: /* else break out */
  637. #if INTENSIVE_DEBUG
  638. sprintf( bwb_ebuf, "in prn_cr(): breakout: c is <%d>, char <%c>[0x%x]",
  639. c, buffer[ c ], buffer[ c ] );
  640. bwb_debug( bwb_ebuf );
  641. #endif
  642. loop = FALSE;
  643. break;
  644. }
  645. }
  646. if ( buffer[ c ] == ';' )
  647. {
  648. #if INTENSIVE_DEBUG
  649. sprintf( bwb_ebuf, "in prn_cr(): concluding <;> detected." );
  650. bwb_debug( bwb_ebuf );
  651. #endif
  652. return FALSE;
  653. }
  654. else
  655. {
  656. prn_xprintf( f, "\n" );
  657. return TRUE;
  658. }
  659. }
  660. /***************************************************************
  661. FUNCTION: prn_xprintf()
  662. DESCRIPTION: This function outputs a null-terminated
  663. string to a specified file or output
  664. device.
  665. ***************************************************************/
  666. #if ANSI_C
  667. int
  668. prn_xprintf( FILE *f, char *buffer )
  669. #else
  670. int
  671. prn_xprintf( f, buffer )
  672. FILE *f;
  673. char *buffer;
  674. #endif
  675. {
  676. char *p;
  677. /* DO NOT try anything so stupid as to run bwb_debug() from
  678. here, because it will create an endless loop. And don't
  679. ask how I know. */
  680. for ( p = buffer; *p != '\0'; ++p )
  681. {
  682. xputc( f, *p );
  683. }
  684. return TRUE;
  685. }
  686. /***************************************************************
  687. FUNCTION: xputc()
  688. DESCRIPTION: This function outputs a character to a
  689. specified file or output device, expanding
  690. TABbed output approriately.
  691. ***************************************************************/
  692. #if ANSI_C
  693. int
  694. xputc( FILE *f, char c )
  695. #else
  696. int
  697. xputc( f, c )
  698. FILE *f;
  699. char c;
  700. #endif
  701. {
  702. static int tab_pending = FALSE;
  703. /* check for pending TAB */
  704. if ( tab_pending == TRUE )
  705. {
  706. if ( (int) c < ( * prn_getcol( f ) ) )
  707. {
  708. xxputc( f, '\n' );
  709. }
  710. while( ( * prn_getcol( f )) < (int) c )
  711. {
  712. xxputc( f, ' ' );
  713. }
  714. tab_pending = FALSE;
  715. return TRUE;
  716. }
  717. /* check c for specific output options */
  718. switch( c )
  719. {
  720. case PRN_TAB:
  721. tab_pending = TRUE;
  722. break;
  723. case '\t':
  724. while( ( (* prn_getcol( f )) % 14 ) != 0 )
  725. {
  726. xxputc( f, ' ' );
  727. }
  728. break;
  729. default:
  730. xxputc( f, c );
  731. break;
  732. }
  733. return TRUE;
  734. }
  735. /***************************************************************
  736. FUNCTION: xxputc()
  737. DESCRIPTION: This function outputs a character to a
  738. specified file or output device, checking
  739. to be sure the PRINT width is within
  740. the bounds specified for that device.
  741. ***************************************************************/
  742. #if ANSI_C
  743. static int
  744. xxputc( FILE *f, char c )
  745. #else
  746. static int
  747. xxputc( f, c )
  748. FILE *f;
  749. char c;
  750. #endif
  751. {
  752. /* check to see if width has been exceeded */
  753. if ( * prn_getcol( f ) >= prn_getwidth( f ))
  754. {
  755. xxxputc( f, '\n' ); /* output LF */
  756. * prn_getcol( f ) = 1; /* and reset */
  757. }
  758. /* adjust the column counter */
  759. if ( c == '\n' )
  760. {
  761. * prn_getcol( f ) = 1;
  762. }
  763. else
  764. {
  765. ++( * prn_getcol( f ));
  766. }
  767. /* now output the character */
  768. return xxxputc( f, c );
  769. }
  770. /***************************************************************
  771. FUNCTION: xxxputc()
  772. DESCRIPTION: This function sends a character to a
  773. specified file or output device.
  774. ***************************************************************/
  775. #if ANSI_C
  776. static int
  777. xxxputc( FILE *f, char c )
  778. #else
  779. static int
  780. xxxputc( f, c )
  781. FILE *f;
  782. char c;
  783. #endif
  784. {
  785. if (( f == stdout ) || ( f == stderr ))
  786. {
  787. return bwx_putc( c );
  788. }
  789. else
  790. {
  791. return fputc( c, f );
  792. }
  793. }
  794. /***************************************************************
  795. FUNCTION: prn_getcol()
  796. DESCRIPTION: This function returns a pointer to an
  797. integer containing the current PRINT
  798. column for a specified file or device.
  799. ***************************************************************/
  800. #if ANSI_C
  801. int *
  802. prn_getcol( FILE *f )
  803. #else
  804. int *
  805. prn_getcol( f )
  806. FILE *f;
  807. #endif
  808. {
  809. register int n;
  810. static int dummy_pos;
  811. if (( f == stdout ) || ( f == stderr ))
  812. {
  813. return &prn_col;
  814. }
  815. #if COMMON_CMDS
  816. for ( n = 0; n < DEF_DEVICES; ++n )
  817. {
  818. if ( dev_table[ n ].cfp == f )
  819. {
  820. return &( dev_table[ n ].col );
  821. }
  822. }
  823. #endif
  824. /* search failed */
  825. #if PROG_ERRORS
  826. bwb_error( "in prn_getcol(): failed to find file pointer" );
  827. #else
  828. bwb_error( err_devnum );
  829. #endif
  830. return &dummy_pos;
  831. }
  832. /***************************************************************
  833. FUNCTION: prn_getwidth()
  834. DESCRIPTION: This function returns the PRINT width for
  835. a specified file or output device.
  836. ***************************************************************/
  837. #if ANSI_C
  838. int
  839. prn_getwidth( FILE *f )
  840. #else
  841. int
  842. prn_getwidth( f )
  843. FILE *f;
  844. #endif
  845. {
  846. register int n;
  847. if (( f == stdout ) || ( f == stderr ))
  848. {
  849. return prn_width;
  850. }
  851. #if COMMON_CMDS
  852. for ( n = 0; n < DEF_DEVICES; ++n )
  853. {
  854. if ( dev_table[ n ].cfp == f )
  855. {
  856. return dev_table[ n ].width;
  857. }
  858. }
  859. #endif
  860. /* search failed */
  861. #if PROG_ERRORS
  862. bwb_error( "in prn_getwidth(): failed to find file pointer" );
  863. #else
  864. bwb_error( err_devnum );
  865. #endif
  866. return 1;
  867. }
  868. /***************************************************************
  869. FUNCTION: prn_precision()
  870. DESCRIPTION: This function returns the level of precision
  871. required for a specified numerical value.
  872. ***************************************************************/
  873. #if ANSI_C
  874. int
  875. prn_precision( struct bwb_variable *v )
  876. #else
  877. int
  878. prn_precision( v )
  879. struct bwb_variable *v;
  880. #endif
  881. {
  882. int max_precision = 6;
  883. bnumber nval, d;
  884. int r;
  885. /* check for double value */
  886. if ( v->type == NUMBER )
  887. {
  888. max_precision = 12;
  889. }
  890. /* get the value in nval */
  891. nval = (bnumber) fabs( (double) var_getnval( v ) );
  892. /* cycle through until precision is found */
  893. d = (bnumber) 1;
  894. for ( r = 0; r < max_precision; ++r )
  895. {
  896. #if INTENSIVE_DEBUG
  897. sprintf( bwb_ebuf, "in prn_precision(): fmod( %f, %f ) = %.12f",
  898. nval, d, fmod( nval, d ) );
  899. bwb_debug( bwb_ebuf );
  900. #endif
  901. if ( fmod( nval, d ) < 0.0000001 )
  902. {
  903. return r;
  904. }
  905. d /= 10;
  906. }
  907. /* return */
  908. return r;
  909. }
  910. /***************************************************************
  911. FUNCTION: bwb_debug()
  912. DESCRIPTION: This function is called to display
  913. debugging messages in Bywater BASIC.
  914. It does not break out at the current
  915. point (as bwb_error() does).
  916. ***************************************************************/
  917. #if PERMANENT_DEBUG
  918. #if ANSI_C
  919. int
  920. bwb_debug( char *message )
  921. #else
  922. int
  923. bwb_debug( message )
  924. char *message;
  925. #endif
  926. {
  927. char tbuf[ MAXSTRINGSIZE + 1 ];
  928. fflush( stdout );
  929. fflush( errfdevice );
  930. if ( prn_col != 1 )
  931. {
  932. prn_xprintf( errfdevice, "\n" );
  933. }
  934. sprintf( tbuf, "DEBUG %s\n", message );
  935. prn_xprintf( errfdevice, tbuf );
  936. return TRUE;
  937. }
  938. #endif
  939. #if COMMON_CMDS
  940. /***************************************************************
  941. FUNCTION: bwb_lerror()
  942. DESCRIPTION: This function implements the BASIC ERROR
  943. command.
  944. ***************************************************************/
  945. #if ANSI_C
  946. struct bwb_line *
  947. bwb_lerror( struct bwb_line *l )
  948. #else
  949. struct bwb_line *
  950. bwb_lerror( l )
  951. struct bwb_line *l;
  952. #endif
  953. {
  954. char tbuf[ MAXSTRINGSIZE + 1 ];
  955. int n;
  956. #if INTENSIVE_DEBUG
  957. sprintf( bwb_ebuf, "in bwb_lerror(): entered function " );
  958. bwb_debug( bwb_ebuf );
  959. #endif
  960. /* Check for argument */
  961. adv_ws( l->buffer, &( l->position ) );
  962. switch( l->buffer[ l->position ] )
  963. {
  964. case '\0':
  965. case '\n':
  966. case '\r':
  967. case ':':
  968. bwb_error( err_incomplete );
  969. return bwb_zline( l );
  970. default:
  971. break;
  972. }
  973. /* get the variable name or numerical constant */
  974. adv_element( l->buffer, &( l->position ), tbuf );
  975. n = atoi( tbuf );
  976. #if INTENSIVE_DEBUG
  977. sprintf( bwb_ebuf, "in bwb_lerror(): error number is <%d> ", n );
  978. bwb_debug( bwb_ebuf );
  979. #endif
  980. /* check the line number value */
  981. if ( ( n < 0 ) || ( n >= N_ERRORS ))
  982. {
  983. sprintf( bwb_ebuf, "Error number %d is out of range", n );
  984. bwb_xerror( bwb_ebuf );
  985. return bwb_zline( l );
  986. }
  987. bwb_xerror( err_table[ n ] );
  988. return bwb_zline( l );
  989. }
  990. /***************************************************************
  991. FUNCTION: bwb_width()
  992. DESCRIPTION: This C function implements the BASIC WIDTH
  993. command, setting the maximum output width
  994. for a specified file or output device.
  995. SYNTAX: WIDTH [# device-number,] number
  996. ***************************************************************/
  997. #if ANSI_C
  998. struct bwb_line *
  999. bwb_width( struct bwb_line *l )
  1000. #else
  1001. struct bwb_line *
  1002. bwb_width( l )
  1003. struct bwb_line *l;
  1004. #endif
  1005. {
  1006. int req_devnumber;
  1007. int req_width;
  1008. struct exp_ese *e;
  1009. char tbuf[ MAXSTRINGSIZE + 1 ];
  1010. int pos;
  1011. /* detect device number if present */
  1012. req_devnumber = -1;
  1013. adv_ws( l->buffer, &( l->position ) );
  1014. if ( l->buffer[ l->position ] == '#' )
  1015. {
  1016. ++( l->position );
  1017. adv_element( l->buffer, &( l->position ), tbuf );
  1018. pos = 0;
  1019. e = bwb_exp( tbuf, FALSE, &pos );
  1020. adv_ws( l->buffer, &( l->position ) );
  1021. if ( l->buffer[ l->position ] == ',' )
  1022. {
  1023. ++( l->position );
  1024. }
  1025. else
  1026. {
  1027. #if PROG_ERRORS
  1028. bwb_error( "in bwb_width(): no comma after#n" );
  1029. #else
  1030. bwb_error( err_syntax );
  1031. #endif
  1032. return bwb_zline( l );
  1033. }
  1034. req_devnumber = (int) exp_getnval( e );
  1035. /* check the requested device number */
  1036. if ( ( req_devnumber < 0 ) || ( req_devnumber >= DEF_DEVICES ))
  1037. {
  1038. #if PROG_ERRORS
  1039. bwb_error( "in bwb_width(): Requested device number is out of range." );
  1040. #else
  1041. bwb_error( err_devnum );
  1042. #endif
  1043. return bwb_zline( l );
  1044. }
  1045. #if INTENSIVE_DEBUG
  1046. sprintf( bwb_ebuf, "in bwb_width(): device number is <%d>",
  1047. req_devnumber );
  1048. bwb_debug( bwb_ebuf );
  1049. #endif
  1050. }
  1051. /* read the width requested */
  1052. e = bwb_exp( l->buffer, FALSE, &( l->position ));
  1053. req_width = (int) exp_getnval( e );
  1054. /* check the width */
  1055. if ( ( req_width < 1 ) || ( req_width > 255 ))
  1056. {
  1057. #if PROG_ERRORS
  1058. bwb_error( "in bwb_width(): Requested width is out of range (1-255)" );
  1059. #else
  1060. bwb_error( err_valoorange );
  1061. #endif
  1062. }
  1063. /* assign the width */
  1064. if ( req_devnumber == -1 )
  1065. {
  1066. prn_width = req_width;
  1067. }
  1068. else
  1069. {
  1070. dev_table[ req_devnumber ].width = req_width;
  1071. }
  1072. /* return */
  1073. return bwb_zline( l );
  1074. }
  1075. #endif /* COMMON_CMDS */
  1076. /***************************************************************
  1077. FUNCTION: bwb_error()
  1078. DESCRIPTION: This function is called to handle errors
  1079. in Bywater BASIC. It displays the error
  1080. message, then calls the break_handler()
  1081. routine.
  1082. ***************************************************************/
  1083. #if ANSI_C
  1084. int
  1085. bwb_error( char *message )
  1086. #else
  1087. int
  1088. bwb_error( message )
  1089. char *message;
  1090. #endif
  1091. {
  1092. register int e;
  1093. static char tbuf[ MAXSTRINGSIZE + 1 ]; /* must be permanent */
  1094. static struct bwb_line eline;
  1095. int save_elevel;
  1096. struct bwb_line *cur_l;
  1097. int cur_mode;
  1098. /* try to find the error message to identify the error number */
  1099. err_number = -1; /* just for now */
  1100. err_line = CURTASK number; /* set error line number */
  1101. for ( e = 0; e < N_ERRORS; ++e )
  1102. {
  1103. if ( message == err_table[ e ] ) /* set error number */
  1104. {
  1105. err_number = e;
  1106. e = N_ERRORS; /* break out of loop quickly */
  1107. }
  1108. }
  1109. /* set the position in the current line to the end */
  1110. while( is_eol( bwb_l->buffer, &( bwb_l->position ) ) != TRUE )
  1111. {
  1112. ++( bwb_l->position );
  1113. }
  1114. /* if err_gosubl is not set, then use xerror routine */
  1115. if ( strlen( err_gosubl ) == 0 )
  1116. {
  1117. return bwb_xerror( message );
  1118. }
  1119. #if INTENSIVE_DEBUG
  1120. fprintf( stderr, "!!!!! USER_CALLED ERROR HANDLER\n" );
  1121. #endif
  1122. /* save line and mode */
  1123. cur_l = bwb_l;
  1124. cur_mode = CURTASK excs[ CURTASK exsc ].code;
  1125. /* err_gosubl is set; call user-defined error subroutine */
  1126. sprintf( tbuf, "%s %s", CMD_GOSUB, err_gosubl );
  1127. eline.next = &CURTASK bwb_end;
  1128. eline.position = 0;
  1129. eline.marked = FALSE;
  1130. eline.buffer = tbuf;
  1131. bwb_setexec( &eline, 0, EXEC_NORM );
  1132. /* must be executed now */
  1133. save_elevel = CURTASK exsc;
  1134. bwb_execline(); /* This is a call to GOSUB and will increment
  1135. the exsc counter above save_elevel */
  1136. while ( CURTASK exsc != save_elevel ) /* loop until return from GOSUB loop */
  1137. {
  1138. bwb_execline();
  1139. }
  1140. cur_l->next->position = 0;
  1141. bwb_setexec( cur_l->next, 0, cur_mode );
  1142. return TRUE;
  1143. }
  1144. /***************************************************************
  1145. FUNCTION: bwb_xerror()
  1146. DESCRIPTION: This function is called by bwb_error()
  1147. in Bywater BASIC. It displays the error
  1148. message, then calls the break_handler()
  1149. routine.
  1150. ***************************************************************/
  1151. #if ANSI_C
  1152. static int
  1153. bwb_xerror( char *message )
  1154. #else
  1155. static int
  1156. bwb_xerror( message )
  1157. char *message;
  1158. #endif
  1159. {
  1160. bwx_errmes( message );
  1161. break_handler();
  1162. return FALSE;
  1163. }
  1164. /***************************************************************
  1165. FUNCTION: bwb_esetovar()
  1166. DESCRIPTION: This function converts the value in expression
  1167. stack 'e' to a bwBASIC variable structure.
  1168. ***************************************************************/
  1169. #if ANSI_C
  1170. static struct bwb_variable *
  1171. bwb_esetovar( struct exp_ese *e )
  1172. #else
  1173. static struct bwb_variable *
  1174. bwb_esetovar( e )
  1175. struct exp_ese *e;
  1176. #endif
  1177. {
  1178. static struct bwb_variable b;
  1179. var_make( &b, e->type );
  1180. switch( e->type )
  1181. {
  1182. case STRING:
  1183. str_btob( var_findsval( &b, b.array_pos ), exp_getsval( e ) );
  1184. break;
  1185. default:
  1186. * var_findnval( &b, b.array_pos ) = e->nval;
  1187. break;
  1188. }
  1189. return &b;
  1190. }
  1191. #if COMMON_CMDS
  1192. /***************************************************************
  1193. FUNCTION: bwb_write()
  1194. DESCRIPTION: This C function implements the BASIC WRITE
  1195. command.
  1196. SYNTAX: WRITE [# device-number,] element [, element ]....
  1197. ***************************************************************/
  1198. #if ANSI_C
  1199. struct bwb_line *
  1200. bwb_write( struct bwb_line *l )
  1201. #else
  1202. struct bwb_line *
  1203. bwb_write( l )
  1204. struct bwb_line *l;
  1205. #endif
  1206. {
  1207. struct exp_ese *e;
  1208. int req_devnumber;
  1209. int pos;
  1210. FILE *fp;
  1211. char tbuf[ MAXSTRINGSIZE + 1 ];
  1212. int loop;
  1213. static struct bwb_variable nvar;
  1214. static int init = FALSE;
  1215. /* initialize variable if necessary */
  1216. if ( init == FALSE )
  1217. {
  1218. init = TRUE;
  1219. var_make( &nvar, NUMBER );
  1220. }
  1221. /* detect device number if present */
  1222. adv_ws( l->buffer, &( l->position ) );
  1223. if ( l->buffer[ l->position ] == '#' )
  1224. {
  1225. ++( l->position );
  1226. adv_element( l->buffer, &( l->position ), tbuf );
  1227. pos = 0;
  1228. e = bwb_exp( tbuf, FALSE, &pos );
  1229. adv_ws( l->buffer, &( l->position ) );
  1230. if ( l->buffer[ l->position ] == ',' )
  1231. {
  1232. ++( l->position );
  1233. }
  1234. else
  1235. {
  1236. #if PROG_ERRORS
  1237. bwb_error( "in bwb_write(): no comma after#n" );
  1238. #else
  1239. bwb_error( err_syntax );
  1240. #endif
  1241. return bwb_zline( l );
  1242. }
  1243. req_devnumber = (int) exp_getnval( e );
  1244. /* check the requested device number */
  1245. if ( ( req_devnumber < 0 ) || ( req_devnumber >= DEF_DEVICES ))
  1246. {
  1247. #if PROG_ERRORS
  1248. bwb_error( "in bwb_write(): Requested device number is out of range." );
  1249. #else
  1250. bwb_error( err_devnum );
  1251. #endif
  1252. return bwb_zline( l );
  1253. }
  1254. if (( dev_table[ req_devnumber ].mode == DEVMODE_CLOSED ) ||
  1255. ( dev_table[ req_devnumber ].mode == DEVMODE_AVAILABLE ))
  1256. {
  1257. #if PROG_ERRORS
  1258. bwb_error( "in bwb_write(): Requested device number is not open." );
  1259. #else
  1260. bwb_error( err_devnum );
  1261. #endif
  1262. return bwb_zline( l );
  1263. }
  1264. if ( dev_table[ req_devnumber ].mode != DEVMODE_OUTPUT )
  1265. {
  1266. #if PROG_ERRORS
  1267. bwb_error( "in bwb_write(): Requested device is not open for OUTPUT." );
  1268. #else
  1269. bwb_error( err_devnum );
  1270. #endif
  1271. return bwb_zline( l );
  1272. }
  1273. #if INTENSIVE_DEBUG
  1274. sprintf( bwb_ebuf, "in bwb_write(): device number is <%d>",
  1275. req_devnumber );
  1276. bwb_debug( bwb_ebuf );
  1277. #endif
  1278. /* look up the requested device in the device table */
  1279. fp = dev_table[ req_devnumber ].cfp;
  1280. }
  1281. else
  1282. {
  1283. fp = stdout;
  1284. }
  1285. /* be sure there is an element to print */
  1286. adv_ws( l->buffer, &( l->position ) );
  1287. loop = TRUE;
  1288. switch( l->buffer[ l->position ] )
  1289. {
  1290. case '\n':
  1291. case '\r':
  1292. case '\0':
  1293. case ':':
  1294. loop = FALSE;
  1295. break;
  1296. }
  1297. /* loop through elements */
  1298. while ( loop == TRUE )
  1299. {
  1300. /* get the next element */
  1301. e = bwb_exp( l->buffer, FALSE, &( l->position ));
  1302. /* perform type-specific output */
  1303. switch( e->type )
  1304. {
  1305. case STRING:
  1306. xputc( fp, '\"' );
  1307. str_btoc( tbuf, exp_getsval( e ) );
  1308. prn_xprintf( fp, tbuf );
  1309. xputc( fp, '\"' );
  1310. #if INTENSIVE_DEBUG
  1311. sprintf( bwb_ebuf, "in bwb_write(): output string element <\"%s\">",
  1312. tbuf );
  1313. bwb_debug( bwb_ebuf );
  1314. #endif
  1315. break;
  1316. default:
  1317. * var_findnval( &nvar, nvar.array_pos ) =
  1318. exp_getnval( e );
  1319. #if NUMBER_DOUBLE
  1320. sprintf( tbuf, " %.*lf", prn_precision( &nvar ),
  1321. var_getnval( &nvar ) );
  1322. #else
  1323. sprintf( tbuf, " %.*f", prn_precision( &nvar ),
  1324. var_getnval( &nvar ) );
  1325. #endif
  1326. prn_xprintf( fp, tbuf );
  1327. #if INTENSIVE_DEBUG
  1328. sprintf( bwb_ebuf, "in bwb_write(): output numerical element <%s>",
  1329. tbuf );
  1330. bwb_debug( bwb_ebuf );
  1331. #endif
  1332. break;
  1333. } /* end of case for type-specific output */
  1334. /* seek a comma at end of element */
  1335. adv_ws( l->buffer, &( l->position ) );
  1336. if ( l->buffer[ l->position ] == ',' )
  1337. {
  1338. xputc( fp, ',' );
  1339. ++( l->position );
  1340. }
  1341. /* no comma: end the loop */
  1342. else
  1343. {
  1344. loop = FALSE;
  1345. }
  1346. } /* end of loop through elements */
  1347. /* print LF */
  1348. xputc( fp, '\n' );
  1349. /* return */
  1350. return bwb_zline( l );
  1351. }
  1352. #endif