Leaked source code of windows server 2003
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.

1006 lines
32 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. /* command.c - processing command keywords. */
  3. #include "gpdparse.h"
  4. // ---- functions defined in command.c ---- //
  5. BOOL BprocessParam(
  6. IN PABSARRAYREF paarValue,
  7. IN PARRAYREF parStrValue,
  8. IN OUT PGLOBL pglobl) ;
  9. BOOL BparseCommandString(
  10. IN PABSARRAYREF paarValue,
  11. IN PARRAYREF parStrValue,
  12. IN OUT PGLOBL pglobl ) ;
  13. BOOL BconstructRPNtokenStream(
  14. IN OUT PABSARRAYREF paarValue,
  15. OUT PARRAYREF parRPNtokenStream,
  16. IN OUT PGLOBL pglobl) ;
  17. VOID VinitOperPrecedence(
  18. IN OUT PGLOBL pglobl) ;
  19. BOOL BparseArithmeticToken(
  20. IN OUT PABSARRAYREF paarValue,
  21. OUT PTOKENSTREAM ptstr,
  22. PGLOBL pglobl
  23. ) ;
  24. BOOL BparseDigits(
  25. IN OUT PABSARRAYREF paarValue,
  26. OUT PTOKENSTREAM ptstr ) ;
  27. BOOL BparseParamKeyword(
  28. IN OUT PABSARRAYREF paarValue,
  29. OUT PTOKENSTREAM ptstr,
  30. PGLOBL pglobl ) ;
  31. BOOL BcmpAARtoStr(
  32. PABSARRAYREF paarStr1,
  33. PBYTE str2) ;
  34. BOOL bDivByZeroCheck(PTOKENSTREAM ptstr) ;
  35. // ---------------------------------------------------- //
  36. BOOL BprocessParam(
  37. IN PABSARRAYREF paarValue,
  38. IN PARRAYREF parStrValue,
  39. IN OUT PGLOBL pglobl)
  40. /* notice this function will append the substring
  41. "%dddd" (without the double quotes) onto the existing
  42. parStrValue. To allow the control module to unambiguously
  43. parse out this substring, The GPD spec requires any occurance
  44. of the character '%' within an Invocation String must be
  45. preceeded by another '%'. So the control module will find two
  46. outcomes when encountering a % char in an invocation string:
  47. it is followed by another % char in which case only one % should
  48. be emitted. It is followed by exactly 4 decimal digits. This
  49. specifies the parameter index and specifies the parameter should be
  50. emitted at this point instead of the "%dddd". Additional digits
  51. are part of the invocation string and should be emitted.
  52. */
  53. {
  54. PSTR pstrDelimiters = "dDcClmqgnvf" ;
  55. PPARAMETER pparamRoot, pparam ;
  56. DWORD dwParamIndex, dwDelIndex ;
  57. ABSARRAYREF aarToken ;
  58. if(! BeatDelimiter(paarValue, "%"))
  59. return(FALSE) ; // BUG_BUG paranoid, inconsistency error.
  60. if(!BdelimitToken(paarValue, pstrDelimiters, &aarToken, &dwDelIndex) )
  61. {
  62. ERR(("Command parameter: Missing format character.\n")) ;
  63. return(FALSE) ;
  64. }
  65. pparamRoot = (PPARAMETER) gMasterTable[MTI_PARAMETER].pubStruct ;
  66. if(!BallocElementFromMasterTable(MTI_PARAMETER, &dwParamIndex, pglobl) )
  67. return(FALSE) ; // try again with more resources.
  68. pparam = pparamRoot + dwParamIndex ;
  69. pparam->dwFlags = 0 ;
  70. pparam->dwFormat = (DWORD)(BYTE)pstrDelimiters[dwDelIndex] ;
  71. (VOID) BeatLeadingWhiteSpaces(&aarToken) ;
  72. if(aarToken.dw)
  73. {
  74. if(pstrDelimiters[dwDelIndex] == 'd' || pstrDelimiters[dwDelIndex] == 'D')
  75. {
  76. if(!BparseInteger(&aarToken, &pparam->dwDigits, VALUE_INTEGER, pglobl) )
  77. {
  78. ERR(("Command parameter: Syntax error in format width field.\n"));
  79. return(FALSE) ;
  80. }
  81. if(pparam->dwDigits != WILDCARD_VALUE)
  82. pparam->dwFlags |= PARAM_FLAG_FIELDWIDTH_USED ;
  83. }
  84. else
  85. {
  86. ERR(("Command parameter: Unexpected chars found preceeding format specifier: '%0.*s'.\n",
  87. aarToken.dw, aarToken.pub));
  88. return(FALSE) ;
  89. }
  90. }
  91. // its ok to omit the fieldwidth specification
  92. // now look for optional 'Limits' construct
  93. pstrDelimiters = "[{" ;
  94. if(!BdelimitToken(paarValue, pstrDelimiters, &aarToken, &dwDelIndex) )
  95. {
  96. ERR(("Command parameter: missing '{' in Value construct.\n"));
  97. return(FALSE) ;
  98. }
  99. (VOID) BeatLeadingWhiteSpaces(&aarToken) ;
  100. if(aarToken.dw)
  101. {
  102. ERR(("Command parameter: unexpected chars found preceeding Limits or Value construct: '%0.*s'.\n",
  103. aarToken.dw, aarToken.pub));
  104. return(FALSE) ;
  105. }
  106. if(pstrDelimiters[dwDelIndex] == '[')
  107. {
  108. if(!BdelimitToken(paarValue, ",", &aarToken, &dwDelIndex) )
  109. {
  110. ERR(("Command parameter: missing comma delimiter in Limits construct.\n"));
  111. return(FALSE) ;
  112. }
  113. if(!BparseInteger(&aarToken, &pparam->lMin, VALUE_INTEGER, pglobl) )
  114. {
  115. ERR(("Command parameter: syntax error in Min Limit field.\n"));
  116. return(FALSE) ;
  117. }
  118. if(pparam->lMin != WILDCARD_VALUE)
  119. pparam->dwFlags |= PARAM_FLAG_MIN_USED ;
  120. if(!BdelimitToken(paarValue, "]", &aarToken, &dwDelIndex) )
  121. {
  122. ERR(("Command parameter: missing closing bracket in Limits construct.\n"));
  123. return(FALSE) ;
  124. }
  125. if(!BparseInteger(&aarToken, &pparam->lMax, VALUE_INTEGER, pglobl) )
  126. {
  127. ERR(("Command parameter: syntax error in Max Limit field.\n"));
  128. return(FALSE) ;
  129. }
  130. if(pparam->lMax != WILDCARD_VALUE)
  131. pparam->dwFlags |= PARAM_FLAG_MAX_USED ;
  132. if(! BeatDelimiter(paarValue, "{"))
  133. {
  134. ERR(("Command parameter: missing required '{' in Value construct.\n"));
  135. return(FALSE) ;
  136. }
  137. }
  138. // now back to parsing Value construct.
  139. if(!BconstructRPNtokenStream(paarValue, &pparam->arTokens, pglobl) )
  140. return(FALSE) ;
  141. // convert dwParamIndex to 4 digit ascii string of
  142. // the form "%dddd"
  143. {
  144. BYTE aub[6] ; // temp array of unsigned bytes
  145. DWORD dwI, dwNdigits = 4 ; // 4 is number of digits in string
  146. ARRAYREF arTmpDest ; // write result here first.
  147. // the most significant digit has the smaller byte address!
  148. aub[0] = '%' ;
  149. aub[dwNdigits + 1] = '\0' ; // null terminate, but not needed.
  150. for(dwI = dwNdigits ; dwI ; dwI--)
  151. {
  152. aub[dwI] = (BYTE)('0' + dwParamIndex % 10) ;
  153. dwParamIndex /= 10 ;
  154. }
  155. // write "%dddd" out to heap. note if parStrValue->dwCount
  156. // is zero, we must initialize parStrValue from
  157. // scratch instead of appending. Must use an Alignment of 1
  158. // to avoid creating gaps in the command string.
  159. if(!BwriteToHeap(&arTmpDest.loOffset, aub, dwNdigits + 1, 1, pglobl))
  160. return(FALSE) ;
  161. // append this run to existing string
  162. if(!parStrValue->dwCount) // no prevs string exists
  163. {
  164. parStrValue->loOffset = arTmpDest.loOffset ;
  165. }
  166. else
  167. {
  168. // BUG_BUG paranoid: may check that string is contiguous
  169. // parStrValue->loOffset + parStrValue->dwCount
  170. // should equal arTmpDest.loOffset
  171. ASSERT(parStrValue->loOffset + parStrValue->dwCount == arTmpDest.loOffset) ;
  172. }
  173. parStrValue->dwCount += dwNdigits + 1 ;
  174. }
  175. return(TRUE) ;
  176. }
  177. BOOL BparseCommandString(
  178. IN PABSARRAYREF paarValue,
  179. IN PARRAYREF parStrValue,
  180. IN OUT PGLOBL pglobl
  181. )
  182. {
  183. ABSARRAYREF aarToken ; // points to individual string segment.
  184. DWORD dwDelIndex, dwNumParams = 0 ; // dummy
  185. parStrValue->dwCount = 0 ; // tells functions to create new string
  186. // in heap. Don't append to existing string.
  187. (VOID) BeatLeadingWhiteSpaces(paarValue) ;
  188. while(paarValue->dw)
  189. {
  190. if(paarValue->pub[0] == '%')
  191. {
  192. if(++dwNumParams > 14)
  193. {
  194. ERR(("CmdInvocation: Unidrv imposes a max limit of 14 parameters per command.\n"));
  195. return(FALSE) ; // Black Death.
  196. }
  197. if(!BprocessParam(paarValue, parStrValue, pglobl) )
  198. // deposits a little turd on the stringheap,
  199. // modifies parStrValue accordingly and
  200. // initializes one parameter array element.
  201. {
  202. return(FALSE) ; // Black Death.
  203. }
  204. }
  205. else if(paarValue->pub[0] == '"')
  206. {
  207. paarValue->pub++ ;
  208. paarValue->dw-- ; // skip the initial quote.
  209. if(!BdelimitToken(paarValue, "\"", &aarToken, &dwDelIndex) )
  210. {
  211. ERR(("CmdInvocation: missing terminating '\"' in command string.\n"));
  212. return(FALSE) ;
  213. }
  214. if(!BparseStrSegment(&aarToken, parStrValue, pglobl))
  215. {
  216. return(FALSE) ;
  217. }
  218. }
  219. else
  220. {
  221. ERR(("CmdInvocation: command string segment must begin with '\"' or '%'.\n"));
  222. return(FALSE) ;
  223. }
  224. (VOID) BeatLeadingWhiteSpaces(paarValue) ;
  225. }
  226. // We don't want null terminations to occur between parameter
  227. // portion of a string. We just want to blindly add the NULL
  228. // when parsing is really finished.
  229. {
  230. DWORD dwDummy ; // don't care about result
  231. if(!BwriteToHeap(&dwDummy, "\0", 1, 1, pglobl) ) // add Null termination
  232. return(FALSE) ;
  233. }
  234. return(TRUE) ;
  235. }
  236. BOOL BconstructRPNtokenStream(
  237. IN OUT PABSARRAYREF paarValue,
  238. OUT PARRAYREF parRPNtokenStream,
  239. IN OUT PGLOBL pglobl)
  240. /* the input stream is parsed into tokens,
  241. each token is assigned an operator value see OPERATOR
  242. enum.
  243. The Control module interprets the tokenstream just as
  244. Reverse Polish Notation.
  245. In the Control Module, each token is processed as follows:
  246. OP_INTEGER: take dwValue and copy to stack
  247. OP_VARI_INDEX: dwValue is an index identifying the
  248. Unidrv Standard Variable whose current value
  249. should be placed on the stack.
  250. OP_MIN :
  251. OP_MAX :
  252. pop the top 2 values from the stack and push the
  253. smaller or larger value back onto the stack.
  254. OP_ADD :
  255. OP_SUB :
  256. OP_MULT :
  257. OP_DIV :
  258. OP_MOD :
  259. pop the top 2 values from the stack perform the
  260. indicated arithmetic operation, and push the
  261. result back onto the stack. The topmost value in the stack
  262. should be used as the 2nd operand in the operation.
  263. OP_NEG : pop the top value from the stack and push its
  264. negative back on the stack.
  265. OP_MAX_REPEAT : this should always be the last command
  266. executed. It means take the top value on the stack and
  267. if it exceeds the MaxValue specified in lMax, emit the
  268. command invocation multiple times with this parameter no
  269. larger than lMax so that the sum equals the value on
  270. the stack.
  271. OP_HALT: If the value on the stack has not yet been emitted
  272. due to processing the OP_MAX_REPEAT operator,
  273. emit this value or the closest of lMin or lMax if specified.
  274. processing for this parameter is now complete.
  275. In the parser, each token is processed as follows:
  276. OP_INTEGER, OP_VARI_INDEX: these values are
  277. placed directly into the RPNtokenStream in the order
  278. they were encountered. They cause the valueToken count to
  279. be incremented.
  280. Then 2nd group of tokens from OP_MIN to OP_HALT
  281. are first placed into an operator queue (or is it a stack?)
  282. They are inserted into the queue in the order in which
  283. they are encountered, and when they leave the queue, they
  284. are inserted into the RPNtokenStream. There are the rules
  285. governing when an operator token may leave the queue:
  286. a) each token is assigned a precedence level.
  287. here they are arranged from highest to lowest.
  288. OP_NEG a unary operator
  289. OP_MULT, OP_DIV, OP_MOD have the same level
  290. OP_ADD, OP_SUB have the same level
  291. OP_MIN, OP_MAX, OP_MAX_REPEAT have the same level
  292. but are always preceeded by an OP_OPENPAR token.
  293. OP_HALT has the lowest level. (it is generated when
  294. the closing } is encountered.)
  295. OP_CLOSEPAR and OP_OPENPAR have even lower
  296. precedences for firewall purposes.
  297. b) no token may leave the queue unless a token with equal
  298. or lower precedence has just arrived. At that point
  299. the token immediately adjacent to the new arrival
  300. leaves. If the next token is also of higher or equal
  301. precedence than the new arrival, it too leaves.
  302. The tokens depart until this condition is false.
  303. c) the OP_HALT token is not only able to allow all other
  304. tokens to leave the queue, but after that, it too
  305. leaves the queue and enters the tokenStream.
  306. d) the OP_OPENPAR and OP_CLOSEPAR tokens are different.
  307. They will never be inserted into the RPNtokenStream, but
  308. only serve to modify the departure rules governing the
  309. queue.
  310. e) OP_OPENPAR acts to freeze all operators
  311. already in the queue. Those operators cannot
  312. exit the queue until the OP_OPENPAR has been removed
  313. from the queue. All subsequent operators entering
  314. the queue are unaffected by OP_OPENPAR already residing
  315. within the queue.
  316. f) the OP_CLOSEPAR acts to flush all operators in the
  317. queue up to the first encountered OP_OPENPAR.
  318. Upon meeting its counterpart, both vanish from the
  319. queue never to be seen again.
  320. The max_repeat operator if used, must be first token parsed.
  321. Obviously it can appear only once in an expression.
  322. Token counting , unary operator detection and syntax
  323. error detection:
  324. for every binary operator parsed the operator count is
  325. incremented. Immediately after incrementing, the operator count
  326. should always be equal to the valueToken count. An excess in
  327. the operator count of one may indicate the operator is
  328. a unary operator. Any deficit or excess greater than 2 indicates
  329. a syntax error. Immediately after incrementing, the valueToken
  330. count should always be one greater than the operator count.
  331. For the purposes of the operator count, the following tokens
  332. qualify:
  333. OP_ADD, OP_SUB, OP_MULT,
  334. OP_DIV, OP_MOD, OP_COMMA
  335. Note: for function operators the function name and opening
  336. parenthesis is parsed as one object.
  337. */
  338. {
  339. TOKENSTREAM tstrNode ;
  340. BOOL bStatus ;
  341. DWORD dwValueToken = 0 , // number of value tokens parsed.
  342. dwOperatorToken = 0 , // number of binary operators parsed.
  343. dwQueuePtr = 0 , // current position in the tmp operator queue.
  344. dwCurToken, // index of curToken.
  345. dwQueueSize ; // num elements in array
  346. PDWORD pdwQueueRoot ; // Points to root of queue.
  347. PTOKENSTREAM ptstr, ptstrRoot ;
  348. pdwQueueRoot = (PDWORD) gMasterTable[MTI_OP_QUEUE].pubStruct ;
  349. dwQueueSize = gMasterTable[MTI_OP_QUEUE].dwArraySize ;
  350. ptstrRoot = (PTOKENSTREAM) gMasterTable[MTI_TOKENSTREAM].pubStruct ;
  351. parRPNtokenStream->loOffset = gMasterTable[MTI_TOKENSTREAM].dwCurIndex ;
  352. parRPNtokenStream->dwCount = 0 ;
  353. while(bStatus = BparseArithmeticToken(paarValue, &tstrNode, pglobl))
  354. {
  355. switch(tstrNode.eType)
  356. {
  357. case OP_INTEGER :
  358. case OP_VARI_INDEX :
  359. {
  360. if(dwOperatorToken != dwValueToken)
  361. {
  362. ERR(("Command parameter: arithmetic syntax error in value construct.\n"));
  363. bStatus = FALSE ;
  364. }
  365. dwValueToken++ ;
  366. break ;
  367. }
  368. case OP_ADD :
  369. case OP_SUB :
  370. case OP_MULT :
  371. case OP_DIV :
  372. case OP_MOD :
  373. case OP_COMMA :
  374. {
  375. dwOperatorToken++ ;
  376. if(dwOperatorToken == dwValueToken)
  377. break ;
  378. else if(dwOperatorToken == dwValueToken + 1)
  379. {
  380. // maybe interpret operator as Unary.
  381. if(tstrNode.eType == OP_SUB)
  382. {
  383. tstrNode.eType = OP_NEG ;
  384. dwOperatorToken-- ;
  385. break ;
  386. }
  387. else if(tstrNode.eType == OP_ADD)
  388. {
  389. tstrNode.eType = OP_NULL ;
  390. dwOperatorToken-- ;
  391. break ;
  392. }
  393. }
  394. ERR(("Command parameter: arithmetic syntax error in value construct.\n"));
  395. bStatus = FALSE ;
  396. break ;
  397. }
  398. case OP_MIN :
  399. case OP_MAX :
  400. case OP_OPENPAR :
  401. {
  402. if(dwValueToken != dwOperatorToken)
  403. {
  404. ERR(("Command parameter: arithmetic syntax error in value construct.\n"));
  405. bStatus = FALSE ;
  406. }
  407. break ;
  408. }
  409. case OP_CLOSEPAR :
  410. case OP_HALT :
  411. {
  412. if(dwValueToken != dwOperatorToken + 1)
  413. {
  414. ERR(("Command parameter: arithmetic syntax error in value construct.\n"));
  415. bStatus = FALSE ;
  416. }
  417. break ;
  418. }
  419. case OP_MAX_REPEAT :
  420. {
  421. if(dwValueToken || dwOperatorToken || dwQueuePtr)
  422. {
  423. ERR(("Command parameter: syntax error in value construct.\n"));
  424. ERR((" OP_MAX_REPEAT must appear as the outermost operator only.\n"));
  425. bStatus = FALSE ;
  426. }
  427. break ;
  428. }
  429. default:
  430. {
  431. break ;
  432. }
  433. }
  434. if(!bStatus )
  435. break ;
  436. switch(tstrNode.eType)
  437. {
  438. case OP_INTEGER :
  439. case OP_VARI_INDEX :
  440. {
  441. bStatus = BallocElementFromMasterTable(
  442. MTI_TOKENSTREAM, &dwCurToken, pglobl) ;
  443. if(!bStatus )
  444. break ;
  445. parRPNtokenStream->dwCount++ ;
  446. ptstr = ptstrRoot + dwCurToken ;
  447. ptstr->eType = tstrNode.eType ;
  448. ptstr->dwValue = tstrNode.dwValue ;
  449. break ;
  450. }
  451. case OP_ADD :
  452. case OP_SUB :
  453. case OP_MULT :
  454. case OP_DIV :
  455. case OP_MOD :
  456. case OP_NEG :
  457. {
  458. while (dwQueuePtr &&
  459. (gdwOperPrecedence[tstrNode.eType] <=
  460. gdwOperPrecedence[*(pdwQueueRoot + dwQueuePtr - 1)]) )
  461. {
  462. bStatus = BallocElementFromMasterTable(
  463. MTI_TOKENSTREAM, &dwCurToken, pglobl) ;
  464. if(!bStatus )
  465. break ;
  466. parRPNtokenStream->dwCount++ ;
  467. ptstr = ptstrRoot + dwCurToken ;
  468. ptstr->eType = *(pdwQueueRoot + dwQueuePtr - 1) ;
  469. ptstr->dwValue = 0 ; // undefined
  470. dwQueuePtr-- ; // pop off the Queue.
  471. bDivByZeroCheck(ptstr);
  472. }
  473. if(dwQueuePtr >= dwQueueSize)
  474. bStatus = FALSE ; // Queue overflow
  475. if(!bStatus )
  476. break ;
  477. //add current Oper to Queue
  478. *(pdwQueueRoot + dwQueuePtr) = tstrNode.eType ;
  479. dwQueuePtr++ ;
  480. break ;
  481. }
  482. case OP_MIN :
  483. case OP_MAX :
  484. case OP_MAX_REPEAT :
  485. case OP_OPENPAR :
  486. {
  487. if(dwQueuePtr + 1 >= dwQueueSize) // room for two?
  488. bStatus = FALSE ; // Queue overflow
  489. if(!bStatus )
  490. break ;
  491. //add OP_OPENPAR to Queue
  492. *(pdwQueueRoot + dwQueuePtr) = OP_OPENPAR ;
  493. dwQueuePtr++ ;
  494. //add current Oper to Queue
  495. if(tstrNode.eType != OP_OPENPAR)
  496. {
  497. *(pdwQueueRoot + dwQueuePtr) = tstrNode.eType ;
  498. dwQueuePtr++ ;
  499. }
  500. break ;
  501. }
  502. case OP_CLOSEPAR :
  503. case OP_COMMA :
  504. case OP_HALT :
  505. {
  506. while (dwQueuePtr &&
  507. (gdwOperPrecedence[tstrNode.eType] <=
  508. gdwOperPrecedence[*(pdwQueueRoot + dwQueuePtr - 1)]) )
  509. {
  510. bStatus = BallocElementFromMasterTable(
  511. MTI_TOKENSTREAM, &dwCurToken, pglobl) ;
  512. if(!bStatus )
  513. break ;
  514. parRPNtokenStream->dwCount++ ;
  515. ptstr = ptstrRoot + dwCurToken ;
  516. ptstr->eType = *(pdwQueueRoot + dwQueuePtr - 1) ;
  517. ptstr->dwValue = 0 ; // undefined
  518. dwQueuePtr-- ; // pop off the Queue.
  519. bDivByZeroCheck(ptstr);
  520. }
  521. if(!bStatus )
  522. break ;
  523. if(tstrNode.eType == OP_COMMA)
  524. {
  525. // comma clears all operators in queue up to the
  526. // function. This completes processing of the
  527. // first argument. The comma effectively converts
  528. // the syntax funct(A + B, C * D) into
  529. // ((A + B) funct (C * D))
  530. // check to see if function operator is now at
  531. // top of queue.
  532. if(dwQueuePtr)
  533. {
  534. OPERATOR eType ;
  535. eType = *(pdwQueueRoot + dwQueuePtr - 1) ;
  536. if(eType == OP_MIN || eType == OP_MAX)
  537. break ;
  538. }
  539. ERR(("Command parameter: syntax error in value construct.\n"));
  540. ERR((" comma used outside of function argument list.\n"));
  541. bStatus = FALSE ;
  542. }
  543. else if(tstrNode.eType == OP_HALT)
  544. {
  545. if(dwQueuePtr)
  546. {
  547. ERR(("Command parameter: syntax error in value construct - unmatched OP_OPENPAR.\n"));
  548. bStatus = FALSE ;
  549. break ;
  550. }
  551. // now add halt operator to tokenStream
  552. bStatus = BallocElementFromMasterTable(
  553. MTI_TOKENSTREAM, &dwCurToken, pglobl) ;
  554. if(!bStatus )
  555. break ;
  556. parRPNtokenStream->dwCount++ ;
  557. ptstr = ptstrRoot + dwCurToken ;
  558. ptstr->eType = OP_HALT ;
  559. ptstr->dwValue = 0 ; // undefined
  560. }
  561. else if(dwQueuePtr &&
  562. (*(pdwQueueRoot + dwQueuePtr - 1) == OP_OPENPAR))
  563. {
  564. dwQueuePtr-- ; // pop OP_OPENPAR off the Queue.
  565. // conjugate pairs meet and annihilate
  566. }
  567. else
  568. {
  569. ERR(("Command parameter: syntax error in value construct - unmatched OP_CLOSEPAR.\n"));
  570. bStatus = FALSE ;
  571. }
  572. break ;
  573. }
  574. default:
  575. break ;
  576. }
  577. if(!bStatus || tstrNode.eType == OP_HALT)
  578. break ;
  579. }
  580. if(!bStatus )
  581. {
  582. parRPNtokenStream->dwCount = 0 ;
  583. parRPNtokenStream->loOffset = 0 ;
  584. }
  585. return(bStatus);
  586. }
  587. VOID VinitOperPrecedence(
  588. IN OUT PGLOBL pglobl)
  589. {
  590. DWORD dwP ; // precedence level ;
  591. dwP = 0 ; // lowest level. fire wall.
  592. gdwOperPrecedence[OP_OPENPAR] = dwP ;
  593. dwP++ ;
  594. gdwOperPrecedence[OP_CLOSEPAR] = dwP ;
  595. dwP++ ;
  596. gdwOperPrecedence[OP_HALT] = dwP ;
  597. dwP++ ;
  598. gdwOperPrecedence[OP_MIN] = gdwOperPrecedence[OP_MAX] =
  599. gdwOperPrecedence[OP_MAX_REPEAT] = dwP ;
  600. dwP++ ; // comma must be next to function operators
  601. gdwOperPrecedence[OP_COMMA] = dwP ; // in precedence level.
  602. dwP++ ;
  603. gdwOperPrecedence[OP_ADD] = gdwOperPrecedence[OP_SUB] = dwP ;
  604. dwP++ ;
  605. gdwOperPrecedence[OP_MULT] = gdwOperPrecedence[OP_DIV] =
  606. gdwOperPrecedence[OP_MOD] = dwP ;
  607. dwP++ ;
  608. gdwOperPrecedence[OP_NEG] = dwP ;
  609. }
  610. BOOL BparseArithmeticToken(
  611. IN OUT PABSARRAYREF paarValue,
  612. OUT PTOKENSTREAM ptstr,
  613. PGLOBL pglobl
  614. )
  615. /* objects to parse:
  616. OP_INTEGER : string of digits delimited by non-Keyword char
  617. OP_MIN : "min" and adjacent '('
  618. OP_MAX : "max" and adjacent '('
  619. OP_MAX_REPEAT : "max_repeat" and adjacent '('
  620. OP_MOD : "MOD"
  621. OP_VARI_INDEX : string of Keyword chars deliniated by non Keyword
  622. chars, not starting with a digit. and not one of the recognized
  623. operator keywords.
  624. Note all of the above string objects must be delinated by
  625. non-Keyword chars.
  626. OP_ADD : '+'
  627. OP_SUB : '-'
  628. OP_MULT : '*'
  629. OP_DIV : '/'
  630. OP_HALT : '}'
  631. OP_OPENPAR : '('
  632. OP_CLOSEPAR : ')'
  633. OP_COMMA : ','
  634. */
  635. {
  636. BYTE ubSrc ;
  637. BOOL bStatus ;
  638. if(!paarValue->dw)
  639. return(FALSE); // nothing left!
  640. (VOID) BeatLeadingWhiteSpaces(paarValue) ;
  641. switch(ubSrc = *paarValue->pub)
  642. {
  643. case '+':
  644. {
  645. ptstr->eType = OP_ADD ;
  646. break ;
  647. }
  648. case '-':
  649. {
  650. ptstr->eType = OP_SUB ;
  651. break ;
  652. }
  653. case '*':
  654. {
  655. ptstr->eType = OP_MULT ;
  656. break ;
  657. }
  658. case '/':
  659. {
  660. ptstr->eType = OP_DIV ;
  661. break ;
  662. }
  663. case '}':
  664. {
  665. ptstr->eType = OP_HALT ;
  666. break ;
  667. }
  668. case '(':
  669. {
  670. ptstr->eType = OP_OPENPAR ;
  671. break ;
  672. }
  673. case ')':
  674. {
  675. ptstr->eType = OP_CLOSEPAR ;
  676. break ;
  677. }
  678. case ',':
  679. {
  680. ptstr->eType = OP_COMMA ;
  681. break ;
  682. }
  683. default:
  684. {
  685. bStatus = FALSE ; // till proven otherwise.
  686. if(ubSrc >= '0' && ubSrc <= '9')
  687. {
  688. bStatus = BparseDigits(paarValue, ptstr) ;
  689. }
  690. else if( (ubSrc >= 'a' && ubSrc <= 'z') ||
  691. (ubSrc >= 'A' && ubSrc <= 'Z') ||
  692. ubSrc == '_' || ubSrc == '?')
  693. {
  694. bStatus = BparseParamKeyword(paarValue, ptstr, pglobl) ;
  695. }
  696. return(bStatus);
  697. }
  698. }
  699. paarValue->pub++ ;
  700. paarValue->dw-- ;
  701. return(TRUE);
  702. }
  703. #define pubM (paarValue->pub)
  704. #define dwM (paarValue->dw)
  705. BOOL BparseDigits(
  706. IN OUT PABSARRAYREF paarValue,
  707. OUT PTOKENSTREAM ptstr )
  708. /* paarValue left pointing after last digit upon exit.
  709. there may in fact be nothing left in paarValue */
  710. {
  711. DWORD dwNumber = 0 ;
  712. BOOL bStatus = FALSE ;
  713. if(*pubM == '0') // leading zero indicates hexadecimal format
  714. {
  715. pubM++ ;
  716. dwM-- ;
  717. if(dwM && (*pubM == 'x' || *pubM == 'X'))
  718. {
  719. pubM++ ;
  720. dwM-- ;
  721. }
  722. else
  723. {
  724. bStatus = TRUE ;
  725. goto EndNumber ; // ignore leading zero and attempt to parse more digits.
  726. }
  727. if(!dwM)
  728. {
  729. ERR(("Command Param-BparseDigits: no digits found in Hex value.\n"));
  730. return(FALSE);
  731. }
  732. for( ; dwM ; pubM++, dwM-- )
  733. {
  734. if(*pubM >= '0' && *pubM <= '9')
  735. {
  736. dwNumber *= 0x10 ;
  737. dwNumber += (*pubM - '0') ;
  738. }
  739. else if(*pubM >= 'a' && *pubM <= 'f')
  740. {
  741. dwNumber *= 0x10 ;
  742. dwNumber += (*pubM - 'a' + 0x0a) ;
  743. }
  744. else if(*pubM >= 'A' && *pubM <= 'F')
  745. {
  746. dwNumber *= 0x10 ;
  747. dwNumber += (*pubM - 'A' + 0x0a) ;
  748. }
  749. else
  750. break;
  751. bStatus = TRUE ;
  752. }
  753. }
  754. EndNumber:
  755. for( ; dwM && *pubM >= '0' && *pubM <= '9' ; )
  756. {
  757. dwNumber *= 10 ;
  758. dwNumber += (*pubM - '0') ;
  759. pubM++ ;
  760. dwM-- ;
  761. bStatus = TRUE ;
  762. }
  763. if( dwM && ((*pubM >= 'a' && *pubM <= 'z') ||
  764. (*pubM >= 'A' && *pubM <= 'Z') ||
  765. *pubM == '_' || *pubM == '?'))
  766. {
  767. ERR(("Command parameter: syntax error in value construct.\n"));
  768. ERR((" integer not clearly delimited using non Keyword characters.\n"));
  769. return(FALSE);
  770. }
  771. ptstr->eType = OP_INTEGER ;
  772. ptstr->dwValue = dwNumber ;
  773. return(bStatus);
  774. }
  775. BOOL BparseParamKeyword(
  776. IN OUT PABSARRAYREF paarValue,
  777. OUT PTOKENSTREAM ptstr,
  778. PGLOBL pglobl )
  779. /* if the keyword happens to be a function operator,
  780. find and eat the opening '('. If keyword is
  781. a Standard Variable, determine its index value
  782. and store in dwValue.
  783. */
  784. {
  785. BOOL bStatus = FALSE ;
  786. ABSARRAYREF aarKey ;
  787. aarKey.pub = pubM ; // start of Keyword
  788. for(aarKey.dw = 0 ; dwM ; aarKey.dw++)
  789. {
  790. if( (*pubM >= 'a' && *pubM <= 'z') ||
  791. (*pubM >= 'A' && *pubM <= 'Z') ||
  792. (*pubM >= '0' && *pubM <= '9') ||
  793. *pubM == '_' || *pubM == '?' )
  794. {
  795. pubM++ ;
  796. dwM-- ;
  797. bStatus = TRUE ;
  798. }
  799. else
  800. break ;
  801. }
  802. // now identify this Keyword
  803. if(!bStatus)
  804. return(bStatus);
  805. if(BcmpAARtoStr(&aarKey, "min"))
  806. {
  807. ptstr->eType = OP_MIN ;
  808. bStatus = BeatDelimiter(paarValue, "(") ;
  809. if(!bStatus)
  810. {
  811. ERR(("Command parameter: '(' must follow 'min' operator.\n"));
  812. }
  813. }
  814. else if(BcmpAARtoStr(&aarKey, "max"))
  815. {
  816. ptstr->eType = OP_MAX ;
  817. bStatus = BeatDelimiter(paarValue, "(") ;
  818. if(!bStatus)
  819. {
  820. ERR(("Command parameter: '(' must follow 'max' operator.\n"));
  821. }
  822. }
  823. else if(BcmpAARtoStr(&aarKey, "max_repeat"))
  824. {
  825. ptstr->eType = OP_MAX_REPEAT ;
  826. bStatus = BeatDelimiter(paarValue, "(") ;
  827. if(!bStatus)
  828. {
  829. ERR(("Command parameter: '(' must follow 'max_repeat' operator.\n"));
  830. }
  831. }
  832. else if(BcmpAARtoStr(&aarKey, "MOD"))
  833. {
  834. ptstr->eType = OP_MOD ;
  835. // don't eat any delimiters
  836. }
  837. else // must therefore be the name of a StandardValue
  838. {
  839. ptstr->eType = OP_VARI_INDEX ;
  840. bStatus = BparseConstant(&aarKey, &ptstr->dwValue,
  841. VALUE_CONSTANT_STANDARD_VARS, pglobl) ;
  842. }
  843. return(bStatus);
  844. }
  845. #undef pubM
  846. #undef dwM
  847. BOOL BcmpAARtoStr(
  848. PABSARRAYREF paarStr1,
  849. PBYTE str2)
  850. // Compares two strings, one referenced by 'aar' the other
  851. // referenced by 'pub or str'. Returns TRUE if they match, FALSE
  852. // otherwise.
  853. {
  854. DWORD dwCnt ;
  855. dwCnt = strlen(str2) ;
  856. if(dwCnt != paarStr1->dw)
  857. return(FALSE) ; // Lengths don't even match!
  858. if(strncmp(paarStr1->pub, str2, dwCnt))
  859. return(FALSE) ;
  860. return(TRUE) ;
  861. }
  862. BOOL bDivByZeroCheck(PTOKENSTREAM ptstr)
  863. {
  864. // assumes ptstr is not pointing to start of tokenstream
  865. // else (ptstr - 1) is undefined.
  866. // this assumption is valid because the syntax checking in
  867. // BconstructRPNtokenStream counts
  868. // dwValueToken , // number of value tokens parsed.
  869. // and dwOperatorToken and ensures that dwValueToken
  870. // is non-zero when OP_DIV is parsed.
  871. if (ptstr->eType == OP_DIV && (ptstr - 1)->eType == OP_INTEGER &&
  872. (ptstr - 1)->dwValue == 0)
  873. {
  874. ERR(("Command parameter: Explicit divide by zero detected.\n"));
  875. return(FALSE) ;
  876. }
  877. return(TRUE) ;
  878. }