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.
 
 
 
 
 
 

874 lines
21 KiB

  1. /***************************************************************
  2. bwb_str.c String-Management Routines
  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. /***************************************************************
  30. WARNING - WARNING - WARNING - WARNING - WARNING - WARNING
  31. BASIC allows embedded NUL (0) characters. C str*() does not.
  32. ALL the StringType code should use mem*() and ->length, but some does not.
  33. ALL the StringType code should prevent string overflow, but some does not.
  34. ***************************************************************/
  35. #include "bwbasic.h"
  36. /***************************************************************
  37. FUNCTION: str_btob()
  38. DESCRIPTION: This C function assigns a bwBASIC string
  39. structure to another bwBASIC string
  40. structure.
  41. ***************************************************************/
  42. int
  43. str_btob(StringType * d, StringType * s)
  44. {
  45. bwx_DEBUG(__FUNCTION__);
  46. /* get memory for new buffer */
  47. if ( d->sbuffer == NULL )
  48. {
  49. if ((d->sbuffer = (char *) CALLOC(BasicStringLengthMax + 1, 1, "str_btob")) == NULL)
  50. {
  51. WARN_OUT_OF_MEMORY;
  52. return FALSE;
  53. }
  54. }
  55. /* write the b string to the b string */
  56. if (s->length > BasicStringLengthMax)
  57. {
  58. WARN_STRING_TOO_LONG;
  59. s->length = BasicStringLengthMax;
  60. }
  61. d->length = s->length;
  62. bwb_memcpy(d->sbuffer, s->sbuffer, d->length);
  63. d->sbuffer[d->length] = BasicNulChar;
  64. return TRUE;
  65. }
  66. /***************************************************************
  67. FUNCTION: str_ctob()
  68. DESCRIPTION: This C function assigns a null-terminated
  69. C string to a bwBASIC string structure.
  70. ***************************************************************/
  71. int
  72. str_ctob(StringType * s, char *buffer)
  73. {
  74. bwx_DEBUG(__FUNCTION__);
  75. /* get memory for new buffer */
  76. if ( s->sbuffer == NULL )
  77. {
  78. if ((s->sbuffer = (char *) CALLOC(BasicStringLengthMax + 1, 1, "str_ctob")) == NULL)
  79. {
  80. WARN_OUT_OF_MEMORY;
  81. return FALSE;
  82. }
  83. }
  84. /* write the c string to the b string */
  85. s->length = bwb_strlen(buffer);
  86. if (s->length > BasicStringLengthMax)
  87. {
  88. WARN_STRING_TOO_LONG;
  89. s->length = BasicStringLengthMax;
  90. }
  91. bwb_memcpy(s->sbuffer, buffer, s->length);
  92. s->sbuffer[s->length] = BasicNulChar;
  93. return TRUE;
  94. }
  95. /***************************************************************
  96. FUNCTION: str_btoc()
  97. DESCRIPTION: This C function assigns a null-terminated
  98. C string to a bwBASIC string structure.
  99. ***************************************************************/
  100. int
  101. str_btoc(char *buffer, StringType * s)
  102. {
  103. bwx_DEBUG(__FUNCTION__);
  104. /* write the b string to the c string */
  105. if (s->length > BasicStringLengthMax)
  106. {
  107. WARN_STRING_TOO_LONG;
  108. s->length = BasicStringLengthMax;
  109. }
  110. bwb_memcpy(buffer, s->sbuffer, s->length);
  111. buffer[s->length] = BasicNulChar;
  112. return TRUE;
  113. }
  114. /***************************************************************
  115. FUNCTION: str_cat()
  116. DESCRIPTION: This C function performs the equivalent
  117. of the C strcat() function, using BASIC
  118. strings.
  119. ***************************************************************/
  120. StringType *
  121. str_cat(StringType * a, StringType * b)
  122. {
  123. int i;
  124. bwx_DEBUG(__FUNCTION__);
  125. if (a->length > BasicStringLengthMax)
  126. {
  127. WARN_STRING_TOO_LONG;
  128. a->length = BasicStringLengthMax;
  129. }
  130. if (b->length > BasicStringLengthMax)
  131. {
  132. WARN_STRING_TOO_LONG;
  133. b->length = BasicStringLengthMax;
  134. }
  135. i = b->length;
  136. if (a->length + i > BasicStringLengthMax)
  137. {
  138. WARN_STRING_TOO_LONG;
  139. i = BasicStringLengthMax - a->length;
  140. }
  141. if (i > 0)
  142. {
  143. char *t;
  144. t = a->sbuffer;
  145. t += a->length;
  146. bwb_memcpy(t, b->sbuffer, i);
  147. a->length += i;
  148. a->sbuffer[a->length] = BasicNulChar;
  149. }
  150. return a;
  151. }
  152. /***************************************************************
  153. FUNCTION: str_cmp()
  154. DESCRIPTION: This C function performs the equivalent
  155. of the C strcmp() function, using BASIC
  156. strings.
  157. ***************************************************************/
  158. int
  159. str_cmp(StringType * a, StringType * b)
  160. {
  161. bwx_DEBUG(__FUNCTION__);
  162. if (a->length > BasicStringLengthMax)
  163. {
  164. WARN_STRING_TOO_LONG;
  165. a->length = BasicStringLengthMax;
  166. }
  167. if (b->length > BasicStringLengthMax)
  168. {
  169. WARN_STRING_TOO_LONG;
  170. b->length = BasicStringLengthMax;
  171. }
  172. if (a->sbuffer == NULL)
  173. {
  174. if (b->sbuffer == NULL)
  175. {
  176. return 0;
  177. }
  178. if (b->length == 0)
  179. {
  180. return 0;
  181. }
  182. return 1;
  183. }
  184. else
  185. {
  186. a->sbuffer[a->length] = BasicNulChar;
  187. }
  188. if (b->sbuffer == NULL)
  189. {
  190. if (a->sbuffer == NULL)
  191. {
  192. return 0;
  193. }
  194. if (a->length == 0)
  195. {
  196. return 0;
  197. }
  198. return -1;
  199. }
  200. else
  201. {
  202. b->sbuffer[b->length] = BasicNulChar;
  203. }
  204. if (My->CurrentVersion->OptionFlags & OPTION_COMPARE_TEXT)
  205. {
  206. /* case insensitive */
  207. return bwb_stricmp(a->sbuffer, b->sbuffer); /* NOTE: embedded NUL characters terminate comparison */
  208. }
  209. else
  210. {
  211. /* case sensitive */
  212. return bwb_strcmp(a->sbuffer, b->sbuffer); /* NOTE: embedded NUL characters terminate comparison */
  213. }
  214. }
  215. /***************************************************************
  216. MATCH
  217. ***************************************************************/
  218. int str_match( char * A, int A_Length, char *B, int B_Length, int I_Start )
  219. {
  220. /*
  221. SYNTAX: j% = MATCH( a$, b$, i% )
  222. MATCH returns the position of the first occurrence of a$ in b$
  223. starting with the character position given by the third parameter.
  224. A zero is returned if no MATCH is found.
  225. The following pattern-matching features are available:
  226. # matches any digit (0-9).
  227. ! matches any upper-or lower-case letter.
  228. ? matches any character.
  229. \ serves as an escape character indicating the following character does not have special meaning.
  230. For example, a ? signifies any character is a MATCH unless preceded by a \.
  231. a$ and b$ must be strings.
  232. If either of these arguments are numeric, an error occurs.
  233. If i% is real, it is converted to an integer.
  234. If i% is a string, an error occurs.
  235. If i% is negative or zero, a run-time error occurs.
  236. When i% is greater than the length of b$, zero is returned.
  237. If b$ is a null string, a 0 is returned.
  238. If b$ is not null, but a$ is null, a 1 is returned.
  239. Examples:
  240. MATCH( "is", "Now is the", 1) returns 5
  241. MATCH( "##", "October 8, 1876", 1) returns 12
  242. MATCH( "a?", "character", 4 ) returns 5
  243. MATCH( "\#", "123#45", 1) returns 4
  244. MATCH( "ABCD", "ABC", 1 ) returns 0
  245. MATCH( "\#1\\\?", "1#1\?2#", 1 ) returns 2
  246. */
  247. int a; /* current position in A$ */
  248. int b; /* current position in B$ */
  249. if( I_Start <= 0 )
  250. {
  251. return 0;
  252. }
  253. if( I_Start > B_Length )
  254. {
  255. return 0;
  256. }
  257. if( B_Length <= 0 )
  258. {
  259. return 0;
  260. }
  261. if( A_Length <= 0 )
  262. {
  263. return 1;
  264. }
  265. I_Start--; /* BASIC to C */
  266. for( b = I_Start; b < B_Length; b++ )
  267. {
  268. int n; /* number of characters in A$ matched with B$ */
  269. n = 0;
  270. for( a = 0; a < A_Length; a++ )
  271. {
  272. int bn;
  273. bn = b + n;
  274. if( A[ a ] == '#' && bwb_isdigit( B[ bn ] ) )
  275. {
  276. n++;
  277. }
  278. else
  279. if( A[ a ] == '!' && bwb_isalpha( B[ bn ] ) )
  280. {
  281. n++;
  282. }
  283. else
  284. if( A[ a ] == '?' )
  285. {
  286. n++;
  287. }
  288. else
  289. if( a < (A_Length - 1) && A[ a ] == '\\' && A[ a + 1 ] == B[ bn ] )
  290. {
  291. n++;
  292. a++;
  293. }
  294. else
  295. if( A[ a ] == B[ bn ] )
  296. {
  297. n++;
  298. }
  299. else
  300. {
  301. break;
  302. }
  303. }
  304. if( a == A_Length )
  305. {
  306. b++; /* C to BASIC */
  307. return b;
  308. }
  309. }
  310. return 0;
  311. }
  312. /***************************************************************
  313. FUNCTION: str_like()
  314. DESCRIPTION: This C function performs the equivalent
  315. of the BASIC LIKE operator, using BASIC
  316. strings.
  317. ***************************************************************/
  318. /*
  319. inspired by http://www.blackbeltcoder.com/Articles/net/implementing-vbs-like-operator-in-c
  320. */
  321. /*
  322. KNOWN ISSUES:
  323. To match the character '[', use "[[]".
  324. To match the character '?', use "[?]".
  325. To match the character '*', use "[*]".
  326. Does not match "" with "[]" or "[!]".
  327. */
  328. #define CHAR_SET '*'
  329. #define CHAR_CLR ' '
  330. static char charList[ 256 ];
  331. static int IndexOf( char * buffer, char find, int start )
  332. {
  333. int buffer_count;
  334. int buffer_length;
  335. buffer_length = bwb_strlen( buffer );
  336. if( My->CurrentVersion->OptionFlags & OPTION_COMPARE_TEXT )
  337. {
  338. find = bwb_toupper( find );
  339. }
  340. for( buffer_count = start; buffer_count < buffer_length; buffer_count++ )
  341. {
  342. char theChar;
  343. theChar = buffer[buffer_count];
  344. if( My->CurrentVersion->OptionFlags & OPTION_COMPARE_TEXT )
  345. {
  346. theChar = bwb_toupper( theChar );
  347. }
  348. if( theChar == find )
  349. {
  350. /* FOUND */
  351. return buffer_count;
  352. }
  353. }
  354. /* NOT FOUND */
  355. return -1;
  356. }
  357. static int CharListToSet(char * pattern, int start, int stop)
  358. {
  359. /*
  360. Converts a string of characters to a HashSet of characters. If the string
  361. contains character ranges, such as A-Z, all characters in the range are
  362. also added to the returned set of characters.
  363. */
  364. int pattern_Count;
  365. bwb_memset( charList, CHAR_CLR, 256 );
  366. if( start > stop )
  367. {
  368. /* ERROR */
  369. return FALSE;
  370. }
  371. /* Leading '-' */
  372. while( pattern[start] == '-' )
  373. {
  374. /* Match character '-' */
  375. charList[ 0x00FF & pattern[start] ] = CHAR_SET;
  376. start++;
  377. if( start > stop )
  378. {
  379. /* DONE */
  380. return TRUE;
  381. }
  382. }
  383. /* Trailing '-' */
  384. while( pattern[stop] == '-' )
  385. {
  386. /* Match character '-' */
  387. charList[ 0x00FF & pattern[stop] ] = CHAR_SET;
  388. stop--;
  389. if( start > stop )
  390. {
  391. /* DONE */
  392. return TRUE;
  393. }
  394. }
  395. for (pattern_Count = start; pattern_Count <= stop; pattern_Count++)
  396. {
  397. if( pattern[pattern_Count] == '-' )
  398. {
  399. /* Character range */
  400. char startChar;
  401. char endChar;
  402. char theChar;
  403. if( pattern_Count > start )
  404. {
  405. startChar = pattern[pattern_Count-1];
  406. if( startChar == '-' )
  407. {
  408. /* ERROR */
  409. return FALSE;
  410. }
  411. if( My->CurrentVersion->OptionFlags & OPTION_COMPARE_TEXT )
  412. {
  413. startChar = bwb_toupper( startChar );
  414. }
  415. }
  416. else
  417. {
  418. /* ERROR */
  419. return FALSE;
  420. }
  421. if( pattern_Count < stop )
  422. {
  423. endChar = pattern[pattern_Count+1];
  424. if( endChar == '-' )
  425. {
  426. /* ERROR */
  427. return FALSE;
  428. }
  429. if( My->CurrentVersion->OptionFlags & OPTION_COMPARE_TEXT )
  430. {
  431. endChar = bwb_toupper( endChar );
  432. }
  433. if( endChar < startChar )
  434. {
  435. /* ERROR */
  436. return FALSE;
  437. }
  438. }
  439. else
  440. {
  441. /* ERROR */
  442. return FALSE;
  443. }
  444. /*
  445. Although the startChar has already been set,
  446. and the endChar will be set on the next loop,
  447. we go ahead and set them here too.
  448. Not the most efficient, but easy to understand,
  449. and we do not have to do anything special
  450. for edge cases such as [A-A] and [A-B].
  451. */
  452. for (theChar = startChar; theChar <= endChar; theChar++)
  453. {
  454. charList[ 0x00FF & theChar ] = CHAR_SET;
  455. }
  456. }
  457. else
  458. {
  459. /* Single character */
  460. char theChar;
  461. theChar = pattern[pattern_Count];
  462. if( My->CurrentVersion->OptionFlags & OPTION_COMPARE_TEXT )
  463. {
  464. theChar = bwb_toupper( theChar );
  465. }
  466. charList[ 0x00FF & theChar ] = CHAR_SET;
  467. }
  468. }
  469. return TRUE;
  470. }
  471. int IsLike(char * buffer , int * buffer_count , int buffer_Length,
  472. char * pattern, int * pattern_count, int pattern_Length)
  473. {
  474. /* Implement's VB's Like operator logic. */
  475. /*
  476. if matched then
  477. buffer_count is updated
  478. pattern_count is updated
  479. returns TRUE
  480. else
  481. returns FALSE
  482. end if
  483. */
  484. int bc;
  485. int pc;
  486. bc = *buffer_count;
  487. pc = *pattern_count;
  488. /* Loop through pattern string */
  489. while ( pc < pattern_Length )
  490. {
  491. /* Get next pattern character */
  492. if ( pattern[pc] == '[' )
  493. {
  494. /* Character list */
  495. /* [] and [!] are special */
  496. char IsExclude = CHAR_CLR;
  497. pc++;
  498. /* pc is first character after '[' */
  499. if( pattern[pc] == '!' )
  500. {
  501. pc++;
  502. IsExclude = CHAR_SET;
  503. }
  504. /* pc is first character after '[' */
  505. if( pattern[pc] == ']' )
  506. {
  507. /* [] and [!] are special */
  508. /* pc is first character after '[' and is a ']' */
  509. pc++;
  510. if( IsExclude == CHAR_CLR )
  511. {
  512. /* [] */
  513. /* matches "" */
  514. }
  515. else
  516. {
  517. /* [!] */
  518. /* same as '?' */
  519. if (bc >= buffer_Length)
  520. {
  521. /* we have completed the buffer without completing the pattern */
  522. return FALSE;
  523. }
  524. bc++;
  525. }
  526. }
  527. else
  528. {
  529. /* Build character list */
  530. /* pc is first character after '[' and is not a ']' */
  531. int stop_count = IndexOf(pattern, ']', pc);
  532. /* stop_count is the character ']' */
  533. if (stop_count < 0)
  534. {
  535. /* NOT FOUND */
  536. return FALSE;
  537. }
  538. /* pc is first character after '[' */
  539. /* stop_count is the character ']' */
  540. CharListToSet(pattern, pc, stop_count - 1);
  541. pc = stop_count + 1;
  542. /* pc is first character after ']' */
  543. if (bc >= buffer_Length)
  544. {
  545. /* we have completed the buffer without completing the pattern */
  546. return FALSE;
  547. }
  548. if (charList[ 0x00FF & buffer[bc] ] == IsExclude)
  549. {
  550. /* not matched */
  551. return FALSE;
  552. }
  553. bc++;
  554. }
  555. }
  556. else
  557. if (pattern[pc] == '?' /* LIKE char */ )
  558. {
  559. /* Matches a single character */
  560. pc++;
  561. if (bc >= buffer_Length)
  562. {
  563. /* Check for end of string */
  564. /* we have completed the buffer without completing the pattern */
  565. return FALSE;
  566. }
  567. bc++;
  568. }
  569. else
  570. if (pattern[pc] == '#' /* LIKE digit */ )
  571. {
  572. /* Matches a single digit */
  573. pc++;
  574. if (bc >= buffer_Length)
  575. {
  576. /* Check for end of string */
  577. /* we have completed the buffer without completing the pattern */
  578. return FALSE;
  579. }
  580. if (bwb_isdigit(buffer[bc]))
  581. {
  582. bc++;
  583. }
  584. else
  585. {
  586. /* not matched */
  587. return FALSE;
  588. }
  589. }
  590. else
  591. if (pattern[pc] == '*' /* LIKE chars */ )
  592. {
  593. /* Zero or more characters */
  594. while(pattern[pc] == '*' /* LIKE chars */ )
  595. {
  596. pc++;
  597. }
  598. if (pc == pattern_Length)
  599. {
  600. /* Matches all remaining characters */
  601. bc = buffer_Length;
  602. pc = pattern_Length;
  603. break;
  604. }
  605. else
  606. {
  607. int p;
  608. int b;
  609. int IsMatched;
  610. p = pc;
  611. b = bc;
  612. IsMatched = FALSE;
  613. while( b <= buffer_Length && IsMatched == FALSE)
  614. {
  615. int last_b;
  616. last_b = b;
  617. IsMatched = IsLike(buffer, &b, buffer_Length, pattern, &p, pattern_Length);
  618. if( IsMatched == FALSE)
  619. {
  620. /* not matched, try again begining at next buffer position */
  621. p = pc;
  622. b = last_b + 1;
  623. }
  624. }
  625. if( IsMatched == FALSE )
  626. {
  627. /* not matched */
  628. return FALSE;
  629. }
  630. pc = p;
  631. bc = b;
  632. }
  633. }
  634. else
  635. {
  636. char pattChar;
  637. char buffChar;
  638. pattChar = pattern[pc];
  639. if (bc >= buffer_Length)
  640. {
  641. /* Check for end of string */
  642. /* we have completed the buffer without completing the pattern */
  643. return FALSE;
  644. }
  645. buffChar = buffer[bc];
  646. if( My->CurrentVersion->OptionFlags & OPTION_COMPARE_TEXT )
  647. {
  648. pattChar = bwb_toupper( pattChar );
  649. buffChar = bwb_toupper( buffChar );
  650. }
  651. if ( pattChar == buffChar )
  652. {
  653. /* matched specified character */
  654. pc++;
  655. bc++;
  656. }
  657. else
  658. {
  659. /* not matched */
  660. return FALSE;
  661. }
  662. }
  663. }
  664. /* Return true if all characters matched */
  665. if(pc < pattern_Length)
  666. {
  667. /* not matched */
  668. return FALSE;
  669. }
  670. if(bc < buffer_Length)
  671. {
  672. /* not matched */
  673. return FALSE;
  674. }
  675. /* all characters matched */
  676. *buffer_count = bc;
  677. *pattern_count = pc;
  678. return TRUE;
  679. }
  680. int
  681. str_like(StringType * a, StringType * b)
  682. {
  683. /*
  684. 'a' is buffer
  685. 'b' is pattern
  686. returns TRUE if "a LIKE b",
  687. returns FALSE otherwise
  688. and when ERROR occurs.
  689. */
  690. int a_count;
  691. int b_count;
  692. bwx_DEBUG(__FUNCTION__);
  693. if (a->length > BasicStringLengthMax)
  694. {
  695. /* INTERNAL ERROR, so truncate */
  696. a->length = BasicStringLengthMax;
  697. }
  698. if (b->length > BasicStringLengthMax)
  699. {
  700. /* INTERNAL ERROR, so truncate */
  701. b->length = BasicStringLengthMax;
  702. }
  703. if (a->sbuffer == NULL)
  704. {
  705. if (b->sbuffer == NULL)
  706. {
  707. return FALSE;
  708. }
  709. if (b->length == 0)
  710. {
  711. return FALSE;
  712. }
  713. return FALSE;
  714. }
  715. else
  716. {
  717. a->sbuffer[a->length] = BasicNulChar;
  718. }
  719. if (b->sbuffer == NULL)
  720. {
  721. if (a->sbuffer == NULL)
  722. {
  723. return FALSE;
  724. }
  725. if (a->length == 0)
  726. {
  727. return FALSE;
  728. }
  729. return FALSE;
  730. }
  731. else
  732. {
  733. b->sbuffer[b->length] = BasicNulChar;
  734. }
  735. a_count = 0;
  736. b_count = 0;
  737. return IsLike(a->sbuffer, &a_count, a->length, b->sbuffer, &b_count, b->length);
  738. }
  739. /* EOF */