Source code of Windows XP (NT5)
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.

1467 lines
48 KiB

  1. /*************************************************************************
  2. * *
  3. * CONVERT.C *
  4. * *
  5. * Copyright (C) Microsoft Corporation 1990-1994 *
  6. * All Rights reserved. *
  7. * *
  8. **************************************************************************
  9. * *
  10. * Module Intent *
  11. * Different data type breakers module *
  12. * *
  13. * Most of the data typre breakers deal with transformation an number *
  14. * to some strings that we are able to compare and search for. A full *
  15. * description of the encoding technique is described in field.doc. *
  16. * *
  17. * An encoded number has the following fields: *
  18. * +------+------ ---+------+----------+---------------------------+ *
  19. * | Len | Data Type | Sign | Exponent | Mantissa | *
  20. * +------+-----------+------+----------+---------------------------+ *
  21. * 2 byte 2 byte 1 byte 3 byte Variable *
  22. * Data type: Differentiate between different "numbers" generated from *
  23. * different data type breakers *
  24. * Sign byte: POSITIVE ('2') or NEGATIVE ('1') *
  25. * Exponent : 500 Bias *
  26. * Mantissa : Variable length, contains the "description" of the number *
  27. **************************************************************************
  28. * *
  29. * Current Owner: BinhN *
  30. * *
  31. **************************************************************************
  32. * *
  33. * Released by Development: (date) *
  34. * *
  35. *************************************************************************/
  36. #include <mvopsys.h>
  37. #include <mvsearch.h>
  38. #include "common.h"
  39. #ifdef _DEBUG
  40. PRIVATE BYTE NEAR s_aszModule[] = __FILE__; // Used by error return functions.
  41. #endif
  42. /* Short cut macros */
  43. #define IS_DIGIT(p) (p >= '0' && p <= '9')
  44. /* Location of different fields of the normalized number */
  45. #define SIGN_BYTE 4
  46. #define EXPONENT_BYTE 5
  47. #define MANTISSA_BYTE 8
  48. /* Size of fields */
  49. #define EXPONENT_FLD_SIZE 3
  50. /* Bias & limit of exponents we can handle */
  51. #define EXPONENT_BIAS 500
  52. #define MAX_EXPONENT 999
  53. /* The following table is used to calculate the 9-complement of a
  54. digit. The 9-complement is defined as
  55. digit + complement = 9
  56. The table is indexed by the value of the digit
  57. */
  58. PRIVATE BYTE ConvertTable[]= {
  59. '9',
  60. '8',
  61. '7',
  62. '6',
  63. '5',
  64. '4',
  65. '3',
  66. '2',
  67. '1',
  68. '0',
  69. };
  70. /* Number of days in regular years */
  71. BYTE DayInRegYear[] = {
  72. 0,
  73. 31, // January
  74. 28, // February
  75. 31, // March
  76. 30, // April
  77. 31, // May
  78. 30, // June
  79. 31, // July
  80. 31, // August
  81. 30, // September
  82. 31, // October
  83. 30, // November
  84. 31, // December
  85. };
  86. /* Number of days in leap years */
  87. BYTE DayInLeapYear[] = {
  88. 0,
  89. 31, // January
  90. 29, // February
  91. 31, // March
  92. 30, // April
  93. 31, // May
  94. 30, // June
  95. 31, // July
  96. 31, // August
  97. 30, // September
  98. 31, // October
  99. 30, // November
  100. 31, // December
  101. };
  102. /*
  103. The following constants are calculated in two ways:
  104. 1) days = <num-of-leap-years>*<leap-days> + <num-norm-years>*<norm-days>
  105. Ex: Days400Years = 97*366 + 303*365 = 35502+110595 = 146097
  106. 2) days = <num-years>*<norm-days> + <extra-days>
  107. Ex: Days400Years = 400*365 + 97 = 146000 + 97 = 146097
  108. The credit goes to Paul Cisek
  109. */
  110. #define DAYS_IN_400_YEARS 146097 /* Days in every 400 years */
  111. #define DAYS_IN_100_YEARS 36524 /* Days in every 100 years */
  112. /*************************************************************************
  113. *
  114. * API FUNCTIONS
  115. * All these functions must be exported in a .DEF file
  116. *************************************************************************/
  117. PUBLIC ERR EXPORT_API FAR PASCAL FBreakDate(LPBRK_PARMS);
  118. PUBLIC ERR EXPORT_API FAR PASCAL FBreakTime(LPBRK_PARMS);
  119. PUBLIC ERR EXPORT_API FAR PASCAL FBreakNumber(LPBRK_PARMS);
  120. PUBLIC ERR EXPORT_API FAR PASCAL FBreakEpoch(LPBRK_PARMS);
  121. /*************************************************************************
  122. *
  123. * INTERNAL GLOBAL FUNCTIONS
  124. * Those functions should be declared FAR to cause less problems with
  125. * with inter-segment calls, unless they are explicitly known to be
  126. * called from the same segment. Those functions should be declared
  127. * in an internal include file
  128. *************************************************************************/
  129. VOID PUBLIC FAR PASCAL LongToString (DWORD, WORD, int, LSZ);
  130. PUBLIC ERR FAR PASCAL DateToString (DWORD, DWORD, DWORD, int, LSZ);
  131. /*************************************************************************
  132. *
  133. * INTERNAL PRIVATE FUNCTIONS
  134. * All of them should be declared near
  135. *************************************************************************/
  136. PRIVATE LSZ PASCAL NEAR ScanNumber (LPDW, LPDW, LPDW, LPDW, LSZ, int FAR *);
  137. PRIVATE VOID PASCAL NEAR SetExponent (LSZ, int, int);
  138. PRIVATE LSZ PASCAL NEAR StringToLong (LSZ, LPDW);
  139. PRIVATE ERR PASCAL NEAR DataCollect (LPIBI, LPB, CB, LCB);
  140. PRIVATE LSZ PASCAL NEAR SkipBlank(LSZ);
  141. PRIVATE BOOL PASCAL NEAR WildCardByteCheck (LSZ, WORD);
  142. PRIVATE BOOL PASCAL NEAR IsBlank(BYTE);
  143. /*************************************************************************
  144. * @doc INTERNAL
  145. *
  146. * @func ERR PASCAL NEAR | DataCollect |
  147. * This function will collect all the characters and save them
  148. * in the raw word buffer. The buffer will be 0-terminated. The
  149. * main reason we have to collect the data is because there is
  150. * no guarantee that the breaker will get a whole entry at a time.
  151. *
  152. * @parm _LPIBI | lpibi |
  153. * Pointer to Internal Breaker Info structure. This must be non-null
  154. * It's left to the caller to do the checking
  155. *
  156. * @parm LPB | lpbInBuf |
  157. * Pointer to input buffer to be copied. It must be non-null.
  158. * It's left to the caller to do the checking
  159. *
  160. * @parm CB | cbInBufSize |
  161. * Size of input buffer
  162. *
  163. * @parm LCB | lcbInBufOffset |
  164. * Offset of the "word". This variable is only used for the
  165. * INITIAL_STATE
  166. *
  167. * @rdesc S_OK if succeeded, other errors in failed
  168. *
  169. * @comm No sanity check is done since it assumes that the caller will
  170. * do appropriate checking
  171. *************************************************************************/
  172. PRIVATE ERR PASCAL NEAR DataCollect (_LPIBI lpibi, LPB lpbInBuf,
  173. register CB cbInBufSize, LCB lcbInBufOffset)
  174. {
  175. register LPB lpbRawWord; // Pointer to input buffer
  176. register LPB lpbBufLimit; // Limit of buffer. This is for quick check
  177. if (lpibi->state == INITIAL_STATE)
  178. {
  179. /*
  180. * This is the beginning of a new datum. Do the initialization,
  181. * change state, then copy the string
  182. */
  183. *(LPW)lpibi->astRawWord = 0; // Set the word length = 0
  184. lpibi->lcb = lcbInBufOffset; // Remember the offset
  185. lpibi->state = COLLECTING_STATE; // Change the state to collect data
  186. }
  187. /* Collect the data */
  188. /*
  189. * Initialize variables
  190. */
  191. lpbBufLimit = &lpibi->astRawWord[CB_MAX_WORD_LEN];
  192. lpbRawWord = &lpibi->astRawWord[GETWORD(lpibi->astRawWord) + 2];
  193. /* Update string length */
  194. *(LPW)lpibi->astRawWord += (BYTE)cbInBufSize;
  195. /* Check for long string */
  196. if (lpbRawWord + cbInBufSize >= lpbBufLimit) {
  197. /* Reset the state */
  198. *(LPW)lpibi->astRawWord = 0;
  199. lpibi->state = INITIAL_STATE;
  200. return E_WORDTOOLONG;
  201. }
  202. /* Copy the string */
  203. while (cbInBufSize > 0) {
  204. *lpbRawWord ++ = *lpbInBuf++;
  205. cbInBufSize --;
  206. }
  207. *lpbRawWord = 0; // Zero terminated string for future use
  208. return S_OK;
  209. }
  210. /*************************************************************************
  211. * @doc API INDEX RETRIEVAL
  212. *
  213. * @func ERR FAR PASCAL | FBreakDate |
  214. * Convert a string of date into normalized dates. The input
  215. * format for date must be
  216. * mm/dd/yyyyy[B]
  217. * where
  218. * m: month
  219. * d: day
  220. * y: year
  221. * B: if B.C. date
  222. * All three fields must be present. The date will be converted into
  223. * number of days. Only one date will be processed.
  224. *
  225. * @parm LPBRK_PARMS | lpBrkParms |
  226. * Pointer to structure containing all the parameters needed for
  227. * the breaker. They include:
  228. * 1/ Pointer to the InternalBreakInfo. Must be non-null
  229. * 2/ Pointer to input buffer containing the word stream. If it is
  230. * NULL, then do the transformation and flush the buffer
  231. * 3/ Size of the input bufer
  232. * 4/ Offset in the source text of the first byte of the input buffer
  233. * 5/ Pointer to user's parameter block for the user's function
  234. * 6/ User's function to call with words. The format of the call should
  235. * be (*lpfnfOutWord)(BYTE *RawWord, BYTE *NormWord, LCB lcb,
  236. * LPV lpvUser)
  237. * The function should return S_OK if succeeded.
  238. * The function can be NULL
  239. * 7/ Pointer to stop word table. This table contains stop words specific
  240. * to this breaker. If this is non-null, then the function
  241. * will flag errors for stop word present in the query
  242. * 8/ Pointer to character table. If NULL, then the default built-in
  243. * character table will be used
  244. *
  245. * @rdesc
  246. * The function returns S_OK if succeeded. The failure's causes
  247. * are:
  248. * @flag E_BADFORMAT | Bad user's format
  249. * @flag E_WORDTOOLONG | Word too long
  250. * @flag E_INVALIDARG | Bad argument (eg. lpBrkParms = NULL)
  251. *
  252. * @comm For this function to successfully performed, the caller must
  253. * make sure to flush the breaker properly after every date
  254. *************************************************************************/
  255. PUBLIC ERR EXPORT_API FAR PASCAL FBreakDate(LPBRK_PARMS lpBrkParms)
  256. {
  257. DWORD day; // Number of days
  258. DWORD year; // Number of years
  259. DWORD month; // Number of months
  260. LPB lpbRawWord; // Collection buffer pointer
  261. ERR fRet; // Returned code
  262. LPB lpbResult; // Pointer to result buffer
  263. /* Breakers parameters break out */
  264. _LPIBI lpibi; // Pointer to internal breaker info
  265. LPB lpbInBuf; // Pointer to input buffer to be scanned
  266. CB cbInBufSize; // Number of bytes in input buffer
  267. LCB lcbInBufOffset; // Offset of the start of the datum from the buffer
  268. LPV lpvUser; // User's lpfnfOutWord parameters
  269. FWORDCB lpfnfOutWord; // User's function to be called with the result
  270. _LPSIPB lpsipb; // Pointer to stopword
  271. int NumCount; // Number of arguments we get
  272. LPB lpbWordStart; // Word's start
  273. /*
  274. * Initialize variables and sanity checks
  275. */
  276. if (lpBrkParms == NULL ||
  277. (lpibi = lpBrkParms->lpInternalBreakInfo) == NULL) {
  278. return E_INVALIDARG;
  279. }
  280. /* The following variables can be 0 or NULL */
  281. lpbInBuf = lpBrkParms->lpbBuf;
  282. cbInBufSize = lpBrkParms->cbBufCount;
  283. lcbInBufOffset = lpBrkParms->lcbBufOffset;
  284. lpvUser = lpBrkParms->lpvUser;
  285. lpfnfOutWord = lpBrkParms->lpfnOutWord;
  286. lpsipb = lpBrkParms->lpStopInfoBlock;
  287. if (lpbInBuf != NULL) {
  288. /* This is the collection state. Keep accumulating the input
  289. data into the buffer
  290. */
  291. return (DataCollect(lpibi, lpbInBuf, cbInBufSize,
  292. lcbInBufOffset));
  293. }
  294. lpbRawWord = &lpibi->astRawWord[2];
  295. /* Check for wildcard characters */
  296. if (WildCardByteCheck (lpbRawWord, *(LPW)lpibi->astRawWord))
  297. return E_WILD_IN_DTYPE;
  298. for (;;)
  299. {
  300. /* Skip all beginning junks */
  301. lpbWordStart = lpbRawWord = SkipBlank(lpbRawWord);
  302. if (*lpbRawWord == 0)
  303. {
  304. fRet = S_OK;
  305. goto ResetState;
  306. }
  307. /* Initialize variables */
  308. fRet = E_BADFORMAT; // Default return
  309. month = year = day = 0;
  310. /* Assume that we have year only */
  311. lpbRawWord = ScanNumber (&year, &day, &month, NULL,
  312. lpbRawWord, &NumCount);
  313. if (NumCount == 3)
  314. {
  315. /* We have complete date, exchange the values of month and year,
  316. since the format is mm/dd/yy */
  317. DWORD tmp;
  318. tmp = year;
  319. year = month;
  320. month = tmp;
  321. }
  322. else if (NumCount != 1)
  323. goto ResetState;
  324. /* Set pointer to result buffer */
  325. lpbResult = lpibi->astNormWord;
  326. /* Convert the date into string format, store it in lpbResult */
  327. if ((DateToString (year, month, day,
  328. ((*lpbRawWord | 0x20) == 'b' ? (int)NEGATIVE : (int)POSITIVE),
  329. lpbResult)) != S_OK)
  330. {
  331. goto ResetState;
  332. }
  333. /* Skip the terminating 'b' if necessary */
  334. if ((*lpbRawWord | 0x20) == 'b')
  335. lpbRawWord++;
  336. /* Make sure that we have nothing else after it */
  337. if (!IsBlank(*lpbRawWord))
  338. goto ResetState;
  339. /* Set the word length */
  340. *(LPW)lpibi->astRawWord = (WORD)(lpbRawWord - lpbWordStart);
  341. /* Check for stop word if required */
  342. if (lpsipb)
  343. {
  344. if (lpsipb->lpfnStopListLookup(lpsipb, lpbResult) == S_OK)
  345. {
  346. fRet = S_OK; // Ignore stop word
  347. continue;
  348. }
  349. }
  350. /* Invoke the user's function with the result */
  351. if (lpfnfOutWord)
  352. fRet = (ERR)((*lpfnfOutWord)(lpibi->astRawWord, lpbResult,
  353. (DWORD)(lpibi->lcb + (lpbWordStart - lpibi->astRawWord -2)), lpvUser));
  354. if (fRet != S_OK)
  355. goto ResetState;
  356. }
  357. ResetState:
  358. /* Reset the state */
  359. *(LPW)lpibi->astRawWord = 0;
  360. lpibi->state = INITIAL_STATE;
  361. return (fRet);
  362. }
  363. /*************************************************************************
  364. * @doc API INDEX RETRIEVAL
  365. *
  366. * @func ERR FAR PASCAL | FBreakTime |
  367. * Convert string of time into normalized time. The input
  368. * format for time must be
  369. * hh:mm:ss:dd[P]
  370. * where
  371. * h: hour
  372. * m: minute
  373. * s: second
  374. * d: hundredths of second
  375. * All first four fields must be present. The time will be converted
  376. * into hundredths of seconds. Only one time will be processed
  377. *
  378. * @parm LPBRK_PARMS | lpBrkParms |
  379. * Pointer to structure containing all the parameters needed for
  380. * the breaker. They include:
  381. * 1/ Pointer to the InternalBreakInfo. Must be non-null
  382. * 2/ Pointer to input buffer containing the word stream. If it is
  383. * NULL, then do the transformation and flush the buffer
  384. * 3/ Size of the input bufer
  385. * 4/ Offset in the source text of the first byte of the input buffer
  386. * 5/ Pointer to user's parameter block for the user's function
  387. * 6/ User's function to call with words. The format of the call should
  388. * be (*lpfnfOutWord)(BYTE *RawWord, BYTE *NormWord, LCB lcb,
  389. * LPV lpvUser)
  390. * The function should return S_OK if succeeded.
  391. * The function can be NULL
  392. * 7/ Pointer to stop word table. This table contains stop words specific
  393. * to this breaker. If this is non-null, then the function
  394. * will flag errors for stop word present in the query
  395. * 8/ Pointer to character table. If NULL, then the default built-in
  396. * character table will be used
  397. *
  398. * @rdesc
  399. * The function returns S_OK if succeeded. The failure's causes
  400. * are:
  401. * @flag E_BADFORMAT | Bad user's format
  402. * @flag E_WORDTOOLONG | Word too long
  403. * @flag E_INVALIDARG | Bad argument (eg. lpBrkParms = NULL)
  404. *
  405. * @comm For this function to successfully performed, the caller must
  406. * make sure to flush the breaker properly after every time
  407. *************************************************************************/
  408. PUBLIC ERR EXPORT_API FAR PASCAL FBreakTime(LPBRK_PARMS lpBrkParms)
  409. {
  410. DWORD hour; // Number of hours
  411. DWORD minute; // Number of minutes
  412. DWORD second; // Number of seconds
  413. DWORD hundredth; // Number of hundreths of second
  414. ERR fRet; // Returned code
  415. LPB lpbRawWord; // Collection buffer pointer
  416. LPB lpbResult; // Pointer to result buffer
  417. LPB lpbWordStart; // Word's start
  418. /* Breakers parameters break out */
  419. _LPIBI lpibi; // Pointer to internal breaker info
  420. LPB lpbInBuf; // Pointer to input buffer to be scanned
  421. CB cbInBufSize; // Number of bytes in input buffer
  422. LCB lcbInBufOffset; // Offset of the start of the datum from the buffer
  423. LPV lpvUser; // User's lpfnfOutWord parameters
  424. FWORDCB lpfnfOutWord; // User's function to be called with the result
  425. _LPSIPB lpsipb; // Pointer to stopword
  426. int NumCount; // Number of arguments we get
  427. /*
  428. * Initialize variables and sanity checks
  429. */
  430. if (lpBrkParms == NULL ||
  431. (lpibi = lpBrkParms->lpInternalBreakInfo) == NULL) {
  432. return E_INVALIDARG;
  433. }
  434. /* The following variables can be 0 or NULL */
  435. lpbInBuf = lpBrkParms->lpbBuf;
  436. cbInBufSize = lpBrkParms->cbBufCount;
  437. lcbInBufOffset = lpBrkParms->lcbBufOffset;
  438. lpvUser = lpBrkParms->lpvUser;
  439. lpfnfOutWord = lpBrkParms->lpfnOutWord;
  440. lpsipb = lpBrkParms->lpStopInfoBlock;
  441. if (lpbInBuf != NULL) {
  442. /* This is the collection state. Keep accumulating the input
  443. data into the buffer
  444. */
  445. return (DataCollect(lpibi, lpbInBuf, cbInBufSize,
  446. lcbInBufOffset));
  447. }
  448. /* Do the transformation and flush the result */
  449. lpbRawWord = &lpibi->astRawWord[2];
  450. /* Check for wildcard characters */
  451. if (WildCardByteCheck (lpbRawWord, *(LPW)lpibi->astRawWord))
  452. return E_WILD_IN_DTYPE;
  453. for (;;) {
  454. /* Skip all beginning junks */
  455. lpbWordStart = lpbRawWord = SkipBlank(lpbRawWord);
  456. if (*lpbRawWord == 0) {
  457. fRet = S_OK;
  458. goto ResetState;
  459. }
  460. lpbResult = lpibi->astNormWord;
  461. fRet = E_BADFORMAT;
  462. hour = minute = second = hundredth = 0;
  463. /* Scan hour, minute, second, hundreth */
  464. lpbRawWord = ScanNumber (&hour, &minute, &second, &hundredth,
  465. lpbRawWord, &NumCount);
  466. /* NumCount == 2 : HH:MM format
  467. * NumCount == 3 : HH:MM:SS format
  468. * NumCount == 4 : HH:MM:SS:HH format */
  469. if (NumCount < 2 || NumCount > 4)
  470. goto ResetState;
  471. /* Make sure that we have nothing else after it */
  472. if (!IsBlank(*lpbRawWord))
  473. goto ResetState;
  474. #if 0 // PM format currently is not spec' ed
  475. if ((*lpbRawWord | 0x20) == 'p') {
  476. /* Deal with PM time. Note: if we have P.M., this is time
  477. and not duration. So:
  478. - If hour < 12, add 12 hours
  479. - If hour >= 24, round it off to 24 hours format
  480. */
  481. if (hour >= 24)
  482. hour = hour % 24 + 12;
  483. if (hour < 12)
  484. hour += 12;
  485. }
  486. #endif
  487. /* Set the word length */
  488. *(LPW)lpibi->astRawWord = (WORD)(lpbRawWord - lpbWordStart);
  489. /* Convert the time into hundredth of seconds */
  490. hundredth += (((hour * 60) + minute) * 60 + second) * 100;
  491. LongToString (hundredth, TIME_FORMAT, POSITIVE, lpbResult);
  492. /* Check for stop word if required */
  493. if (lpsipb)
  494. {
  495. if (lpsipb->lpfnStopListLookup(lpsipb, lpbResult) == S_OK)
  496. {
  497. fRet = S_OK; // Ignore stop word
  498. continue;
  499. }
  500. }
  501. fRet = S_OK;
  502. /* Invoke the user's function with the result */
  503. if (lpfnfOutWord)
  504. fRet = (ERR)((*lpfnfOutWord)(lpibi->astRawWord, lpbResult,
  505. (DWORD)(lpibi->lcb + (lpbWordStart - lpibi->astRawWord -2)), lpvUser));
  506. if (fRet != S_OK)
  507. goto ResetState;
  508. }
  509. ResetState:
  510. /* Reset the state */
  511. *(LPW)lpibi->astRawWord = 0;
  512. lpibi->state = INITIAL_STATE;
  513. return (fRet);
  514. }
  515. /*************************************************************************
  516. * @doc API INDEX RETRIEVAL
  517. *
  518. * @func ERR FAR PASCAL | FBreakNumber |
  519. * Normalize an ASCII number. The input format of the number must be
  520. * [+-]nnnn.nnn[E[+-]eee]
  521. * where
  522. * n: digit
  523. * e: exponent
  524. * The total exponent must be less than 499. No space is allowed
  525. * between the fields
  526. *
  527. * @parm LPBRK_PARMS | lpBrkParms |
  528. * Pointer to structure containing all the parameters needed for
  529. * the breaker. They include:
  530. * 1/ Pointer to the InternalBreakInfo. Must be non-null
  531. * 2/ Pointer to input buffer containing the word stream. If it is
  532. * NULL, then do the transformation and flush the buffer
  533. * 3/ Size of the input bufer
  534. * 4/ Offset in the source text of the first byte of the input buffer
  535. * 5/ Pointer to user's parameter block for the user's function
  536. * 6/ User's function to call with words. The format of the call should
  537. * be (*lpfnfOutWord)(BYTE *RawWord, BYTE *NormWord, LCB lcb,
  538. * LPV lpvUser)
  539. * The function should return S_OK if succeeded.
  540. * The function can be NULL
  541. * 7/ Pointer to stop word table. This table contains stop words specific
  542. * to this breaker. If this is non-null, then the function
  543. * will flag errors for stop word present in the query
  544. * 8/ Pointer to character table. If NULL, then the default built-in
  545. * character table will be used
  546. *
  547. * @rdesc
  548. * The function returns S_OK if succeeded. The failure's causes
  549. * are:
  550. * @flag E_BADFORMAT | Bad user's format
  551. * @flag E_WORDTOOLONG | Word too long
  552. * @flag E_INVALIDARG | Bad argument (eg. lpBrkParms = NULL)
  553. *
  554. * @comm For this function to successfully performed, the caller must
  555. * make sure to flush the breaker properly after every number
  556. *************************************************************************/
  557. PUBLIC ERR EXPORT_API FAR PASCAL FBreakNumber(LPBRK_PARMS lpBrkParms)
  558. {
  559. int exponent; // Exponent to be emitted
  560. int exp; // Exponent get from the input data
  561. LPB lpStart; // Starting of mantissa string
  562. DWORD tmp; // Temporary scratch
  563. LPB lpbRawWord; // Collection buffer pointer
  564. register LSZ lpbResult; // Pointer to result buffer
  565. LSZ Result; // Beginning of result buffer (quick access)
  566. ERR fRet; // Return code
  567. /* Breakers parameters break out */
  568. _LPIBI lpibi; // Pointer to internal breaker info
  569. LPB lpbInBuf; // Pointer to input buffer to be scanned
  570. CB cbInBufSize; // Number of bytes in input buffer
  571. LCB lcbInBufOffset; // Offset of the start of the datum from the buffer
  572. LPV lpvUser; // User's lpfnfOutWord parameters
  573. FWORDCB lpfnfOutWord; // User's function to be called with the result
  574. _LPSIPB lpsipb; // Pointer to stopword
  575. LPB lpbWordStart; // Word's start
  576. /*
  577. * Initialize variables and sanity checks
  578. */
  579. if (lpBrkParms == NULL ||
  580. (lpibi = lpBrkParms->lpInternalBreakInfo) == NULL) {
  581. return E_INVALIDARG;
  582. }
  583. /* The following variables can be 0 or NULL */
  584. lpbInBuf = lpBrkParms->lpbBuf;
  585. cbInBufSize = lpBrkParms->cbBufCount;
  586. lcbInBufOffset = lpBrkParms->lcbBufOffset;
  587. lpvUser = lpBrkParms->lpvUser;
  588. lpfnfOutWord = lpBrkParms->lpfnOutWord;
  589. lpsipb = lpBrkParms->lpStopInfoBlock;
  590. if (lpbInBuf != NULL) {
  591. /* This is the collection state. Keep accumulating the input
  592. data into the buffer
  593. */
  594. return (DataCollect(lpibi, lpbInBuf, cbInBufSize,
  595. lcbInBufOffset));
  596. }
  597. lpbRawWord = &lpibi->astRawWord[2];
  598. /* Check for wildcard characters */
  599. if (WildCardByteCheck (lpbRawWord, *(LPW)lpibi->astRawWord))
  600. return E_WILD_IN_DTYPE;
  601. for (;;)
  602. {
  603. Result = lpibi->astNormWord;
  604. lpbResult = &Result[2];
  605. /* Skip all beginning junks */
  606. lpbWordStart = lpbRawWord = SkipBlank(lpbRawWord);
  607. if (*lpbRawWord == 0) {
  608. fRet = S_OK;
  609. goto ResetState;
  610. }
  611. fRet = E_BADFORMAT; // Default error
  612. exponent = exp = 0;
  613. *lpbResult++ = 1;
  614. *lpbResult++ = NUMBER_FORMAT;
  615. /* Get the sign */
  616. if (*lpbRawWord == '-')
  617. {
  618. *lpbResult = NEGATIVE;
  619. lpbRawWord++;
  620. }
  621. else
  622. {
  623. *lpbResult = POSITIVE;
  624. if (*lpbRawWord == '+') lpbRawWord++; // Skip the sign
  625. }
  626. /* Allow the form .01, ie. integral not needed */
  627. if (!IS_DIGIT(*lpbRawWord) && *lpbRawWord != '.')
  628. goto ResetState;
  629. /* Get the integral part */
  630. lpStart = lpbResult = &Result[MANTISSA_BYTE];
  631. while (*lpbRawWord == '0') // skip all leading 0
  632. lpbRawWord++;
  633. /* The scanner accepts ',' as part of the number. This should be
  634. country specific (ie. scanned and checked by UI), but since nobody is
  635. doing the checking now, I have to do it here by just acceopting the ','.
  636. What it means is that entry like ,,,,1,,,2,, will be accepted. It is
  637. possible to do better checking, but is it necessary?
  638. */
  639. while (IS_DIGIT(*lpbRawWord) || *lpbRawWord == ',') {
  640. if (*lpbRawWord != ',') {
  641. *lpbResult++ = *lpbRawWord;
  642. exponent++;
  643. }
  644. lpbRawWord++;
  645. }
  646. if (*lpbRawWord == 0)
  647. goto Done;
  648. /* Get the fractional part */
  649. if (*lpbRawWord == '.') {
  650. *lpbRawWord++;
  651. while (*lpbRawWord == '0') {
  652. /* Handle the '0' for of 0.000001 for example */
  653. if (exponent <= 0)
  654. exponent--;
  655. else
  656. *lpbResult++ = *lpbRawWord;
  657. lpbRawWord++;
  658. }
  659. /* Just copy the remaining digits */
  660. while (IS_DIGIT(*lpbRawWord))
  661. *lpbResult++ = *lpbRawWord++;
  662. }
  663. if (*lpbRawWord == 0)
  664. goto Done;
  665. /* Check for exponent */
  666. if (*lpbRawWord == 'E' || *lpbRawWord == 'e') {
  667. lpbRawWord++;
  668. if (*lpbRawWord == '-') {
  669. exp = -1;
  670. lpbRawWord++;
  671. }
  672. else {
  673. exp = 1;
  674. if (*lpbRawWord == '+')
  675. lpbRawWord++;
  676. }
  677. /* Scan the exponent */
  678. if ((lpbRawWord = (LPB)StringToLong(lpbRawWord, &tmp)) == NULL)
  679. goto ResetState;
  680. exp *= (int)tmp;
  681. }
  682. Done:
  683. /* Set the word length */
  684. *(LPW)lpibi->astRawWord = (WORD)(lpbRawWord - lpbWordStart);
  685. /* Make sure that we have nothing else after it */
  686. if (!IsBlank(*lpbRawWord))
  687. goto ResetState;
  688. exponent += exp + EXPONENT_BIAS - 1;
  689. if (exponent > MAX_EXPONENT || exponent < 0) {
  690. fRet = E_BADVALUE;
  691. goto ResetState;
  692. }
  693. if (lpbResult <= lpStart) {
  694. /* No significant digit, ie. 0 */
  695. exponent = 0;
  696. Result[SIGN_BYTE] = POSITIVE;
  697. *lpbResult++ = '0';
  698. }
  699. *lpbResult = 0;
  700. /* Write the ascii exponent */
  701. SetExponent(&Result[MANTISSA_BYTE]-1, exponent, EXPONENT_FLD_SIZE - 1);
  702. if (Result[SIGN_BYTE] == NEGATIVE) { /* Negative number */
  703. /* Complement the result */
  704. for (lpbResult = &Result[EXPONENT_BYTE]; *lpbResult; lpbResult++)
  705. *lpbResult = ConvertTable[*lpbResult - '0'];
  706. }
  707. /* Remove trailing 0's */
  708. for (--lpbResult; *lpbResult == '0' && lpbResult > lpStart; lpbResult--)
  709. *lpbResult = 0;
  710. /* Set the word length */
  711. *(LPW)Result = (BYTE) (lpbResult - Result);
  712. /* Check for stop word if required */
  713. if (lpsipb) {
  714. if (lpsipb->lpfnStopListLookup(lpsipb, Result) == S_OK)
  715. {
  716. fRet = S_OK; // Ignore stop word
  717. continue;
  718. }
  719. }
  720. /* Invoke the user's function with the result */
  721. fRet = S_OK;
  722. if (lpfnfOutWord)
  723. fRet = (ERR)((*lpfnfOutWord)(lpibi->astRawWord, Result,
  724. (DWORD)(lpibi->lcb + (lpbWordStart - lpibi->astRawWord -2)), lpvUser));
  725. if (fRet != S_OK)
  726. goto ResetState;
  727. }
  728. ResetState:
  729. /* Reset the state */
  730. *(LPW)lpibi->astRawWord = 0;
  731. lpibi->state = INITIAL_STATE;
  732. return (fRet);
  733. }
  734. /*************************************************************************
  735. * @doc API INDEX RETRIEVAL
  736. *
  737. * @func ERR FAR PASCAL | FBreakEpoch |
  738. * Normalize an epoch. The input format of the epoch must be
  739. * is:
  740. * nnnnnn...nnnnnn[B]
  741. * where
  742. * n: digit
  743. * The total exponent must be less than 499. No space is allowed between
  744. * the fields
  745. *
  746. * @parm LPBRK_PARMS | lpBrkParms |
  747. * Pointer to structure containing all the parameters needed for
  748. * the breaker. They include:
  749. * 1/ Pointer to the InternalBreakInfo. Must be non-null
  750. * 2/ Pointer to input buffer containing the word stream. If it is
  751. * NULL, then do the transformation and flush the buffer
  752. * 3/ Size of the input bufer
  753. * 4/ Offset in the source text of the first byte of the input buffer
  754. * 5/ Pointer to user's parameter block for the user's function
  755. * 6/ User's function to call with words. The format of the call should
  756. * be (*lpfnfOutWord)(BYTE *RawWord, BYTE *NormWord, LCB lcb,
  757. * LPV lpvUser)
  758. * The function should return S_OK if succeeded.
  759. * The function can be NULL
  760. * 7/ Pointer to stop word table. This table contains stop words specific
  761. * to this breaker. If this is non-null, then the function
  762. * will flag errors for stop word present in the query
  763. * 8/ Pointer to character table. If NULL, then the default built-in
  764. * character table will be used
  765. *
  766. * @rdesc
  767. * The function returns S_OK if succeeded. The failure's causes
  768. * are:
  769. * @flag E_BADFORMAT | Bad user's format
  770. * @flag E_WORDTOOLONG | Word too long
  771. * @flag E_INVALIDARG | Bad argument (eg. lpBrkParms = NULL)
  772. *
  773. * @comm For this function to successfully performed, the caller must
  774. * make sure to flush the breaker properly after every epoch
  775. *************************************************************************/
  776. PUBLIC ERR EXPORT_API FAR PASCAL FBreakEpoch(LPBRK_PARMS lpBrkParms)
  777. {
  778. int exponent;
  779. int exp;
  780. LPB lpStart;
  781. LPB lpbRawWord; // Collection buffer pointer
  782. register LSZ lpbResult;
  783. LSZ Result;
  784. ERR fRet;
  785. /* Breakers parameters break out */
  786. _LPIBI lpibi;
  787. LPB lpbInBuf;
  788. CB cbInBufSize;
  789. LCB lcbInBufOffset;
  790. LPV lpvUser;
  791. FWORDCB lpfnfOutWord;
  792. _LPSIPB lpsipb;
  793. LPB lpbWordStart; // Word's start
  794. /*
  795. * Initialize variables
  796. */
  797. if (lpBrkParms == NULL ||
  798. (lpibi = lpBrkParms->lpInternalBreakInfo) == NULL)
  799. return E_INVALIDARG;
  800. lpbInBuf = lpBrkParms->lpbBuf;
  801. cbInBufSize = lpBrkParms->cbBufCount;
  802. lcbInBufOffset = lpBrkParms->lcbBufOffset;
  803. lpvUser = lpBrkParms->lpvUser;
  804. lpfnfOutWord = lpBrkParms->lpfnOutWord;
  805. lpsipb = lpBrkParms->lpStopInfoBlock;
  806. if (lpbInBuf != NULL) {
  807. /* This is the collection state. Keep accumulating the input
  808. data into the buffer
  809. */
  810. return (DataCollect(lpibi, lpbInBuf, cbInBufSize,
  811. lcbInBufOffset));
  812. }
  813. lpbRawWord = &lpibi->astRawWord[2];
  814. /* Check for wildcard characters */
  815. if (WildCardByteCheck (lpbRawWord, *(LPW)lpibi->astRawWord))
  816. return E_WILD_IN_DTYPE;
  817. for (;;) {
  818. /* Skip all beginning junks */
  819. lpbWordStart = lpbRawWord = SkipBlank(lpbRawWord);
  820. if (*lpbRawWord == 0) {
  821. fRet = S_OK;
  822. goto ResetState;
  823. }
  824. Result = lpibi->astNormWord;
  825. lpbResult = &Result[2];
  826. fRet = E_BADFORMAT;
  827. exponent = exp = 0;
  828. *lpbResult++ = 1;
  829. *lpbResult++ = EPOCH_FORMAT;
  830. /* If it is not a digit then just return E_BADFORMAT */
  831. if (!IS_DIGIT(*lpbRawWord))
  832. goto ResetState;
  833. /* Get the integral part */
  834. lpStart = lpbResult = &Result[MANTISSA_BYTE];
  835. while (*lpbRawWord == '0') // skip all leading 0
  836. lpbRawWord++;
  837. /* The scanner accepts ',' as part of the number. This should be
  838. country specific (ie. scanned and checked by UI), but since nobody is
  839. doing the checking now, I have to do it here by just acceopting the ','.
  840. What it means is that entry like ,,,,1,,,2,, will be accepted. It is
  841. possible to do better checking, but is it necessary?
  842. */
  843. while (IS_DIGIT(*lpbRawWord) || *lpbRawWord == ',') {
  844. if (*lpbRawWord != ',') {
  845. *lpbResult++ = *lpbRawWord;
  846. exponent++;
  847. }
  848. lpbRawWord++;
  849. }
  850. /* Check for the last 'B' */
  851. Result[SIGN_BYTE] = ((*lpbRawWord | 0x20) == 'b') ?
  852. (BYTE)NEGATIVE : (BYTE)POSITIVE;
  853. /* Skip the terminating 'b' if necessary */
  854. if ((*lpbRawWord | 0x20) == 'b')
  855. lpbRawWord++;
  856. /* Make sure that we have nothing else after it */
  857. if (!IsBlank(*lpbRawWord))
  858. goto ResetState;
  859. /* Set the word length and offset */
  860. *(LPW)lpibi->astRawWord = (WORD)(lpbRawWord - lpbWordStart);
  861. exponent += exp + EXPONENT_BIAS - 1;
  862. if (exponent > MAX_EXPONENT || exponent < 0) {
  863. fRet = E_BADVALUE;
  864. goto ResetState;
  865. }
  866. if (lpbResult <= lpStart) {
  867. /* No significant digit, ie. 0 */
  868. exponent = 0;
  869. Result[SIGN_BYTE] = POSITIVE;
  870. *lpbResult++ = '0';
  871. }
  872. *lpbResult = 0;
  873. SetExponent(&Result[MANTISSA_BYTE]-1, exponent, EXPONENT_FLD_SIZE-1);
  874. if (Result[SIGN_BYTE] == NEGATIVE) { /* Negative number */
  875. for (lpbResult = &Result[EXPONENT_BYTE]; *lpbResult; lpbResult++)
  876. *lpbResult = ConvertTable[*lpbResult - '0'];
  877. }
  878. /* Remove trailing 0's */
  879. for (--lpbResult; *lpbResult == '0' && lpbResult > lpStart; lpbResult--)
  880. *lpbResult = 0;
  881. /* Set the word length */
  882. *(LPW)Result = (WORD) (lpbResult - Result);
  883. /* Check for stop word if required */
  884. if (lpsipb) {
  885. if (lpsipb->lpfnStopListLookup(lpsipb, Result) == S_OK)
  886. {
  887. fRet = S_OK; // Ignore stop word
  888. continue;
  889. }
  890. }
  891. /* Invoke the user's function with the result */
  892. fRet = S_OK;
  893. if (lpfnfOutWord)
  894. fRet = (ERR)((*lpfnfOutWord)(lpibi->astRawWord, Result,
  895. (DWORD)(lpibi->lcb + (lpbWordStart - lpibi->astRawWord -2)), lpvUser));
  896. if (fRet != S_OK)
  897. goto ResetState;
  898. }
  899. ResetState:
  900. /* Reset the state */
  901. *(LPW)lpibi->astRawWord = 0;
  902. lpibi->state = INITIAL_STATE;
  903. return (fRet);
  904. }
  905. /*************************************************************************
  906. * @doc INTERNAL
  907. *
  908. * @func ERR FAR PASCAL | DateToString |
  909. * Given a date in numerical value of year, month, and days, this
  910. * function will return a string containing the normalized form of
  911. * the date (converted into number of days)
  912. *
  913. * @parm DWORD | year |
  914. * Numerical year
  915. *
  916. * @parm DWORD | month |
  917. * Numerical month
  918. *
  919. * @parm DWORD | day |
  920. * Numerical months
  921. *
  922. * @parm int | fSign |
  923. * Either POSITIVE, or NEGATIVE
  924. *
  925. * @parm LSZ | lszResult |
  926. * Buffer for the normalized result
  927. *
  928. * @rdesc
  929. * The function returns S_OK if succeeded. The failure's causes
  930. * are:
  931. * @flag S_OK | if S_OK.
  932. * @flag E_BADVALUE| if the date is ill-formed
  933. *************************************************************************/
  934. PUBLIC ERR FAR PASCAL DateToString (DWORD year, DWORD month, DWORD day,
  935. int fSign, LSZ lszResult)
  936. {
  937. register BYTE *pDayInMonth; // Pointer to number of days in month
  938. register DWORD i; // Scratch variable
  939. DWORD tmpYear; // Scratch variable
  940. /* Check for date consistency. Note that invidual parameter can
  941. be 0, but not all of them
  942. */
  943. if ((year | month | day) == 0)
  944. return E_BADVALUE;
  945. /* Check for leap year */
  946. if ((year % 4 != 0) || ((year % 100 == 0) && (year % 400 != 0))) {
  947. /* Not a leap year */
  948. pDayInMonth = DayInRegYear;
  949. }
  950. else // Leap year
  951. pDayInMonth = DayInLeapYear;
  952. /* Check for date validity */
  953. if (month > 12 || day > pDayInMonth[month] || year > MAX_YEAR)
  954. return E_BADVALUE;
  955. /* Convert the date to number of days */
  956. if (month > 0) {
  957. year --;
  958. }
  959. if (day > 0) {
  960. if (month == 0)
  961. return E_BADVALUE;
  962. month --;
  963. }
  964. for (i = 1; i <= month; i++)
  965. day += pDayInMonth[i];
  966. /* One way for year to be >= MAX_YEAR at this point is that the user
  967. types in mm/dd/0. By decrementing year above, we make it > MAX_YEAR
  968. */
  969. if (year < MAX_YEAR) {
  970. /* Convert <year> into <days> */
  971. day += year/400 * DAYS_IN_400_YEARS;
  972. year = year % 400;
  973. day += year/100 * DAYS_IN_100_YEARS;
  974. year = year % 100;
  975. for (tmpYear = 0; tmpYear <= year; tmpYear++) {
  976. if ((tmpYear % 4 != 0) ||
  977. ((tmpYear % 100 == 0) && (tmpYear % 400 != 0))) {
  978. /* Not a leap year */
  979. day += 365;
  980. }
  981. else
  982. day += 366;
  983. }
  984. }
  985. LongToString (day, DATE_FORMAT, fSign, lszResult);
  986. return S_OK;
  987. }
  988. /*************************************************************************
  989. * @doc INTERNAL
  990. *
  991. * @func VOID FAR PASCAL | LongToString |
  992. * Given a DWORD number, the function will convert it into a
  993. * normalized string.
  994. *
  995. * @parm DWORD | Number |
  996. * The number in unsigned format
  997. *
  998. * @parm WORD | FormatStamp |
  999. * The number stamp, which states the data type of the number
  1000. *
  1001. * @parm WORD | Sign |
  1002. * Value: POSITIVE, or NEGATIVE
  1003. *
  1004. * @parm LSZ | lszResult |
  1005. * Buffer to receive the result
  1006. *
  1007. *************************************************************************/
  1008. VOID PUBLIC FAR PASCAL LongToString (DWORD Number, WORD FormatStamp,
  1009. int Sign, LSZ lszResult)
  1010. {
  1011. BYTE Buffer[CB_MAX_WORD_LEN]; // Scratch buffer containing the "number"
  1012. register LSZ lsz; // Scratch pointer
  1013. int Exponent; // Number's exponent
  1014. LPB lpbStart; // Beginnning of lszResult
  1015. #ifdef TEST
  1016. printf ("Convert %ld ,", Number);
  1017. #endif
  1018. /* Remember where we start, and leave room for the word's length */
  1019. lpbStart = lszResult;
  1020. lszResult += sizeof(WORD);
  1021. /* Set the format */
  1022. *lszResult++ = 1;
  1023. *lszResult = (BYTE)FormatStamp;
  1024. lszResult ++;
  1025. /*
  1026. Handle 0 case. 0 will be represented as 0 exponent,
  1027. and 0 mantissa
  1028. */
  1029. if (Number == 0) {
  1030. *lszResult ++ = POSITIVE;
  1031. /* 3 zero for exponent, and 1 for matissa */
  1032. *(DWORD FAR *)lszResult = 0x30303030; // "0000"
  1033. lszResult += sizeof (DWORD);
  1034. *lszResult = 0;
  1035. *lpbStart = (BYTE)(lszResult - lpbStart);
  1036. return;
  1037. }
  1038. *lszResult++ = (BYTE)Sign;
  1039. Exponent = EXPONENT_BIAS;
  1040. lsz = &Buffer[CB_MAX_WORD_LEN - 1];
  1041. *lsz-- = 0; // Terminated 0
  1042. while (Number) {
  1043. *lsz-- = (BYTE)(Number % 10 + '0');
  1044. Number /= 10;
  1045. Exponent ++;
  1046. }
  1047. SetExponent(lsz, Exponent, EXPONENT_FLD_SIZE-1);
  1048. lsz -= 2;
  1049. /* Copy the string over */
  1050. if (Sign == POSITIVE) {
  1051. while (*lszResult = *lsz++)
  1052. lszResult++;
  1053. }
  1054. else {
  1055. while (*lsz)
  1056. *lszResult++ = ConvertTable [*lsz++ - '0'];
  1057. *lszResult = 0;
  1058. }
  1059. /* Remove trailing 0's */
  1060. while (*--lszResult == '0')
  1061. *lszResult = 0;
  1062. *(LPW)lpbStart = (WORD)(lszResult - lpbStart);
  1063. }
  1064. /*************************************************************************
  1065. * @doc INTERNAL
  1066. *
  1067. * @func LSZ PASCAL NEAR | StringToLong |
  1068. * The function reads in a string of digits and convert them into
  1069. * a DWORD. The function will move the input pointer correspondingly
  1070. *
  1071. * @parm LSZ | lszBuf |
  1072. * Input buffer containing the string of digit
  1073. * @parm LPDW | lpValue |
  1074. * Pointer to a DWORD that receives the result
  1075. *
  1076. * @rdesc NULL, if there is no digit. The new position of the input
  1077. * buffer pointer
  1078. *************************************************************************/
  1079. PRIVATE LSZ PASCAL NEAR StringToLong (LSZ lszBuf, LPDW lpValue)
  1080. {
  1081. register DWORD Result; // Returned result
  1082. register int i; // Scratch variable
  1083. char fGetDigit; // Flag to mark we do get a digit
  1084. /* Skip all blanks, tabs, <CR> */
  1085. lszBuf = SkipBlank(lszBuf);
  1086. Result = fGetDigit = 0;
  1087. /* The credit of this piece of code goes to Leon */
  1088. while (i = *lszBuf - '0', i >= 0 && i <= 9) {
  1089. fGetDigit = TRUE;
  1090. Result = Result * 10 + i;
  1091. lszBuf++;
  1092. }
  1093. *lpValue = Result;
  1094. return (fGetDigit ? lszBuf : NULL);
  1095. }
  1096. /*************************************************************************
  1097. * @doc INTERNAL
  1098. *
  1099. * @func LSZ PASCAL NEAR | ScanNumber |
  1100. * The function reads in a string of digits of the format
  1101. * nnnn/nnnn/nnnn
  1102. * where:
  1103. * n : digits
  1104. * Any non-digit delimiter can be used.
  1105. * It then breaks the string into invidual numbers. The input
  1106. * pointer will advance accordingly
  1107. *
  1108. * @parm LSZ | lszBuf |
  1109. * Input buffer containing the string of digit
  1110. *
  1111. * @parm LPDW | lpNum1 |
  1112. * Pointer to DWORD that will receive the 1st result
  1113. *
  1114. * @parm LPDW | lpNum2 |
  1115. * Pointer to DWORD that will receive the 2nd result
  1116. *
  1117. * @parm LPDW | lpNum3 |
  1118. * Pointer to DWORD that will receive the 3rd result
  1119. *
  1120. * @parm LPDW | lpNum4 |
  1121. * Pointer to DWORD that will receive the 4th result
  1122. *
  1123. * @rdesc
  1124. * NULL, if there is not enough digits to be processed
  1125. * The new position of the input buffer pointer
  1126. *************************************************************************/
  1127. PRIVATE LSZ PASCAL NEAR ScanNumber (LPDW lpNum1, LPDW lpNum2,
  1128. LPDW lpNum3, LPDW lpNum4, LSZ lszInBuf, int FAR *lpArgCount)
  1129. {
  1130. LSZ lszStart;
  1131. lszStart = lszInBuf; // Save initial offset
  1132. /* Scan 1st number */
  1133. if ((lszInBuf = StringToLong (lszInBuf, lpNum1)) == NULL) {
  1134. *lpArgCount = 0;
  1135. return lszStart;
  1136. }
  1137. /* We get at least one argument */
  1138. *lpArgCount = 1;
  1139. if (lpNum2 == NULL || *lszInBuf == 0 || (*lszInBuf | 0x20) == 'b' ||
  1140. *lszInBuf == ' ' || *lszInBuf == '\t' || *lszInBuf == '\r' ||
  1141. *lszInBuf == '\n')
  1142. return lszInBuf;
  1143. if (*lszInBuf != '/' && *lszInBuf != ':')
  1144. return lszInBuf;
  1145. lszStart = ++lszInBuf; // Skip delimiter
  1146. if (!IS_DIGIT(*lszInBuf)) {
  1147. *lpArgCount = 0; // Make sure that we have error
  1148. return lszInBuf;
  1149. }
  1150. /* Scan 2nd number */
  1151. if ((lszInBuf = StringToLong (lszInBuf, lpNum2)) == NULL)
  1152. return lszStart;
  1153. *lpArgCount = 2;
  1154. if (lpNum3 == NULL || *lszInBuf == 0)
  1155. return lszInBuf;
  1156. if (*lszInBuf != '/' && *lszInBuf != ':')
  1157. return lszInBuf;
  1158. lszStart = ++lszInBuf; // Skip delimiter
  1159. if (!IS_DIGIT(*lszInBuf)) {
  1160. *lpArgCount = 0; // Make sure that we have error
  1161. return lszInBuf;
  1162. }
  1163. /* Scan 3rd number */
  1164. if ((lszInBuf = StringToLong (lszInBuf, lpNum3)) == NULL)
  1165. return lszStart;
  1166. *lpArgCount = 3;
  1167. if (lpNum4 == NULL || *lszInBuf == 0)
  1168. return lszInBuf;
  1169. if (*lszInBuf != '/' && *lszInBuf != ':')
  1170. return lszInBuf;
  1171. lszStart = ++lszInBuf; // Skip delimiter
  1172. if (!IS_DIGIT(*lszInBuf)) {
  1173. *lpArgCount = 0; // Make sure that we have error
  1174. return lszInBuf;
  1175. }
  1176. /* Scan 4th number */
  1177. if ((lszInBuf = StringToLong (lszInBuf, lpNum4)) == NULL)
  1178. return lszStart;
  1179. *lpArgCount = 4;
  1180. return lszInBuf;
  1181. }
  1182. /*************************************************************************
  1183. * @doc INTERNAL
  1184. *
  1185. * @func VOID PASCAL NEAR | SetExponent |
  1186. * Given a buffer and a numerical exponent, ths function will
  1187. * write the exponent in its ASCII form into the buffer. The
  1188. * beginning of the exponent will be padded with '0' if necessary
  1189. * The writing is done from right to left, and is controlled
  1190. * by level, which is also a zero-based index into the exponent buffer
  1191. * (where to put the digit)
  1192. *
  1193. * @parm LSZ | pBuf |
  1194. * Buffer that will contain the ASCII exponent
  1195. *
  1196. * @parm int | exponent |
  1197. * Numerical exponent
  1198. *
  1199. * @parm int | level |
  1200. * Length of buffer (also controlling the level of recursion)
  1201. *************************************************************************/
  1202. PRIVATE VOID PASCAL NEAR SetExponent (LSZ pBuf, int exponent, int level)
  1203. {
  1204. int exp;
  1205. if (level < 0)
  1206. return;
  1207. *pBuf = (char)(exponent - (exp = (exponent / 10)) * 10 + '0');
  1208. SetExponent (pBuf - 1, exp, level - 1);
  1209. }
  1210. /*************************************************************************
  1211. * @doc INTERNAL
  1212. *
  1213. * @func LSZ PASCAL NEAR | SkipBlank |
  1214. * Skip any blank, tab, CR, newline
  1215. *
  1216. * @parm LSZ | lpBuf |
  1217. * Input zero-terminated string buffer pointer
  1218. *
  1219. * @rdesc Advance the pointer to the non-blank character.
  1220. *************************************************************************/
  1221. PRIVATE LSZ PASCAL NEAR SkipBlank(LSZ lpBuf)
  1222. {
  1223. while (*lpBuf == ' ' || *lpBuf == '\t' || *lpBuf == '\r' ||
  1224. *lpBuf == '\n')
  1225. lpBuf++;
  1226. return lpBuf;
  1227. }
  1228. /*************************************************************************
  1229. * @doc INTERNAL
  1230. *
  1231. * @func BOOL PASCAL NEAR | IsBlank |
  1232. * Check to see the current char is a blank, tab, CR, newline
  1233. * 0 is consider to be a blank
  1234. *
  1235. * @parm BYTE | bCur |
  1236. * Current byte
  1237. *
  1238. * @rdesc TRUE if it is
  1239. *************************************************************************/
  1240. PRIVATE BOOL PASCAL NEAR IsBlank(BYTE bCur)
  1241. {
  1242. return (bCur == ' ' || bCur == '\t' || bCur == '\r' ||
  1243. bCur == '\n' || bCur == 0);
  1244. }
  1245. /*************************************************************************
  1246. * @doc INTERNAL
  1247. *
  1248. * @func LSZ PASCAL NEAR | WildCardByteCheck |
  1249. * Check for wildcard character in the string
  1250. *
  1251. * @parm LSZ | lpBuf |
  1252. * Input zero-terminated string buffer pointer
  1253. *
  1254. * @parm WORD | cbBufSize |
  1255. * Size of input string
  1256. *
  1257. * @rdesc 0 if there is no wildcard character
  1258. *************************************************************************/
  1259. PRIVATE BOOL PASCAL NEAR WildCardByteCheck (LSZ lpBuf, WORD cbBufSize)
  1260. {
  1261. while (cbBufSize > 0 && *lpBuf != WILDCARD_STAR)
  1262. {
  1263. lpBuf++;
  1264. cbBufSize--;
  1265. }
  1266. return (cbBufSize);
  1267. }