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.
 
 
 
 
 
 

2927 lines
68 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. /*---------------------------------------------------------------*/
  18. /* NOTE: Modifications marked "JBV" were made by Jon B. Volkoff, */
  19. /* 11/1995 (eidetics@cerf.net). */
  20. /* */
  21. /* Those additionally marked with "DD" were at the suggestion of */
  22. /* Dale DePriest (daled@cadence.com). */
  23. /* */
  24. /* Version 3.00 by Howard Wulf, AF5NE */
  25. /* */
  26. /* Version 3.10 by Howard Wulf, AF5NE */
  27. /* */
  28. /*---------------------------------------------------------------*/
  29. #include "bwbasic.h"
  30. /*
  31. We try to allow as many legacy PRINT USING formats as reasonable.
  32. Many legacy PRINT USING formats are incompatible with one another.
  33. For example:
  34. 1) some use '%' for strings, others use '%' for numbers, others consider '%' as a lieral.
  35. 2) some count a leading or traling signs in the width, while others do not.
  36. 3) when a value requires more digits than the assigned width:
  37. a) some truncate the displayed value to the width,
  38. b) some expand the width,
  39. c) some print a number of '%' or '*', and
  40. d) some halt processing.
  41. There is no perfect solution that will work for all possible dialects.
  42. */
  43. #define PrintUsingNumberDigit My->CurrentVersion->UsingDigit /* Digit placeholder, usually '#' */
  44. #define PrintUsingNumberComma My->CurrentVersion->UsingComma /* Comma, such as thousands, usually ',' */
  45. #define PrintUsingNumberPeriod My->CurrentVersion->UsingPeriod /* Period, such as dollars and cents, usually '.' */
  46. #define PrintUsingNumberPlus My->CurrentVersion->UsingPlus /* Plus sign, positive value, usually '+' */
  47. #define PrintUsingNumberMinus My->CurrentVersion->UsingMinus /* Minus sign, negative value, usually '-' */
  48. #define PrintUsingNumberExponent My->CurrentVersion->UsingExrad /* Exponential format, usually '^' */
  49. #define PrintUsingNumberDollar My->CurrentVersion->UsingDollar /* Currency symbol, usually '$' */
  50. #define PrintUsingNumberFiller My->CurrentVersion->UsingFiller /* Print filler, such as checks, usually '*' */
  51. #define PrintUsingLiteral My->CurrentVersion->UsingLiteral /* The next char is a literal, usually '_' */
  52. #define PrintUsingStringFirst My->CurrentVersion->UsingFirst /* The first character of the string, usually '!' */
  53. #define PrintUsingStringAll My->CurrentVersion->UsingAll /* Print the entire string, usually '&' */
  54. #define PrintUsingStringLength My->CurrentVersion->UsingLength /* Print a substring, usually '%' */
  55. /* Prototypes for functions visible only to this file */
  56. static int get_prnfmt(char *buffer, int *position, VariantType *e);
  57. static int xputc(char c);
  58. static int xxputc(char c);
  59. static int xxxputc(char c);
  60. static int bwb_xprint(LineType * l);
  61. static int prn_xxprintf(char *buffer);
  62. int is_empty_filename( char * Buffer )
  63. {
  64. while( *Buffer == ' ' )
  65. {
  66. Buffer++;
  67. }
  68. if( *Buffer == BasicNulChar )
  69. {
  70. return TRUE;
  71. }
  72. return FALSE;
  73. }
  74. FileType * find_file_by_name( char * FileName )
  75. {
  76. FileType * F;
  77. if( is_empty_filename( FileName ) )
  78. {
  79. /* the rules for Console and Printer vary by command */
  80. return NULL;
  81. }
  82. /* search the list of OPEN files */
  83. for( F = My->file_head; F != NULL; F = F->next )
  84. {
  85. if( F->mode != DEVMODE_CLOSED )
  86. {
  87. if( bwb_stricmp( F->filename, FileName ) == 0 )
  88. {
  89. /* FOUND */
  90. return F;
  91. }
  92. }
  93. }
  94. /* NOT FOUND */
  95. return NULL;
  96. }
  97. FileType * find_file_by_number( int FileNumber )
  98. {
  99. FileType * F;
  100. /* handle MAGIC file numbers */
  101. if( FileNumber <= 0 )
  102. {
  103. /* the rules for Console and Printer vary by command */
  104. return NULL;
  105. }
  106. /* search the list of OPEN files */
  107. for( F = My->file_head; F != NULL; F = F->next )
  108. {
  109. if( F->mode != DEVMODE_CLOSED )
  110. {
  111. if( F->FileNumber == FileNumber )
  112. {
  113. /* FOUND */
  114. return F;
  115. }
  116. }
  117. }
  118. /* NOT FOUND */
  119. return NULL;
  120. }
  121. FileType * file_new( void )
  122. {
  123. /* search for an empty slot. If not found, add a new slot. */
  124. FileType * F;
  125. for( F = My->file_head; F != NULL; F = F->next )
  126. {
  127. if( F->mode == DEVMODE_CLOSED )
  128. {
  129. /* FOUND */
  130. return F;
  131. }
  132. }
  133. /* NOT FOUND */
  134. F = calloc( 1, sizeof( FileType ) );
  135. F->next = My->file_head;
  136. My->file_head = F;
  137. return F;
  138. }
  139. void file_clear( FileType * F )
  140. {
  141. /* clean up a file slot that is no longer needed */
  142. #if NEW_VIRTUAL
  143. clear_virtual_by_file( F->FileNumber );
  144. #endif /* NEW_VIRTUAL */
  145. F->FileNumber = 0;
  146. F->mode = DEVMODE_CLOSED; /* DEVMODE_ item */
  147. F->width = 0; /* width for OUTPUT and APPEND; reclen for RANDOM; not used for INPUT or BINARY */
  148. F->col = 0; /* current column for OUTPUT and APPEND */
  149. F->row = 0; /* current row for OUTPUT and APPEND */
  150. F->EOF_LineNumber = 0; /* CBASIC-II: IF END # filenumber THEN linenumber */
  151. F->delimit = BasicNulChar; /* DELIMIT for READ and WRITE */
  152. F->filename[0] = BasicNulChar; /* filename */
  153. if( F->cfp != NULL )
  154. {
  155. fclose( F->cfp ); /* F->cfp != NULL */
  156. F->cfp = NULL;
  157. }
  158. if( F->buffer != NULL ) /* pointer to character buffer for RANDOM */
  159. {
  160. free( F->buffer );
  161. F->buffer = NULL;
  162. }
  163. }
  164. int file_next_number( void )
  165. {
  166. int FileNumber;
  167. FileType * F;
  168. FileNumber = 0;
  169. for (F = My->file_head; F != NULL; F = F->next)
  170. {
  171. if (F->mode != DEVMODE_CLOSED)
  172. {
  173. if( F->FileNumber > FileNumber )
  174. {
  175. FileNumber = F->FileNumber;
  176. }
  177. }
  178. }
  179. /* 'FileNumber' is the highest FileNumber that is currently open */
  180. FileNumber++;
  181. return FileNumber;
  182. }
  183. /***************************************************************
  184. FUNCTION: bwx_putc()
  185. DESCRIPTION: This function outputs a single character
  186. to the default output device.
  187. ***************************************************************/
  188. int
  189. bwx_PRINT(char c)
  190. {
  191. bwx_DEBUG(__FUNCTION__);
  192. /* send character to console */
  193. return fputc(c, My->SYSOUT->cfp);
  194. }
  195. int
  196. bwx_LPRINT(char c)
  197. {
  198. bwx_DEBUG(__FUNCTION__);
  199. /* send character to printer */
  200. return fputc(c, My->SYSPRN->cfp);
  201. }
  202. int
  203. prn_lprintf(char *buffer)
  204. {
  205. bwx_DEBUG(__FUNCTION__);
  206. while (*buffer != BasicNulChar)
  207. {
  208. bwx_LPRINT(*buffer);
  209. buffer++;
  210. }
  211. return 0;
  212. }
  213. int
  214. prn_xprintf(char *buffer)
  215. {
  216. /* Catch-22: an error has occurred before the devicce table is loaded */
  217. int n;
  218. bwx_DEBUG(__FUNCTION__);
  219. n = My->SYSOUT->width;
  220. if (n > 0)
  221. {
  222. int i;
  223. i = 0;
  224. while (*buffer)
  225. {
  226. fputc(*buffer, My->SYSOUT->cfp);
  227. buffer++;
  228. if (*buffer == '\n')
  229. {
  230. i = 0;
  231. }
  232. i++;
  233. if (i >= n)
  234. {
  235. fputc('\n', My->SYSOUT->cfp);
  236. i = 0;
  237. }
  238. }
  239. }
  240. else
  241. {
  242. /* raw */
  243. while (*buffer)
  244. {
  245. fputc(*buffer, My->SYSOUT->cfp);
  246. buffer++;
  247. }
  248. }
  249. fflush(My->SYSOUT->cfp);
  250. return 0;
  251. }
  252. static void CleanNumericString(char *prnbuf, int RemoveDot)
  253. {
  254. /* remove trailing zeroes */
  255. char *E;
  256. char *D;
  257. bwx_DEBUG(__FUNCTION__);
  258. E = bwb_strchr(prnbuf, 'E');
  259. if (E == NULL)
  260. {
  261. E = bwb_strchr(prnbuf, 'e');
  262. }
  263. if (E)
  264. {
  265. /* SCIENTIFIC == SCALED notation */
  266. /* trim leading zeroes in exponent */
  267. char *F;
  268. char *G;
  269. F = E;
  270. while (bwb_isalpha(*F))
  271. {
  272. F++;
  273. }
  274. while (*F == '+' || *F == '-')
  275. {
  276. /* skip sign */
  277. F++;
  278. }
  279. G = F;
  280. while (*G == '0' || *G == ' ')
  281. {
  282. /* skip leading zeroes or spaces */
  283. G++;
  284. }
  285. if (G > F)
  286. {
  287. bwb_strcpy(F, G);
  288. }
  289. G = NULL; /* no longer valid */
  290. *E = BasicNulChar; /* for bwb_strlen() */
  291. }
  292. D = bwb_strchr(prnbuf, '.');
  293. if (D)
  294. {
  295. int N = bwb_strlen(D);
  296. if (N > 1)
  297. {
  298. int M;
  299. N--;
  300. M = N;
  301. while (D[N] == '0')
  302. {
  303. /* remove trailing zeroes */
  304. D[N] = '_';
  305. N--;
  306. }
  307. if (RemoveDot)
  308. {
  309. if (E)
  310. {
  311. /* SCIENTIFIC == SCALED notation */
  312. /* do NOT remove '.' */
  313. }
  314. else
  315. {
  316. /* NORMAL == UNSCALED notation */
  317. /* remove trailing '.' */
  318. /* this will only occur for integer
  319. * values */
  320. while (D[N] == '.')
  321. {
  322. /* _###. POSITIVE INTEGER */
  323. /* -###. NEGATIVE INTEGER */
  324. D[N] = '_';
  325. N--;
  326. }
  327. }
  328. }
  329. if (N < M)
  330. {
  331. if (E)
  332. {
  333. /* SCIENTIFIC == SCALED notation */
  334. *E = 'E';
  335. E = NULL;
  336. }
  337. N++;
  338. /* if INTEGER, then N == 0, else N > 0 */
  339. M++;
  340. /* if SCIENTIFIC, then *M == 'E' else *M == BasicNulChar */
  341. bwb_strcpy(&(D[N]), &(D[M]));
  342. }
  343. }
  344. }
  345. if (E)
  346. {
  347. /* SCIENTIFIC == SCALED notation */
  348. *E = 'E';
  349. E = NULL;
  350. }
  351. if (prnbuf[1] == '0' && prnbuf[2] == '.')
  352. {
  353. /* _0.### POSITIVE FRACTION ==> _.### */
  354. /* -0.### NEGATIVE FRACTION ==> -.### */
  355. bwb_strcpy(&(prnbuf[1]), &(prnbuf[2]));
  356. }
  357. if (prnbuf[1] == '.' && prnbuf[2] == 'E')
  358. {
  359. /* _.E POSITIVE ZERO ==> _0 */
  360. /* -.E NEGATIVE ZERO ==> _0 */
  361. bwb_strcpy(prnbuf, " 0");
  362. }
  363. }
  364. static int SignificantDigits(char *Buffer)
  365. {
  366. int NumDigits;
  367. char *P;
  368. bwx_DEBUG(__FUNCTION__);
  369. /* determine the number of significant digits */
  370. NumDigits = 0;
  371. P = Buffer;
  372. while (*P)
  373. {
  374. if (bwb_isalpha(*P))
  375. {
  376. /* 'E', 'e', and so on. */
  377. break;
  378. }
  379. if (bwb_isdigit(*P))
  380. {
  381. NumDigits++;
  382. }
  383. P++;
  384. }
  385. return NumDigits;
  386. }
  387. void
  388. BasicNumerc(BasicNumberType Input, char *Output)
  389. {
  390. /*
  391. ********************************************************************************
  392. This is essentially sprintf( Output, "%g", Input ),
  393. except the rules for selecting between "%e", "%f", and "%d" are different.
  394. The C rules depend upon the value of the exponent.
  395. The BASIC rules depend upon the number of significant digits.
  396. The results of this routine have been verified by the NBS2 test suite, so
  397. THINK VERY CAREFULLY BEFORE MAKING ANY CHANGES TO THIS ROUTINE.
  398. ********************************************************************************
  399. */
  400. char *E;
  401. bwx_DEBUG(__FUNCTION__);
  402. /* print in scientific form first, to determine exponent and significant digits */
  403. sprintf(Output, "% 1.*E", SIGNIFICANT_DIGITS - 1, Input);
  404. E = bwb_strchr(Output, 'E');
  405. if (E == NULL)
  406. {
  407. E = bwb_strchr(Output, 'e');
  408. }
  409. if (E)
  410. {
  411. /* valid */
  412. int Exponent;
  413. int NumDigits;
  414. int DisplayDigits;
  415. int zz;
  416. char *F; /* pointer to the exponent's value */
  417. F = E;
  418. while (bwb_isalpha(*F))
  419. {
  420. F++;
  421. }
  422. Exponent = atoi(F);
  423. CleanNumericString(Output, 0);
  424. NumDigits = SignificantDigits(Output);
  425. DisplayDigits = MIN( NumDigits, SIGNIFICANT_DIGITS );
  426. zz = MAX(Exponent,DisplayDigits - Exponent - 2);
  427. if (zz >= SIGNIFICANT_DIGITS)
  428. {
  429. /* SCIENTIFIC */
  430. sprintf(Output, "%# 1.*E", DisplayDigits - 1, Input);
  431. }
  432. else
  433. if (Input == (int) Input)
  434. {
  435. /* INTEGER */
  436. sprintf(Output, "% *d", DisplayDigits, (int) Input);
  437. }
  438. else
  439. {
  440. /* FLOAT */
  441. int N;
  442. /* number of digits before the '.' */
  443. int M;
  444. /* number of digits after the '.' */
  445. N = Exponent + 1;
  446. if (N < 0)
  447. {
  448. N = 0;
  449. }
  450. M = SIGNIFICANT_DIGITS - N;
  451. if (M < 0)
  452. {
  453. M = 0;
  454. }
  455. sprintf(Output, "%# *.*f", N, M, Input);
  456. }
  457. CleanNumericString(Output, 0);
  458. }
  459. else
  460. {
  461. /* ERROR, NAN, INFINITY, ETC. */
  462. }
  463. return;
  464. }
  465. LineType *
  466. bwb_LPRINT(LineType * l)
  467. {
  468. bwx_DEBUG(__FUNCTION__);
  469. My->CurrentFile = My->SYSPRN;
  470. bwb_xprint(l);
  471. return bwb_zline(l);
  472. }
  473. /***************************************************************
  474. FUNCTION: bwb_print()
  475. DESCRIPTION: This function implements the BASIC PRINT
  476. command.
  477. SYNTAX: PRINT [# device-number,][USING format-string$;] expressions...
  478. ***************************************************************/
  479. LineType *
  480. bwb_QUEST(LineType * l)
  481. {
  482. bwx_DEBUG(__FUNCTION__);
  483. return bwb_PRINT(l);
  484. }
  485. static int bwb_print_at(LineType * l)
  486. {
  487. int position;
  488. int r;
  489. int c;
  490. position = 0;
  491. r = 0;
  492. c = 0;
  493. if( line_read_integer_expression(l, &position) == FALSE )
  494. {
  495. WARN_SYNTAX_ERROR;
  496. return FALSE;
  497. }
  498. if ( line_skip_comma(l))
  499. {
  500. /* OK */
  501. }
  502. else
  503. {
  504. WARN_SYNTAX_ERROR;
  505. return FALSE;
  506. }
  507. if( position < 0 )
  508. {
  509. WARN_SYNTAX_ERROR;
  510. return FALSE;
  511. }
  512. if( My->CurrentFile->width <= 0 )
  513. {
  514. WARN_SYNTAX_ERROR;
  515. return FALSE;
  516. }
  517. if( My->SCREEN_ROWS <= 0 )
  518. {
  519. WARN_SYNTAX_ERROR;
  520. return FALSE;
  521. }
  522. r = position / My->CurrentFile->width;
  523. c = position - r * My->CurrentFile->width;
  524. while( r >= My->SCREEN_ROWS )
  525. {
  526. r -= My->SCREEN_ROWS;
  527. }
  528. r++;
  529. c++;
  530. switch (My->OptionTerminalType)
  531. {
  532. case C_OPTION_TERMINAL_NONE:
  533. break;
  534. case C_OPTION_TERMINAL_ADM:
  535. fprintf(My->CurrentFile->cfp, "%c=%c%c", 27, r + 32, c + 32);
  536. break;
  537. case C_OPTION_TERMINAL_ANSI:
  538. fprintf(My->CurrentFile->cfp, "%c[%d;%dH", 27, r, c);
  539. break;
  540. default:
  541. WARN_SYNTAX_ERROR;
  542. return FALSE;
  543. /* break; */
  544. }
  545. My->CurrentFile->row = r;
  546. My->CurrentFile->col = c;
  547. return TRUE;
  548. }
  549. static int bwb_print_num(LineType * l)
  550. {
  551. int UserFileNumber;
  552. if( line_read_integer_expression(l, &UserFileNumber) == FALSE )
  553. {
  554. WARN_SYNTAX_ERROR;
  555. return FALSE;
  556. }
  557. if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) )
  558. {
  559. /*
  560. CBASIC-II: SERIAL & RANDOM file writes
  561. PRINT # file_number ; expression [, expression] ' SERIAL write
  562. PRINT # file_number , record_number ; expression [, expression] ' RANDOM write
  563. */
  564. if( UserFileNumber <= 0 )
  565. {
  566. WARN_BAD_FILE_NUMBER;
  567. return FALSE;
  568. }
  569. /* normal file */
  570. My->CurrentFile = find_file_by_number( UserFileNumber );
  571. if( My->CurrentFile == NULL )
  572. {
  573. WARN_BAD_FILE_NUMBER;
  574. return FALSE;
  575. }
  576. if( line_skip_char( l, ',' ) )
  577. {
  578. /*
  579. PRINT # file_number , record_number ; expression [, expression] ' RANDOM write
  580. */
  581. /* get the RecordNumber */
  582. int RecordNumber;
  583. if( (My->CurrentFile->mode & DEVMODE_RANDOM) == 0 )
  584. {
  585. WARN_BAD_FILE_MODE;
  586. return FALSE;
  587. }
  588. if( My->CurrentFile->width <= 0 )
  589. {
  590. WARN_FIELD_OVERFLOW;
  591. return FALSE;
  592. }
  593. if( line_read_integer_expression( l, &RecordNumber ) == FALSE )
  594. {
  595. WARN_SYNTAX_ERROR;
  596. return FALSE;
  597. }
  598. if( RecordNumber <= 0 )
  599. {
  600. WARN_BAD_RECORD_NUMBER;
  601. return FALSE;
  602. }
  603. RecordNumber--; /* BASIC to C */
  604. /* if( TRUE ) */
  605. {
  606. long offset;
  607. offset = RecordNumber;
  608. offset *= My->CurrentFile->width;
  609. fseek( My->CurrentFile->cfp, offset, SEEK_SET );
  610. }
  611. }
  612. if( line_is_eol( l ) )
  613. {
  614. /* PRINT # filenum */
  615. /* PRINT # filenum , recnum */
  616. }
  617. else
  618. if( line_skip_char( l, ';' ) )
  619. {
  620. /* PRINT # filenum ; */
  621. /* PRINT # filenum , recnum ; */
  622. }
  623. else
  624. {
  625. WARN_SYNTAX_ERROR;
  626. return FALSE;
  627. }
  628. return TRUE;
  629. }
  630. /*
  631. SERIAL file writes:
  632. PRINT # file_number
  633. PRINT # file_number [, expression]
  634. */
  635. if( UserFileNumber < 0 )
  636. {
  637. My->CurrentFile = My->SYSPRN;
  638. }
  639. else
  640. if( UserFileNumber == 0 )
  641. {
  642. My->CurrentFile = My->SYSOUT;
  643. }
  644. else
  645. {
  646. /* normal file */
  647. My->CurrentFile = find_file_by_number( UserFileNumber );
  648. }
  649. if( My->CurrentFile == NULL )
  650. {
  651. WARN_BAD_FILE_NUMBER;
  652. return FALSE;
  653. }
  654. if( ( My->CurrentFile->mode & DEVMODE_WRITE ) == 0 )
  655. {
  656. WARN_BAD_FILE_NUMBER;
  657. return FALSE;
  658. }
  659. if( line_is_eol( l ) )
  660. {
  661. /* PRINT # 2 */
  662. }
  663. else
  664. if( line_skip_comma( l ) )
  665. {
  666. /* PRINT # 2 , ... */
  667. }
  668. else
  669. {
  670. WARN_SYNTAX_ERROR;
  671. return FALSE;
  672. }
  673. return TRUE;
  674. }
  675. LineType *
  676. bwb_PRINT(LineType * l)
  677. {
  678. bwx_DEBUG(__FUNCTION__);
  679. My->CurrentFile = My->SYSOUT;
  680. line_skip_spaces(l);
  681. if ( line_skip_char(l,'@'))
  682. {
  683. /* PRINT @ position, ... */
  684. if( bwb_print_at(l) == FALSE )
  685. {
  686. return bwb_zline(l);
  687. }
  688. }
  689. else
  690. if ( line_skip_word( l, "AT") )
  691. {
  692. /* PRINT AT position, ... */
  693. if( bwb_print_at(l) == FALSE )
  694. {
  695. return bwb_zline(l);
  696. }
  697. }
  698. else
  699. if ( line_skip_char(l,BasicFileNumberPrefix))
  700. {
  701. /* PRINT # file, ... */
  702. if( bwb_print_num(l) == FALSE )
  703. {
  704. return bwb_zline(l);
  705. }
  706. }
  707. bwb_xprint(l);
  708. if( My->CurrentFile == My->SYSOUT )
  709. {
  710. /* FOR I = 1 TO 1000: PRINT "."; : NEXT I : PRINT */
  711. fflush(My->SYSOUT->cfp);
  712. }
  713. return bwb_zline(l);
  714. }
  715. /***************************************************************
  716. FUNCTION: bwb_xprint()
  717. DESCRIPTION: This function implements the PRINT
  718. command, utilizing a specified file our
  719. output device.
  720. ***************************************************************/
  721. static int buff_read_using( char * buffer, int * position, char * format_string )
  722. {
  723. int p;
  724. p = *position;
  725. buff_skip_spaces( buffer, &p );
  726. if( buff_skip_word( buffer, &p, "USING" ) )
  727. {
  728. buff_skip_spaces( buffer, &p );
  729. if( bwb_isdigit( buffer[p] ) )
  730. {
  731. /* PRINT USING ### */
  732. int LineNumber;
  733. LineType *x = NULL;
  734. char *C;
  735. char *F;
  736. if( buff_read_line_number(buffer, &p, &LineNumber) == FALSE )
  737. {
  738. WARN_SYNTAX_ERROR;
  739. return FALSE;
  740. }
  741. /* check for target label */
  742. x = find_line_number( LineNumber, TRUE );
  743. if (x == NULL)
  744. {
  745. WARN_UNDEFINED_LINE;
  746. return FALSE;
  747. }
  748. /* line exists */
  749. if( x->cmdnum != C_IMAGE )
  750. {
  751. WARN_UNDEFINED_LINE;
  752. return FALSE;
  753. }
  754. /* line contains IMAGE command */
  755. C = x->buffer;
  756. C += x->Startpos;
  757. F = format_string;
  758. /* look for leading quote in IMAGE "..." */
  759. while( *C == ' ' )
  760. {
  761. C++;
  762. }
  763. if( *C == BasicQuoteChar )
  764. {
  765. /* QUOTED */
  766. /* skip leading quote */
  767. C++;
  768. while( *C != BasicQuoteChar && *C != BasicNulChar )
  769. {
  770. /* copy format string, but not the trailing quote */
  771. *F = *C;
  772. C++;
  773. F++;
  774. }
  775. /* skip trailing quote */
  776. }
  777. else
  778. {
  779. /* UNQUOTED */
  780. while( *C != BasicNulChar )
  781. {
  782. /* copy format string verbatim */
  783. *F = *C;
  784. C++;
  785. F++;
  786. }
  787. }
  788. /* terminate format string */
  789. *F = BasicNulChar;
  790. buff_skip_spaces(buffer, &p);
  791. if ( buff_skip_comma(buffer, &p ) == FALSE)
  792. {
  793. WARN_SYNTAX_ERROR;
  794. return FALSE;
  795. }
  796. buff_skip_spaces(buffer, &p);
  797. }
  798. else
  799. {
  800. {
  801. char * Value = NULL;
  802. if( buff_read_string_expression( buffer, &p, &Value ) == FALSE )
  803. {
  804. WARN_SYNTAX_ERROR; /* HERE-Here-here BUG? */
  805. return FALSE;
  806. }
  807. if( Value == NULL )
  808. {
  809. WARN_SYNTAX_ERROR;
  810. return FALSE;
  811. }
  812. bwb_strcpy( format_string, Value );
  813. free( Value );
  814. }
  815. buff_skip_spaces(buffer, &p);
  816. if ( buff_skip_comma(buffer, &p) == FALSE)
  817. {
  818. WARN_SYNTAX_ERROR;
  819. return FALSE;
  820. }
  821. buff_skip_spaces(buffer, &p);
  822. }
  823. *position = p;
  824. return TRUE;
  825. }
  826. return FALSE;
  827. }
  828. static int bwb_xprint(LineType * l)
  829. {
  830. int Success = FALSE;
  831. VariantType e; /* no leaks */
  832. VariantType *E = &e; /* no leaks */
  833. static int fs_pos;
  834. int OutputCR;
  835. char format_string[BasicStringLengthMax + 1];
  836. bwx_DEBUG(__FUNCTION__);
  837. CLEAR_VARIANT( E );
  838. /* Detect USING Here */
  839. format_string[0] = BasicNulChar;
  840. fs_pos = 0;
  841. /* get "USING" in format_string */
  842. if( buff_read_using( l->buffer, &(l->position), format_string ) == TRUE )
  843. {
  844. fs_pos = 0;
  845. }
  846. /* if no arguments, simply print CR and return */
  847. /* LOOP THROUGH PRINT ELEMENTS */
  848. OutputCR = TRUE;
  849. /* 1980 PRINT , , ,"A" */
  850. line_skip_spaces(l);
  851. while( line_is_eol(l) == FALSE )
  852. {
  853. /* 1980 PRINT , , ,"A" */
  854. if ( line_skip_char( l, ',' /* comma-specific */ ) )
  855. {
  856. /* tab over */
  857. OutputCR = FALSE;
  858. if( format_string[0] == BasicNulChar )
  859. {
  860. /* Tab only if there's no format specification! (JBV) */
  861. if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) && My->CurrentFile->FileNumber > 0 )
  862. {
  863. /* CBASIC-II: files use commas between values */
  864. xputc(',');
  865. }
  866. else
  867. {
  868. xputc('\t');
  869. }
  870. }
  871. if( buff_read_using( l->buffer, &(l->position), format_string ) == TRUE )
  872. {
  873. fs_pos = 0;
  874. OutputCR = TRUE;
  875. }
  876. }
  877. else
  878. if ( line_skip_char( l, ';' /* semicolon-specific */ ) )
  879. {
  880. /* concatenate strings */
  881. OutputCR = FALSE;
  882. if( buff_read_using( l->buffer, &(l->position), format_string ) == TRUE )
  883. {
  884. fs_pos = 0;
  885. OutputCR = TRUE;
  886. }
  887. if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) && My->CurrentFile->FileNumber > 0 )
  888. {
  889. /* CBASIC-II: files cannot use semicolon */
  890. WARN_SYNTAX_ERROR;
  891. goto EXIT;
  892. }
  893. }
  894. else
  895. {
  896. /* resolve the string */
  897. OutputCR = TRUE;
  898. if( line_read_expression( l, E ) == FALSE )
  899. {
  900. WARN_SYNTAX_ERROR;
  901. goto EXIT;
  902. }
  903. if( bwb_Warning_Pending() /* Keep This */ )
  904. {
  905. /*
  906. this might look odd...
  907. but we want to abort printing on the first warning.
  908. The expression list could include a function with side-effects,
  909. so any warning should immediately halt further evaluation.
  910. */
  911. goto EXIT;
  912. }
  913. if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) && My->CurrentFile->FileNumber > 0 )
  914. {
  915. /* CBASIC-II: files have quoted strings */
  916. if( E->TypeChar == BasicStringSuffix )
  917. {
  918. xputc('\"');
  919. }
  920. }
  921. if( get_prnfmt(format_string, &fs_pos, E ) == FALSE )
  922. {
  923. WARN_SYNTAX_ERROR;
  924. goto EXIT;
  925. }
  926. if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) && My->CurrentFile->FileNumber > 0 )
  927. {
  928. /* CBASIC-II: files have quoted strings */
  929. if( E->TypeChar == BasicStringSuffix )
  930. {
  931. xputc('\"');
  932. }
  933. }
  934. RELEASE( E );
  935. }
  936. line_skip_spaces(l);
  937. } /* end of loop through print elements */
  938. if (OutputCR == TRUE)
  939. {
  940. /* did not end with ',' or ';' */
  941. xputc('\n');
  942. }
  943. Success = TRUE;
  944. EXIT:
  945. RELEASE( E );
  946. return Success;
  947. }
  948. /***************************************************************
  949. FUNCTION: get_prnfmt()
  950. DESCRIPTION: This function gets the PRINT USING
  951. format string, returning a structure
  952. to the format.
  953. ***************************************************************/
  954. static int num_prnfmt(char *buffer, int *position, VariantType *e)
  955. {
  956. /*
  957. Format a NUMBER.
  958. 'buffer' points to the beginning of a PRINT USING format string, such as "###.##".
  959. 'position' is the current offset in 'buffer'.
  960. 'e' is the current expression to print.
  961. */
  962. int width = 0;
  963. int precision = 0;
  964. int exponent = 0;
  965. char HeadChar = ' ';
  966. char FillChar = ' ';
  967. char CurrChar = ' ';
  968. char ComaChar = ' ';
  969. char TailChar = ' ';
  970. int p;
  971. char tbuf[BasicStringLengthMax + 1];
  972. p = *position;
  973. while( IS_CHAR(buffer[p], PrintUsingNumberPlus) || IS_CHAR( buffer[p], PrintUsingNumberMinus ) )
  974. {
  975. HeadChar = buffer[p];
  976. width++;
  977. p++;
  978. }
  979. while( IS_CHAR(buffer[p], PrintUsingNumberFiller) || IS_CHAR(buffer[p], PrintUsingNumberDollar ) )
  980. {
  981. if( IS_CHAR( buffer[p], PrintUsingNumberFiller ) )
  982. {
  983. FillChar = PrintUsingNumberFiller;
  984. }
  985. else
  986. if( IS_CHAR( buffer[p], PrintUsingNumberDollar ) )
  987. {
  988. CurrChar = PrintUsingNumberDollar;
  989. }
  990. width++;
  991. p++;
  992. }
  993. while( IS_CHAR(buffer[p], PrintUsingNumberDigit) || IS_CHAR(buffer[p], PrintUsingNumberComma ) )
  994. {
  995. if( IS_CHAR( buffer[p], PrintUsingNumberComma ) )
  996. {
  997. ComaChar = PrintUsingNumberComma;
  998. }
  999. width++;
  1000. p++;
  1001. }
  1002. if( IS_CHAR( buffer[p], PrintUsingNumberPeriod ) )
  1003. {
  1004. while( IS_CHAR( buffer[p], PrintUsingNumberPeriod ) )
  1005. {
  1006. width++;
  1007. p++;
  1008. }
  1009. while( IS_CHAR( buffer[p], PrintUsingNumberDigit ) )
  1010. {
  1011. precision++;
  1012. width++;
  1013. p++;
  1014. }
  1015. }
  1016. while( IS_CHAR( buffer[p], PrintUsingNumberExponent ) )
  1017. {
  1018. exponent++;
  1019. precision++;
  1020. width++;
  1021. p++;
  1022. }
  1023. while( IS_CHAR( buffer[p], PrintUsingNumberPlus ) || IS_CHAR( buffer[p], PrintUsingNumberMinus ) )
  1024. {
  1025. TailChar = buffer[p];
  1026. width++;
  1027. p++;
  1028. }
  1029. /* format the number */
  1030. /* displaying both a Heading and a Trailing sign is NOT supported */
  1031. if( TailChar == ' ' )
  1032. {
  1033. /* do nothing */
  1034. }
  1035. else
  1036. if( IS_CHAR( TailChar, PrintUsingNumberPlus ) || IS_CHAR( TailChar, PrintUsingNumberMinus ) )
  1037. {
  1038. /* force the sign to be printed, so we can move it */
  1039. HeadChar = TailChar;
  1040. }
  1041. else
  1042. {
  1043. WARN_INTERNAL_ERROR;
  1044. return FALSE;
  1045. }
  1046. if( HeadChar == ' ' )
  1047. {
  1048. /* only display a '-' sign */
  1049. if( exponent > 0 )
  1050. {
  1051. sprintf(tbuf, "%*.*e", width, precision, e->Number);
  1052. }
  1053. else
  1054. {
  1055. sprintf(tbuf, "%*.*f", width, precision, e->Number);
  1056. }
  1057. }
  1058. else
  1059. if( IS_CHAR( HeadChar, PrintUsingNumberPlus ) || IS_CHAR( HeadChar, PrintUsingNumberMinus ) )
  1060. {
  1061. /* force a leading sign '+' or '-' */
  1062. if( exponent > 0 )
  1063. {
  1064. sprintf(tbuf, "%+*.*e", width, precision, e->Number);
  1065. }
  1066. else
  1067. {
  1068. sprintf(tbuf, "%+*.*f", width, precision, e->Number);
  1069. }
  1070. }
  1071. else
  1072. {
  1073. WARN_INTERNAL_ERROR;
  1074. return FALSE;
  1075. }
  1076. if( TailChar == ' ' )
  1077. {
  1078. /* do nothing */
  1079. }
  1080. else
  1081. if( IS_CHAR( TailChar, PrintUsingNumberPlus ) || IS_CHAR( TailChar, PrintUsingNumberMinus ) )
  1082. {
  1083. /* move sign '+' or '-' to end */
  1084. int i;
  1085. int n;
  1086. n = bwb_strlen(tbuf);
  1087. for (i = 0; i < n; i++)
  1088. {
  1089. if( tbuf[i] != ' ' )
  1090. {
  1091. if( IS_CHAR( tbuf[i], PrintUsingNumberPlus ) )
  1092. {
  1093. tbuf[i] = ' ';
  1094. if( IS_CHAR( TailChar, PrintUsingNumberPlus ) )
  1095. {
  1096. /* TailChar of '+' does print a '+' */
  1097. bwb_strcat(tbuf,"+");
  1098. }
  1099. else
  1100. if( IS_CHAR( TailChar, PrintUsingNumberMinus ) )
  1101. {
  1102. /* TailChar of '-' does NOT print a '+' */
  1103. bwb_strcat(tbuf," ");
  1104. }
  1105. }
  1106. else
  1107. if( IS_CHAR( tbuf[i], PrintUsingNumberMinus ) )
  1108. {
  1109. tbuf[i] = ' ';
  1110. bwb_strcat(tbuf,"-");
  1111. }
  1112. break;
  1113. }
  1114. }
  1115. if( tbuf[0] == ' ' )
  1116. {
  1117. n = bwb_strlen(tbuf);
  1118. /* n > 0 */
  1119. for( i = 1; i < n; i++ )
  1120. {
  1121. tbuf[i-1] = tbuf[i];
  1122. }
  1123. tbuf[n-1] = BasicNulChar;
  1124. }
  1125. }
  1126. else
  1127. {
  1128. WARN_INTERNAL_ERROR;
  1129. return FALSE;
  1130. }
  1131. if( CurrChar == ' ' )
  1132. {
  1133. /* do nothing */
  1134. }
  1135. else
  1136. if( IS_CHAR( CurrChar, PrintUsingNumberDollar ) )
  1137. {
  1138. int i;
  1139. int n;
  1140. n = bwb_strlen(tbuf);
  1141. for (i = 0; i < n; i++)
  1142. {
  1143. if (tbuf[i] != ' ')
  1144. {
  1145. if (i > 0)
  1146. {
  1147. if (bwb_isdigit(tbuf[i]))
  1148. {
  1149. tbuf[i - 1] = CurrChar;
  1150. }
  1151. else
  1152. {
  1153. /* sign char */
  1154. tbuf[i - 1] = tbuf[i];
  1155. tbuf[i] = CurrChar;
  1156. }
  1157. }
  1158. break;
  1159. }
  1160. }
  1161. }
  1162. else
  1163. {
  1164. WARN_INTERNAL_ERROR;
  1165. return FALSE;
  1166. }
  1167. if( FillChar == ' ' )
  1168. {
  1169. /* do nothing */
  1170. }
  1171. else
  1172. if( IS_CHAR( FillChar, PrintUsingNumberFiller ) )
  1173. {
  1174. int i;
  1175. int n;
  1176. n = bwb_strlen(tbuf);
  1177. for (i = 0; i < n; i++)
  1178. {
  1179. if (tbuf[i] != ' ')
  1180. {
  1181. break;
  1182. }
  1183. tbuf[i] = PrintUsingNumberFiller;
  1184. }
  1185. }
  1186. else
  1187. {
  1188. WARN_INTERNAL_ERROR;
  1189. return FALSE;
  1190. }
  1191. if( ComaChar == ' ' )
  1192. {
  1193. prn_xxprintf(tbuf);
  1194. }
  1195. else
  1196. if( IS_CHAR( ComaChar, PrintUsingNumberComma ) )
  1197. {
  1198. int dig_pos = -1;
  1199. int dec_pos = -1;
  1200. int i;
  1201. int n;
  1202. int commas;
  1203. n = bwb_strlen(tbuf);
  1204. for (i = 0; i < n; i++)
  1205. {
  1206. if ((bwb_isdigit(tbuf[i]) != 0) && (dig_pos == -1))
  1207. {
  1208. dig_pos = i;
  1209. }
  1210. if ((tbuf[i] == PrintUsingNumberPeriod) && (dec_pos == -1))
  1211. {
  1212. dec_pos = i;
  1213. }
  1214. if ((dig_pos != -1) && (dec_pos != -1))
  1215. {
  1216. break;
  1217. }
  1218. }
  1219. if (dig_pos == -1)
  1220. {
  1221. dec_pos = n;
  1222. }
  1223. if (dec_pos == -1)
  1224. {
  1225. dec_pos = n;
  1226. }
  1227. /* count the number of commas */
  1228. commas = 0;
  1229. for (i = 0; i < n; ++i)
  1230. {
  1231. if (((dec_pos - i) % 3 == 0) && (i > dig_pos) && (i < dec_pos))
  1232. {
  1233. commas++;
  1234. }
  1235. }
  1236. /* now, actually print */
  1237. for( i = 0; i < n; i++ )
  1238. {
  1239. if( i < commas && IS_CHAR( tbuf[i], FillChar ) )
  1240. {
  1241. /*
  1242. Ignore the same number of leading spaces as there are commas.
  1243. While not perfect for all possible cases,
  1244. it is usually good enough for practical purposes.
  1245. */
  1246. }
  1247. else
  1248. {
  1249. if (((dec_pos - i) % 3 == 0) && (i > dig_pos) && (i < dec_pos))
  1250. {
  1251. xxputc(PrintUsingNumberComma);
  1252. }
  1253. xxputc(tbuf[i]);
  1254. }
  1255. }
  1256. }
  1257. else
  1258. {
  1259. WARN_INTERNAL_ERROR;
  1260. return FALSE;
  1261. }
  1262. *position = p;
  1263. return TRUE;
  1264. }
  1265. static int str_prnfmt(char *buffer, int *position, VariantType *e)
  1266. {
  1267. /*
  1268. Format a STRING.
  1269. 'buffer' points to the beginning of a PRINT USING format string, such as "###.##".
  1270. 'position' is the current offset in 'buffer'.
  1271. 'e' is the current expression to print.
  1272. */
  1273. int p;
  1274. char tbuf[BasicStringLengthMax + 1];
  1275. p = *position;
  1276. if( e->TypeChar == BasicStringSuffix )
  1277. {
  1278. bwb_strcpy( tbuf, e->Buffer );
  1279. }
  1280. else
  1281. {
  1282. BasicNumerc( e->Number, tbuf );
  1283. }
  1284. if( IS_CHAR( buffer[ p ], PrintUsingStringFirst ) )
  1285. {
  1286. /* print first character only */
  1287. int i = 0;
  1288. if( tbuf[i] == BasicNulChar )
  1289. {
  1290. xxputc(' ');
  1291. }
  1292. else
  1293. {
  1294. xxputc(tbuf[i]);
  1295. i++;
  1296. }
  1297. p++;
  1298. }
  1299. else
  1300. if( IS_CHAR( buffer[ p ], PrintUsingStringAll ) )
  1301. {
  1302. /* print entire string */
  1303. p++;
  1304. prn_xxprintf(tbuf);
  1305. }
  1306. else
  1307. if( IS_CHAR( buffer[ p ], PrintUsingStringLength ) )
  1308. {
  1309. /* print N characters or spaces */
  1310. int i = 0;
  1311. if( tbuf[i] == BasicNulChar )
  1312. {
  1313. xxputc(' ');
  1314. }
  1315. else
  1316. {
  1317. xxputc(tbuf[i]);
  1318. i++;
  1319. }
  1320. p++;
  1321. while( buffer[p] != BasicNulChar && buffer[p] != PrintUsingStringLength )
  1322. {
  1323. if( tbuf[i] == BasicNulChar )
  1324. {
  1325. xxputc(' ');
  1326. }
  1327. else
  1328. {
  1329. xxputc(tbuf[i]);
  1330. i++;
  1331. }
  1332. p++;
  1333. }
  1334. if( buffer[p] == PrintUsingStringLength )
  1335. {
  1336. if( tbuf[i] == BasicNulChar )
  1337. {
  1338. xxputc(' ');
  1339. }
  1340. else
  1341. {
  1342. xxputc(tbuf[i]);
  1343. i++;
  1344. }
  1345. p++;
  1346. }
  1347. }
  1348. *position = p;
  1349. return TRUE;
  1350. }
  1351. static int is_magic_string( char * buffer )
  1352. {
  1353. /*
  1354. for the character string pointed to 'buffer':
  1355. return TRUE if it is a MagicString sequence,
  1356. return FALSE otherwise.
  1357. */
  1358. char *P;
  1359. /* "!" */
  1360. P = buffer;
  1361. if( IS_CHAR( *P, PrintUsingStringFirst ) )
  1362. {
  1363. return TRUE;
  1364. }
  1365. /* "&" */
  1366. P = buffer;
  1367. if( IS_CHAR( *P, PrintUsingStringAll ) )
  1368. {
  1369. return TRUE;
  1370. }
  1371. /* "%...%" */
  1372. P = buffer;
  1373. if( IS_CHAR( *P, PrintUsingStringLength ) )
  1374. {
  1375. return TRUE;
  1376. }
  1377. return FALSE;
  1378. }
  1379. static int is_magic_number( char * buffer )
  1380. {
  1381. /*
  1382. for the character string pointed to 'buffer':
  1383. return TRUE if it is a MagicNumber sequence,
  1384. return FALSE otherwise.
  1385. */
  1386. char *P;
  1387. /* "+**" */
  1388. P = buffer;
  1389. if( IS_CHAR( *P, PrintUsingNumberPlus ) )
  1390. {
  1391. P++;
  1392. if( IS_CHAR( *P, PrintUsingNumberFiller ) )
  1393. {
  1394. P++;
  1395. if( IS_CHAR( *P, PrintUsingNumberFiller ) )
  1396. {
  1397. /* "+**" */
  1398. return TRUE;
  1399. }
  1400. }
  1401. }
  1402. /* "+$$" */
  1403. P = buffer;
  1404. if( IS_CHAR( *P, PrintUsingNumberPlus ) )
  1405. {
  1406. P++;
  1407. if( IS_CHAR( *P, PrintUsingNumberDollar ) )
  1408. {
  1409. P++;
  1410. if( IS_CHAR( *P, PrintUsingNumberDollar ) )
  1411. {
  1412. /* "+$$" */
  1413. return TRUE;
  1414. }
  1415. }
  1416. }
  1417. /* "+#" */
  1418. P = buffer;
  1419. if( IS_CHAR( *P, PrintUsingNumberPlus ) )
  1420. {
  1421. P++;
  1422. if( IS_CHAR( *P, PrintUsingNumberDigit ) )
  1423. {
  1424. /* "+#" */
  1425. return TRUE;
  1426. }
  1427. }
  1428. /* "-**" */
  1429. P = buffer;
  1430. if( IS_CHAR( *P, PrintUsingNumberMinus ) )
  1431. {
  1432. P++;
  1433. if( IS_CHAR( *P, PrintUsingNumberFiller ) )
  1434. {
  1435. P++;
  1436. if( IS_CHAR( *P, PrintUsingNumberFiller ) )
  1437. {
  1438. /* "-**" */
  1439. return TRUE;
  1440. }
  1441. }
  1442. }
  1443. /* "-$$" */
  1444. P = buffer;
  1445. if( IS_CHAR( *P, PrintUsingNumberMinus ) )
  1446. {
  1447. P++;
  1448. if( IS_CHAR( *P, PrintUsingNumberDollar ) )
  1449. {
  1450. P++;
  1451. if( IS_CHAR( *P, PrintUsingNumberDollar ) )
  1452. {
  1453. /* "-$$" */
  1454. return TRUE;
  1455. }
  1456. }
  1457. }
  1458. /* "-#" */
  1459. P = buffer;
  1460. if( IS_CHAR( *P, PrintUsingNumberMinus ) )
  1461. {
  1462. P++;
  1463. if( IS_CHAR( *P, PrintUsingNumberDigit ) )
  1464. {
  1465. /* "-#" */
  1466. return TRUE;
  1467. }
  1468. }
  1469. /* "**" */
  1470. P = buffer;
  1471. if( IS_CHAR( *P, PrintUsingNumberFiller ) )
  1472. {
  1473. P++;
  1474. if( IS_CHAR( *P, PrintUsingNumberFiller ) )
  1475. {
  1476. /* "**" */
  1477. return TRUE;
  1478. }
  1479. }
  1480. /* "$$" */
  1481. P = buffer;
  1482. if( IS_CHAR( *P, PrintUsingNumberDollar ) )
  1483. {
  1484. P++;
  1485. if( IS_CHAR( *P, PrintUsingNumberDollar ) )
  1486. {
  1487. /* "$$" */
  1488. return TRUE;
  1489. }
  1490. }
  1491. /* "#" */
  1492. P = buffer;
  1493. if( IS_CHAR( *P, PrintUsingNumberDigit ) )
  1494. {
  1495. /* "#" */
  1496. return TRUE;
  1497. }
  1498. return FALSE;
  1499. }
  1500. static int get_prnfmt(char *buffer, int *position, VariantType *e)
  1501. {
  1502. /*
  1503. Format an EXPRESSION.
  1504. 'buffer' points to the beginning of a PRINT USING format string, such as "###.##".
  1505. 'position' is the current offset in 'buffer'.
  1506. 'e' is the current expression to print.
  1507. */
  1508. int p;
  1509. int IsLoop = TRUE;
  1510. int IsUsed = FALSE;
  1511. bwx_DEBUG(__FUNCTION__);
  1512. p = *position;
  1513. if( p < 0 )
  1514. {
  1515. p = 0;
  1516. }
  1517. else
  1518. if( p > 0 )
  1519. {
  1520. if( buffer[p] == BasicNulChar )
  1521. {
  1522. p = 0;
  1523. }
  1524. }
  1525. while( IsLoop == TRUE )
  1526. {
  1527. if( buffer[p] == BasicNulChar )
  1528. {
  1529. IsLoop = FALSE;
  1530. }
  1531. else
  1532. {
  1533. int IsLiteral = TRUE;
  1534. if( IsLiteral == TRUE )
  1535. {
  1536. char * S;
  1537. S = buffer;
  1538. S += p;
  1539. if( is_magic_string( S ) )
  1540. {
  1541. /* MagicString Value */
  1542. if( IsUsed == TRUE )
  1543. {
  1544. IsLoop = FALSE;
  1545. }
  1546. else
  1547. if( e->TypeChar == BasicStringSuffix )
  1548. {
  1549. str_prnfmt( buffer, &p, e );
  1550. IsUsed = TRUE;
  1551. }
  1552. else
  1553. {
  1554. IsLoop = FALSE;
  1555. }
  1556. IsLiteral = FALSE;
  1557. }
  1558. }
  1559. if( IsLiteral == TRUE )
  1560. {
  1561. char * S;
  1562. S = buffer;
  1563. S += p;
  1564. if( is_magic_number( S ) )
  1565. {
  1566. /* MagicNumber Value */
  1567. if( IsUsed == TRUE )
  1568. {
  1569. IsLoop = FALSE;
  1570. }
  1571. else
  1572. if( e->TypeChar == BasicStringSuffix )
  1573. {
  1574. IsLoop = FALSE;
  1575. }
  1576. else
  1577. {
  1578. num_prnfmt( buffer, &p, e );
  1579. IsUsed = TRUE;
  1580. }
  1581. IsLiteral = FALSE;
  1582. }
  1583. }
  1584. if( IsLiteral == TRUE )
  1585. {
  1586. if( PrintUsingLiteral != BasicNulChar && buffer[p] == PrintUsingLiteral )
  1587. {
  1588. /* print next character as literal */
  1589. p++;
  1590. if( buffer[p] == BasicNulChar )
  1591. {
  1592. /* PRINT USING "_" */
  1593. xxputc(' ');
  1594. }
  1595. else
  1596. {
  1597. xxputc(buffer[p]);
  1598. p++;
  1599. }
  1600. }
  1601. else
  1602. {
  1603. xxputc(buffer[p]);
  1604. p++;
  1605. }
  1606. }
  1607. }
  1608. }
  1609. if( IsUsed == FALSE )
  1610. {
  1611. if( e->TypeChar == BasicStringSuffix )
  1612. {
  1613. /* PRINT USING "";A$ */
  1614. /* PRINT USING "ABC";A$ */
  1615. prn_iprintf(e->Buffer);
  1616. }
  1617. else
  1618. {
  1619. /* PRINT USING "";X */
  1620. /* PRINT USING "ABC";X */
  1621. /* [space]number[space] POSITIVE or ZERO
  1622. * [minus]number[space] NEGATIVE */
  1623. char tbuf[ 32 ];
  1624. BasicNumerc(e->Number, tbuf);
  1625. if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) && My->CurrentFile->FileNumber > 0 )
  1626. {
  1627. /* CBASIC-II: numbers in files do NOT have leading or trailing spaces */
  1628. char * P;
  1629. P = tbuf;
  1630. while( *P == ' ' )
  1631. {
  1632. P++;
  1633. }
  1634. prn_iprintf( P );
  1635. }
  1636. else
  1637. {
  1638. prn_iprintf(tbuf);
  1639. xputc(' ');
  1640. }
  1641. }
  1642. }
  1643. *position = p;
  1644. return TRUE;
  1645. }
  1646. /***************************************************************
  1647. FUNCTION: prn_iprintf()
  1648. DESCRIPTION: This function outputs a null-terminated
  1649. string to a specified file or output
  1650. device.
  1651. ***************************************************************/
  1652. int
  1653. prn_iprintf(char *buffer)
  1654. {
  1655. int n;
  1656. bwx_DEBUG(__FUNCTION__);
  1657. if (My->CurrentFile->width == 0)
  1658. {
  1659. /* ignore when WIDTH == 0 -- BINARY output */
  1660. while (*buffer)
  1661. {
  1662. xxxputc(*buffer);
  1663. buffer++;
  1664. }
  1665. return 0;
  1666. }
  1667. /* check to see if width will be exceeded */
  1668. n = My->CurrentFile->col + bwb_strlen(buffer) - 1;
  1669. if (n > My->CurrentFile->width)
  1670. {
  1671. xputc('\n');
  1672. }
  1673. /* output the string */
  1674. while (*buffer)
  1675. {
  1676. xputc(*buffer);
  1677. buffer++;
  1678. }
  1679. return 0;
  1680. }
  1681. /***************************************************************
  1682. FUNCTION: prn_xxprintf()
  1683. DESCRIPTION: This function outputs a null-terminated
  1684. string to a specified file or output
  1685. device without expanding tabs.
  1686. Added by JBV 10/95
  1687. ***************************************************************/
  1688. static int prn_xxprintf(char *buffer)
  1689. {
  1690. int n;
  1691. bwx_DEBUG(__FUNCTION__);
  1692. if (My->CurrentFile->width == 0)
  1693. {
  1694. /* ignore when WIDTH == 0 -- BINARY output */
  1695. while (*buffer)
  1696. {
  1697. xxxputc(*buffer);
  1698. buffer++;
  1699. }
  1700. return 0;
  1701. }
  1702. /* check to see if width will be exceeded */
  1703. n = My->CurrentFile->col + bwb_strlen(buffer) - 1;
  1704. if (n > My->CurrentFile->width)
  1705. {
  1706. xxputc('\n');
  1707. }
  1708. /* output the string */
  1709. while (*buffer)
  1710. {
  1711. xxputc(*buffer);
  1712. buffer++;
  1713. }
  1714. return 0;
  1715. }
  1716. /***************************************************************
  1717. FUNCTION: xputc()
  1718. DESCRIPTION: This function outputs a character to a
  1719. specified file or output device, expanding
  1720. TABbed output approriately.
  1721. ***************************************************************/
  1722. static int xputc(char c)
  1723. {
  1724. static char CHR_pending = FALSE;
  1725. bwx_DEBUG(__FUNCTION__);
  1726. if (My->CurrentFile->width == 0)
  1727. {
  1728. /* ignore when WIDTH == 0 -- BINARY output */
  1729. xxxputc(c);
  1730. return 0;
  1731. }
  1732. /* check for pending SPC */
  1733. if (CHR_pending == PRN_SPC)
  1734. {
  1735. /* 190 PRINT SPC(A);"X" ' A = 0...255 */
  1736. int i;
  1737. for (i = 0; i < c; i++)
  1738. {
  1739. xxputc(' ');
  1740. }
  1741. CHR_pending = FALSE;
  1742. return TRUE;
  1743. }
  1744. /* check for pending TAB */
  1745. if (CHR_pending == PRN_TAB)
  1746. {
  1747. /* WIDTH 80 */
  1748. while (c > My->CurrentFile->width)
  1749. {
  1750. /* If n is greater than the margin m, then n is
  1751. * reduced by an integral multiple of m so that it is
  1752. * in the range 1 <= n <= m; */
  1753. c -= My->CurrentFile->width;
  1754. }
  1755. /* 190 PRINT TAB(A);"X" ' A = 0 */
  1756. if (c == 0)
  1757. {
  1758. /* use the value of one */
  1759. c = 1;
  1760. /* continue processing */
  1761. }
  1762. if ((int) c < My->CurrentFile->col)
  1763. {
  1764. xxputc('\n');
  1765. }
  1766. while (My->CurrentFile->col < (int) c)
  1767. {
  1768. xxputc(' ');
  1769. }
  1770. CHR_pending = FALSE;
  1771. return TRUE;
  1772. }
  1773. /* check for specific output options */
  1774. switch (c)
  1775. {
  1776. case PRN_SPC:
  1777. case PRN_TAB:
  1778. CHR_pending = c;
  1779. break;
  1780. case '\t':
  1781. {
  1782. int LastZoneColumn;
  1783. LastZoneColumn = 1;
  1784. while (LastZoneColumn < My->CurrentFile->width)
  1785. {
  1786. LastZoneColumn += ZONE_WIDTH;
  1787. }
  1788. LastZoneColumn -= ZONE_WIDTH;
  1789. if (My->CurrentFile->col >= LastZoneColumn)
  1790. {
  1791. /* advance to a new line */
  1792. xxputc('\n');
  1793. }
  1794. else
  1795. {
  1796. /* advance to the next print zone */
  1797. if ((My->CurrentFile->col % ZONE_WIDTH) == 1)
  1798. {
  1799. xxputc(' ');
  1800. }
  1801. while ((My->CurrentFile->col % ZONE_WIDTH) != 1)
  1802. {
  1803. xxputc(' ');
  1804. }
  1805. }
  1806. }
  1807. break;
  1808. default:
  1809. xxputc(c);
  1810. break;
  1811. }
  1812. return 0;
  1813. }
  1814. /***************************************************************
  1815. FUNCTION: xxputc()
  1816. DESCRIPTION: This function outputs a character to a
  1817. specified file or output device, checking
  1818. to be sure the PRINT width is within
  1819. the bounds specified for that device.
  1820. ***************************************************************/
  1821. static int xxputc(char c)
  1822. {
  1823. bwx_DEBUG(__FUNCTION__);
  1824. if (My->CurrentFile->width == 0)
  1825. {
  1826. /* ignore when WIDTH == 0 -- BINARY output */
  1827. xxxputc(c);
  1828. return 0;
  1829. }
  1830. /* check to see if width has been exceeded */
  1831. if (c != '\n')
  1832. {
  1833. /* REM this should print one line, not two lines WIDTH 80
  1834. * PRINT SPACE$( 80 ) */
  1835. if (My->CurrentFile->col > My->CurrentFile->width)
  1836. {
  1837. xxxputc('\n'); /* output LF */
  1838. }
  1839. }
  1840. /* output the character */
  1841. xxxputc(c);
  1842. return 0;
  1843. }
  1844. /***************************************************************
  1845. FUNCTION: xxxputc()
  1846. DESCRIPTION: This function sends a character to a
  1847. specified file or output device.
  1848. ***************************************************************/
  1849. static int xxxputc(char c)
  1850. {
  1851. bwx_DEBUG(__FUNCTION__);
  1852. if (My->CurrentFile == My->SYSPRN)
  1853. {
  1854. bwx_LPRINT(c);
  1855. if (c == '\n' && My->SYSPRN->width > 0 && My->LPRINT_NULLS > 0)
  1856. {
  1857. int i;
  1858. for (i = 0; i < My->LPRINT_NULLS; i++)
  1859. {
  1860. bwx_LPRINT(0);
  1861. }
  1862. }
  1863. }
  1864. else
  1865. {
  1866. if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) && c == '\n' )
  1867. {
  1868. if( My->CurrentFile->mode & DEVMODE_RANDOM && My->CurrentFile->width > 0 )
  1869. {
  1870. /* CBASIC-II: RANDOM files are padded on the right with spaces */
  1871. while( My->CurrentFile->col < My->CurrentFile->width )
  1872. {
  1873. fputc(' ', My->CurrentFile->cfp);
  1874. My->CurrentFile->col++;
  1875. }
  1876. }
  1877. }
  1878. fputc(c, My->CurrentFile->cfp);
  1879. }
  1880. /* update current column position */
  1881. if (My->CurrentFile->width == 0)
  1882. {
  1883. /* ignore when WIDTH == 0 -- BINARY output */
  1884. My->CurrentFile->col = 1;
  1885. My->CurrentFile->row = 1;
  1886. }
  1887. else
  1888. if (c == '\n')
  1889. {
  1890. My->CurrentFile->col = 1;
  1891. My->CurrentFile->row ++;
  1892. }
  1893. else
  1894. {
  1895. My->CurrentFile->col ++;
  1896. }
  1897. return 0;
  1898. }
  1899. void
  1900. ResetConsoleColumn( void )
  1901. {
  1902. bwx_DEBUG(__FUNCTION__);
  1903. My->SYSOUT->col = 1;
  1904. }
  1905. LineType *
  1906. bwb_PUT(LineType * l)
  1907. {
  1908. bwx_DEBUG(__FUNCTION__);
  1909. if( My->CurrentVersion->OptionVersionBitmask & ( I70 | I73 ) )
  1910. {
  1911. /* PUT filename$ , value [, ...] */
  1912. VariantType e; /* no leaks */
  1913. VariantType *E = &e; /* no leaks */
  1914. CLEAR_VARIANT( E );
  1915. line_skip_spaces(l);
  1916. if( line_read_expression( l, E ) == FALSE )
  1917. {
  1918. goto EXIT;
  1919. }
  1920. if( E->TypeChar == BasicStringSuffix )
  1921. {
  1922. /* STRING */
  1923. /* PUT filename$ ... */
  1924. if( is_empty_filename( E->Buffer ) )
  1925. {
  1926. /* "PUT # 0" is an error */
  1927. WARN_BAD_FILE_NUMBER;
  1928. goto EXIT;
  1929. }
  1930. My->CurrentFile = find_file_by_name( E->Buffer );
  1931. if( My->CurrentFile == NULL )
  1932. {
  1933. /* implicitly OPEN for writing */
  1934. My->CurrentFile = file_new();
  1935. My->CurrentFile->cfp = fopen(E->Buffer, "w");
  1936. if( My->CurrentFile->cfp == NULL )
  1937. {
  1938. /* bad file name */
  1939. WARN_BAD_FILE_NUMBER;
  1940. goto EXIT;
  1941. }
  1942. My->CurrentFile->FileNumber = file_next_number();
  1943. My->CurrentFile->mode = DEVMODE_OUTPUT;
  1944. My->CurrentFile->width = 0;
  1945. /* WIDTH == RECLEN */
  1946. My->CurrentFile->col = 1;
  1947. My->CurrentFile->row = 1;
  1948. My->CurrentFile->delimit = ',';
  1949. My->CurrentFile->buffer = NULL;
  1950. bwb_strcpy(My->CurrentFile->filename, E->Buffer);
  1951. }
  1952. }
  1953. else
  1954. {
  1955. /* NUMBER -- file must already be OPEN */
  1956. /* PUT filenumber ... */
  1957. if( E->Number < 0 )
  1958. {
  1959. /* "PUT # -1" is an error */
  1960. WARN_BAD_FILE_NUMBER;
  1961. goto EXIT;
  1962. }
  1963. if( E->Number == 0 )
  1964. {
  1965. /* "PUT # 0" is an error */
  1966. WARN_BAD_FILE_NUMBER;
  1967. goto EXIT;
  1968. }
  1969. /* normal file */
  1970. My->CurrentFile = find_file_by_number( (int) bwb_rint( E->Number ) );
  1971. if( My->CurrentFile == NULL )
  1972. {
  1973. /* file not OPEN */
  1974. WARN_BAD_FILE_NUMBER;
  1975. goto EXIT;
  1976. }
  1977. }
  1978. RELEASE( E );
  1979. if( My->CurrentFile == NULL )
  1980. {
  1981. WARN_BAD_FILE_NUMBER;
  1982. goto EXIT;
  1983. }
  1984. if (( My->CurrentFile->mode & DEVMODE_WRITE) == 0)
  1985. {
  1986. WARN_BAD_FILE_NUMBER;
  1987. goto EXIT;
  1988. }
  1989. if( line_is_eol(l) )
  1990. {
  1991. /* PUT F$ */
  1992. /* PUT #1 */
  1993. xputc('\n');
  1994. goto EXIT;
  1995. }
  1996. else
  1997. if (line_skip_comma(l))
  1998. {
  1999. /* OK */
  2000. }
  2001. else
  2002. {
  2003. WARN_SYNTAX_ERROR;
  2004. goto EXIT;
  2005. }
  2006. /* loop through elements */
  2007. while( ! line_is_eol(l) )
  2008. {
  2009. while (line_skip_comma(l))
  2010. {
  2011. /* PUT F$, ,,,A,,,B,,, */
  2012. /* PUT #1, ,,,A,,,B,,, */
  2013. xputc( My->CurrentFile->delimit );
  2014. }
  2015. if ( ! line_is_eol(l) )
  2016. {
  2017. /* print this item */
  2018. /* get the next element */
  2019. line_skip_spaces(l);
  2020. if( line_read_expression( l, E ) == FALSE )
  2021. {
  2022. goto EXIT;
  2023. }
  2024. if( E->TypeChar == BasicStringSuffix )
  2025. {
  2026. /* STRING */
  2027. xputc(BasicQuoteChar);
  2028. prn_iprintf(E->Buffer);
  2029. xputc(BasicQuoteChar);
  2030. }
  2031. else
  2032. {
  2033. /* NUMBER */
  2034. char tbuf[ 32 ];
  2035. BasicNumerc(E->Number, tbuf);
  2036. prn_iprintf(tbuf);
  2037. }
  2038. RELEASE( E );
  2039. }
  2040. }
  2041. /* print LF */
  2042. xputc('\n');
  2043. /* OK */
  2044. EXIT:
  2045. RELEASE( E );
  2046. return bwb_zline(l);
  2047. }
  2048. else
  2049. if( My->CurrentVersion->OptionVersionBitmask & ( D71 ) )
  2050. {
  2051. /* PUT # file_number [ , RECORD record_number ] */
  2052. int file_number = 0;
  2053. if( line_skip_char( l, BasicFileNumberPrefix ) == FALSE )
  2054. {
  2055. /* OPTIONAL */
  2056. }
  2057. if( line_read_integer_expression( l, &file_number ) == FALSE )
  2058. {
  2059. WARN_BAD_FILE_NUMBER;
  2060. return bwb_zline( l );
  2061. }
  2062. if( file_number < 1 )
  2063. {
  2064. WARN_BAD_FILE_NUMBER;
  2065. return bwb_zline( l );
  2066. }
  2067. My->CurrentFile = find_file_by_number( file_number );
  2068. if( My->CurrentFile == NULL )
  2069. {
  2070. WARN_BAD_FILE_NUMBER;
  2071. return bwb_zline( l );
  2072. }
  2073. if( My->CurrentFile->mode != DEVMODE_RANDOM )
  2074. {
  2075. WARN_BAD_FILE_NUMBER;
  2076. return bwb_zline( l );
  2077. }
  2078. if( My->CurrentFile->width <= 0 )
  2079. {
  2080. WARN_BAD_FILE_NUMBER;
  2081. return bwb_zline( l );
  2082. }
  2083. if( line_is_eol( l ) )
  2084. {
  2085. /* PUT # file_number */
  2086. }
  2087. else
  2088. {
  2089. /* PUT # file_number , RECORD record_number */
  2090. int record_number = 0;
  2091. long offset = 0;
  2092. if( line_skip_comma( l ) == FALSE )
  2093. {
  2094. WARN_SYNTAX_ERROR;
  2095. return bwb_zline( l );
  2096. }
  2097. if( line_skip_word( l, "RECORD" ) == FALSE )
  2098. {
  2099. WARN_SYNTAX_ERROR;
  2100. return bwb_zline( l );
  2101. }
  2102. if( line_read_integer_expression( l, &record_number ) == FALSE )
  2103. {
  2104. WARN_BAD_RECORD_NUMBER;
  2105. return bwb_zline( l );
  2106. }
  2107. if( record_number <= 0 )
  2108. {
  2109. WARN_BAD_RECORD_NUMBER;
  2110. return bwb_zline( l );
  2111. }
  2112. record_number--; /* BASIC to C */
  2113. offset = record_number;
  2114. offset *= My->CurrentFile->width;
  2115. if (fseek(My->CurrentFile->cfp, offset, SEEK_SET) != 0)
  2116. {
  2117. WARN_BAD_RECORD_NUMBER;
  2118. return bwb_zline( l );
  2119. }
  2120. }
  2121. field_put( My->CurrentFile );
  2122. /* if( TRUE ) */
  2123. {
  2124. int i;
  2125. for (i = 0; i < My->CurrentFile->width; i++)
  2126. {
  2127. char c;
  2128. c = My->CurrentFile->buffer[i];
  2129. fputc(c, My->CurrentFile->cfp);
  2130. }
  2131. }
  2132. /* OK */
  2133. return bwb_zline( l );
  2134. }
  2135. WARN_SYNTAX_ERROR;
  2136. return bwb_zline(l);
  2137. }
  2138. /***************************************************************
  2139. FUNCTION: bwb_write()
  2140. DESCRIPTION: This C function implements the BASIC WRITE
  2141. command.
  2142. SYNTAX: WRITE [# device-number,] element [, element ]....
  2143. ***************************************************************/
  2144. LineType *
  2145. bwb_WRITE(LineType * l)
  2146. {
  2147. int OutputCR;
  2148. VariantType x; /* no leaks */
  2149. VariantType *X = &x; /* no leaks */
  2150. bwx_DEBUG(__FUNCTION__);
  2151. CLEAR_VARIANT( X );
  2152. My->CurrentFile = My->SYSOUT;
  2153. if ( line_skip_char( l, BasicFileNumberPrefix ) )
  2154. {
  2155. int UserFileNumber;
  2156. if( line_read_integer_expression(l, &UserFileNumber) == FALSE )
  2157. {
  2158. WARN_SYNTAX_ERROR;
  2159. return bwb_zline(l);
  2160. }
  2161. /* check the requested device number */
  2162. if( UserFileNumber < 0 )
  2163. {
  2164. My->CurrentFile = My->SYSPRN;
  2165. }
  2166. else
  2167. if( UserFileNumber == 0 )
  2168. {
  2169. My->CurrentFile = My->SYSOUT;
  2170. }
  2171. else
  2172. {
  2173. /* normal file */
  2174. My->CurrentFile = find_file_by_number( UserFileNumber );
  2175. }
  2176. if( My->CurrentFile == NULL )
  2177. {
  2178. WARN_BAD_FILE_NUMBER;
  2179. return bwb_zline(l);
  2180. }
  2181. if (( My->CurrentFile->mode & DEVMODE_WRITE) == 0)
  2182. {
  2183. WARN_BAD_FILE_NUMBER;
  2184. return bwb_zline(l);
  2185. }
  2186. if( line_is_eol(l) )
  2187. {
  2188. /* WRITE #1 */
  2189. xputc('\n');
  2190. return bwb_zline(l);
  2191. }
  2192. else
  2193. if (line_skip_comma(l))
  2194. {
  2195. /* OK */
  2196. }
  2197. else
  2198. {
  2199. WARN_SYNTAX_ERROR;
  2200. return bwb_zline(l);
  2201. }
  2202. }
  2203. /* loop through elements */
  2204. OutputCR = TRUE;
  2205. line_skip_spaces( l );
  2206. while( line_is_eol(l) == FALSE )
  2207. {
  2208. if( line_skip_comma(l) )
  2209. {
  2210. /* WRITE ,,,A,,,B,,, */
  2211. /* WRITE #1, ,,,A,,,B,,, */
  2212. OutputCR = FALSE;
  2213. xputc( My->CurrentFile->delimit );
  2214. }
  2215. else
  2216. {
  2217. /* print the expression */
  2218. OutputCR = TRUE;
  2219. if( line_read_expression( l, X ) == FALSE )
  2220. {
  2221. goto EXIT;
  2222. }
  2223. if( bwb_Warning_Pending() /* Keep This */ )
  2224. {
  2225. /*
  2226. this might look odd...
  2227. but we want to abort printing on the first error.
  2228. The expression list could include a function with side-effects,
  2229. so any kind of error should immediately halt further evaluation.
  2230. */
  2231. goto EXIT;
  2232. }
  2233. if( X->TypeChar == BasicStringSuffix )
  2234. {
  2235. /* STRING */
  2236. xputc(BasicQuoteChar);
  2237. prn_iprintf(X->Buffer);
  2238. xputc(BasicQuoteChar);
  2239. }
  2240. else
  2241. {
  2242. /* NUMBER */
  2243. char tbuf[ 32 ];
  2244. BasicNumerc(X->Number, tbuf);
  2245. prn_iprintf(tbuf);
  2246. }
  2247. RELEASE( X );
  2248. }
  2249. line_skip_spaces( l );
  2250. }
  2251. /* print LF */
  2252. if( OutputCR == TRUE )
  2253. {
  2254. xputc('\n');
  2255. }
  2256. EXIT:
  2257. RELEASE( X );
  2258. return bwb_zline(l);
  2259. }
  2260. static LineType * file_write_matrix( LineType * l, char delimit )
  2261. {
  2262. /* MAT PRINT arrayname [;|,] */
  2263. /* Array must be 1, 2 or 3 dimensions */
  2264. /* Array may be either NUMBER or STRING */
  2265. VariableType *v;
  2266. char ItemSeperator[2];
  2267. bwx_DEBUG(__FUNCTION__);
  2268. /* get the variable name */
  2269. line_skip_spaces(l);
  2270. while( bwb_isalpha( l->buffer[l->position] ) )
  2271. {
  2272. /* get matrix name */
  2273. if((v = line_read_matrix( l )) == NULL)
  2274. {
  2275. WARN_SUBSCRIPT_OUT_OF_RANGE;
  2276. return bwb_zline(l);
  2277. }
  2278. /* variable MUST be an array of 1, 2 or 3 dimensions */
  2279. if (v->dimensions < 1)
  2280. {
  2281. WARN_SUBSCRIPT_OUT_OF_RANGE;
  2282. return bwb_zline(l);
  2283. }
  2284. if(v->dimensions > 3)
  2285. {
  2286. WARN_SUBSCRIPT_OUT_OF_RANGE;
  2287. return bwb_zline(l);
  2288. }
  2289. /*
  2290. This may look odd, but MAT PRINT is special.
  2291. The variable seperator AFTER the variable determines how
  2292. the variable's values are printed.
  2293. The number of dimension determines:
  2294. a) the meaning of comma (,) and semicolon (;)
  2295. b) the default of row-by-row or col-by-col
  2296. */
  2297. /* default the item seperator based upon variable's dimensions */
  2298. ItemSeperator[0] = BasicNulChar;
  2299. ItemSeperator[1] = BasicNulChar;
  2300. switch( v->dimensions )
  2301. {
  2302. case 1:
  2303. /* by default, a one dimension array is printed row-by-row */
  2304. ItemSeperator[0] = '\n';
  2305. break;
  2306. case 2:
  2307. /* by default, a two dimension array is printed col-by-col */
  2308. ItemSeperator[0] = delimit;
  2309. break;
  2310. case 3:
  2311. /* by default, a three dimension array is printed col-by-col */
  2312. ItemSeperator[0] = delimit;
  2313. break;
  2314. }
  2315. /* allow user to assign the item seperator */
  2316. if( line_skip_char(l, ',' /* comma-specific */ ))
  2317. {
  2318. /* force printing col-by-col */
  2319. ItemSeperator[0] = delimit;
  2320. }
  2321. else
  2322. if( line_skip_char(l, ';' /* semicolon-specific */ ))
  2323. {
  2324. /* force concatenating the columns */
  2325. ItemSeperator[0] = BasicNulChar;
  2326. }
  2327. /* print array */
  2328. switch( v->dimensions )
  2329. {
  2330. case 1:
  2331. {
  2332. /*
  2333. OPTION BASE 0
  2334. DIM A(5)
  2335. ...
  2336. MAT PRINT A
  2337. ...
  2338. FOR I = 0 TO 5
  2339. PRINT A(I)
  2340. NEXT I
  2341. ...
  2342. */
  2343. for( v->array_pos[0] = v->LBOUND[0]; v->array_pos[0] <= v->UBOUND[0]; v->array_pos[0]++ )
  2344. {
  2345. char tbuf[BasicStringLengthMax + 1];
  2346. if( ItemSeperator[0] != BasicNulChar && v->array_pos[0] > v->LBOUND[0] )
  2347. {
  2348. prn_iprintf(ItemSeperator);
  2349. }
  2350. /* if( TRUE ) */
  2351. {
  2352. VariantType variant;
  2353. if( var_get( v, &variant) == FALSE )
  2354. {
  2355. WARN_VARIABLE_NOT_DECLARED;
  2356. return bwb_zline(l);
  2357. }
  2358. if( variant.TypeChar == '$' )
  2359. {
  2360. bwb_strcpy( tbuf, variant.Buffer );
  2361. }
  2362. else
  2363. {
  2364. BasicNumerc( variant.Number, tbuf );
  2365. }
  2366. }
  2367. prn_iprintf(tbuf);
  2368. }
  2369. prn_iprintf("\n");
  2370. }
  2371. break;
  2372. case 2:
  2373. {
  2374. /*
  2375. OPTION BASE 0
  2376. DIM B(2,3)
  2377. ...
  2378. MAT PRINT B
  2379. ...
  2380. FOR I = 0 TO 2
  2381. FOR J = 0 TO 3
  2382. PRINT B(I,J),
  2383. NEXT J
  2384. PRINT
  2385. NEXT I
  2386. ...
  2387. */
  2388. for( v->array_pos[0] = v->LBOUND[0]; v->array_pos[0] <= v->UBOUND[0]; v->array_pos[0]++ )
  2389. {
  2390. for( v->array_pos[1] = v->LBOUND[1]; v->array_pos[1] <= v->UBOUND[1]; v->array_pos[1]++ )
  2391. {
  2392. char tbuf[BasicStringLengthMax + 1];
  2393. if( ItemSeperator[0] != BasicNulChar && v->array_pos[1] > v->LBOUND[1] )
  2394. {
  2395. prn_iprintf(ItemSeperator);
  2396. }
  2397. /* if( TRUE ) */
  2398. {
  2399. VariantType variant;
  2400. if( var_get( v, &variant) == FALSE )
  2401. {
  2402. WARN_VARIABLE_NOT_DECLARED;
  2403. return bwb_zline(l);
  2404. }
  2405. if( variant.TypeChar == '$' )
  2406. {
  2407. bwb_strcpy( tbuf, variant.Buffer );
  2408. }
  2409. else
  2410. {
  2411. BasicNumerc( variant.Number, tbuf );
  2412. }
  2413. }
  2414. prn_iprintf(tbuf);
  2415. }
  2416. prn_iprintf("\n");
  2417. }
  2418. }
  2419. break;
  2420. case 3:
  2421. {
  2422. /*
  2423. OPTION BASE 0
  2424. DIM C(2,3,4)
  2425. ...
  2426. MAT PRINT C
  2427. ...
  2428. FOR I = 0 TO 2
  2429. FOR J = 0 TO 3
  2430. FOR K = 0 TO 4
  2431. PRINT C(I,J,K),
  2432. NEXT K
  2433. PRINT
  2434. NEXT J
  2435. PRINT
  2436. NEXT I
  2437. ...
  2438. */
  2439. for( v->array_pos[0] = v->LBOUND[0]; v->array_pos[0] <= v->UBOUND[0]; v->array_pos[0]++ )
  2440. {
  2441. for( v->array_pos[1] = v->LBOUND[1]; v->array_pos[1] <= v->UBOUND[1]; v->array_pos[1]++ )
  2442. {
  2443. for( v->array_pos[2] = v->LBOUND[2]; v->array_pos[2] <= v->UBOUND[2]; v->array_pos[2]++ )
  2444. {
  2445. char tbuf[BasicStringLengthMax + 1];
  2446. if( ItemSeperator[0] != BasicNulChar && v->array_pos[2] > v->LBOUND[2] )
  2447. {
  2448. prn_iprintf(ItemSeperator);
  2449. }
  2450. /* if( TRUE ) */
  2451. {
  2452. VariantType variant;
  2453. if( var_get( v, &variant) == FALSE )
  2454. {
  2455. WARN_VARIABLE_NOT_DECLARED;
  2456. return bwb_zline(l);
  2457. }
  2458. if( variant.TypeChar == '$' )
  2459. {
  2460. bwb_strcpy( tbuf, variant.Buffer );
  2461. }
  2462. else
  2463. {
  2464. BasicNumerc( variant.Number, tbuf );
  2465. }
  2466. }
  2467. prn_iprintf(tbuf);
  2468. }
  2469. prn_iprintf("\n");
  2470. }
  2471. prn_iprintf("\n");
  2472. }
  2473. }
  2474. break;
  2475. }
  2476. /* skip spaces */
  2477. line_skip_spaces(l);
  2478. /* process the next variable, if any */
  2479. }
  2480. return bwb_zline(l);
  2481. }
  2482. static LineType * bwb_mat_dump(LineType * l, int IsWrite )
  2483. {
  2484. /* MAT PRINT arrayname [;|,] */
  2485. /* Array must be 1, 2 or 3 dimensions */
  2486. /* Array may be either NUMBER or STRING */
  2487. char delimit;
  2488. bwx_DEBUG(__FUNCTION__);
  2489. My->CurrentFile = My->SYSOUT;
  2490. if ( line_skip_char( l, BasicFileNumberPrefix ) )
  2491. {
  2492. int UserFileNumber;
  2493. if( line_read_integer_expression(l, &UserFileNumber) == FALSE )
  2494. {
  2495. WARN_SYNTAX_ERROR;
  2496. return bwb_zline(l);
  2497. }
  2498. /* check the requested device number */
  2499. if( UserFileNumber < 0 )
  2500. {
  2501. My->CurrentFile = My->SYSPRN;
  2502. }
  2503. else
  2504. if( UserFileNumber == 0 )
  2505. {
  2506. My->CurrentFile = My->SYSOUT;
  2507. }
  2508. else
  2509. {
  2510. /* normal file */
  2511. My->CurrentFile = find_file_by_number( UserFileNumber );
  2512. }
  2513. if( My->CurrentFile == NULL )
  2514. {
  2515. WARN_BAD_FILE_NUMBER;
  2516. return bwb_zline(l);
  2517. }
  2518. if ((My->CurrentFile->mode & DEVMODE_WRITE) == 0)
  2519. {
  2520. WARN_BAD_FILE_NUMBER;
  2521. return bwb_zline(l);
  2522. }
  2523. if (line_skip_comma(l))
  2524. {
  2525. /* OK */
  2526. }
  2527. else
  2528. {
  2529. WARN_SYNTAX_ERROR;
  2530. return bwb_zline(l);
  2531. }
  2532. }
  2533. if( IsWrite )
  2534. {
  2535. /* MAT WRITE */
  2536. delimit = My->CurrentFile->delimit;
  2537. }
  2538. else
  2539. {
  2540. /* MAT PRINT */
  2541. delimit = '\t';
  2542. }
  2543. return file_write_matrix( l, delimit );
  2544. }
  2545. LineType *
  2546. bwb_MAT_PUT(LineType * l)
  2547. {
  2548. /* MAT PUT filename$ , matrix [, ...] */
  2549. VariantType x; /* no leaks */
  2550. VariantType *X = &x; /* no leaks */
  2551. bwx_DEBUG(__FUNCTION__);
  2552. CLEAR_VARIANT( X );
  2553. My->CurrentFile = My->SYSOUT;
  2554. line_skip_spaces(l);
  2555. if( line_read_expression( l, X ) == FALSE )
  2556. {
  2557. goto EXIT;
  2558. }
  2559. if( X->TypeChar == BasicStringSuffix )
  2560. {
  2561. /* STRING */
  2562. /* MAT PUT filename$ ... */
  2563. if( is_empty_filename( X->Buffer ) )
  2564. {
  2565. /* "MAT PUT # 0" is an error */
  2566. WARN_BAD_FILE_NUMBER;
  2567. goto EXIT;
  2568. }
  2569. My->CurrentFile = find_file_by_name( X->Buffer );
  2570. if( My->CurrentFile == NULL )
  2571. {
  2572. /* implicitly OPEN for writing */
  2573. My->CurrentFile = file_new();
  2574. My->CurrentFile->cfp = fopen( X->Buffer, "w" );
  2575. if( My->CurrentFile->cfp == NULL )
  2576. {
  2577. /* bad file name */
  2578. WARN_BAD_FILE_NUMBER;
  2579. goto EXIT;
  2580. }
  2581. My->CurrentFile->FileNumber = file_next_number();
  2582. My->CurrentFile->mode = DEVMODE_OUTPUT;
  2583. My->CurrentFile->width = 0;
  2584. /* WIDTH == RECLEN */
  2585. My->CurrentFile->col = 1;
  2586. My->CurrentFile->row = 1;
  2587. My->CurrentFile->delimit = ',';
  2588. My->CurrentFile->buffer = NULL;
  2589. bwb_strcpy( My->CurrentFile->filename, X->Buffer );
  2590. }
  2591. }
  2592. else
  2593. {
  2594. /* NUMBER -- file must already be OPEN */
  2595. /* PUT filenumber ... */
  2596. if( X->Number < 0 )
  2597. {
  2598. /* "MAT PUT # -1" is an error */
  2599. WARN_BAD_FILE_NUMBER;
  2600. goto EXIT;
  2601. }
  2602. if( X->Number == 0 )
  2603. {
  2604. /* "MAT PUT # 0" is an error */
  2605. WARN_BAD_FILE_NUMBER;
  2606. goto EXIT;
  2607. }
  2608. /* normal file */
  2609. My->CurrentFile = find_file_by_number( (int) bwb_rint( X->Number ) );
  2610. if( My->CurrentFile == NULL )
  2611. {
  2612. /* file not OPEN */
  2613. WARN_BAD_FILE_NUMBER;
  2614. goto EXIT;
  2615. }
  2616. }
  2617. RELEASE( X );
  2618. if( My->CurrentFile == NULL )
  2619. {
  2620. WARN_BAD_FILE_NUMBER;
  2621. goto EXIT;
  2622. }
  2623. if (( My->CurrentFile->mode & DEVMODE_WRITE) == 0)
  2624. {
  2625. WARN_BAD_FILE_NUMBER;
  2626. goto EXIT;
  2627. }
  2628. if ( line_skip_comma(l) )
  2629. {
  2630. /* OK */
  2631. }
  2632. else
  2633. {
  2634. WARN_SYNTAX_ERROR;
  2635. goto EXIT;
  2636. }
  2637. return file_write_matrix( l, My->CurrentFile->delimit );
  2638. EXIT:
  2639. RELEASE( X );
  2640. return bwb_zline(l);
  2641. }
  2642. LineType *
  2643. bwb_MAT_WRITE(LineType * l)
  2644. {
  2645. return bwb_mat_dump( l, TRUE );
  2646. }
  2647. LineType *
  2648. bwb_MAT_PRINT(LineType * l)
  2649. {
  2650. return bwb_mat_dump( l, FALSE );
  2651. }
  2652. /* EOF */