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.
 
 
 
 
 
 

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