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.

1906 lines
54 KiB

  1. /****************************************************************************\
  2. *
  3. * PARSERAT.C -- Code to parse .RAT files
  4. *
  5. * Created: Greg Jones
  6. *
  7. \****************************************************************************/
  8. /*Includes------------------------------------------------------------------*/
  9. #include "stdafx.h"
  10. #include "cnfgprts.h"
  11. #include "parserat.h"
  12. #include <iis64.h>
  13. /****************************************************************************
  14. Some design notes on how this parser works:
  15. A ParenThing is:
  16. '(' identifier [stuff] ')'
  17. where [stuff] could be:
  18. a quoted string
  19. a number
  20. a boolean
  21. a series of ParenThings
  22. in the case of extensions:
  23. a quoted string, followed by
  24. one or more quoted strings and/or ParenThings
  25. The entire .RAT file is a ParenThing, except that it has no identifier, just
  26. a list of ParenThings inside it.
  27. **********************************************************************
  28. We pass the parser a schema for what things it expects -- we have
  29. a big array listing identifiers for each different possible keyword, and
  30. each parser call receives a smaller array containing only those indices
  31. that are valid to occur within that object.
  32. We make PicsRatingSystem, PicsCategory, and PicsEnum derive from a common
  33. base class which supports a virtual function AddItem(ID,data). So at the
  34. top level, we construct an (empty) PicsRatingSystem. We call the parser,
  35. giving it a pointer to that guy, and a structure describing what to parse --
  36. the ParenObject's token is a null string (since the global structure is the
  37. one that doesn't start with a token before its first embedded ParenThing),
  38. and we give a list saying the allowable things in a PicsRatingSystem are
  39. PICS-version, rating-system, rating-service, default, description, extension,
  40. icon, name, category. There is a global table indicating a handler function
  41. for every type of ParenThing, which knows how to create a data structure
  42. completely describing that ParenThing. (That data structure could be as
  43. simple as a number or as complex as allocating and parsing a complete
  44. PicsCategory object.)
  45. The parser walks along, and for each ParenThing he finds, he identifies it
  46. by looking up its token in the list provided by the caller. Each entry in
  47. that list should include a field which indicates whether multiple things
  48. of that identity are allowed (e.g., 'category') or not (e.g., rating-system).
  49. If only one is allowed, then when the parser finds one he marks it as having
  50. been found.
  51. When the parser identifies the ParenThing, he calls its handler function to
  52. completely parse the data in the ParenThing and return that object into an
  53. LPVOID provided by the parser. If that is successful, the parser then calls
  54. its object's AddItem(ID,data) virtual function to add the specified item to
  55. the object, relying on the object itself to know what type "data" points to --
  56. a number, a pointer to a heap string which can be given to ETS::SetTo, a
  57. pointer to a PicsCategory object which can be appended to an array, etc.
  58. The RatFileParser class exists solely to provide a line number shared by
  59. all the parsing routines. This line number is updated as the parser walks
  60. through the file, and is frozen as soon as an error is found. This line
  61. number can later be reported to the user to help localize errors in RAT files.
  62. *****************************************************************************/
  63. /*Globals-------------------------------------------------------------------*/
  64. #define EXTTEXT(n) const CHAR n[]
  65. #define TEXTCONST(name,text) EXTTEXT(name) = (text)
  66. /* Text strings used in parsing rating labels. */
  67. TEXTCONST(szNULL,"");
  68. TEXTCONST(szDoubleCRLF,"\r\n\r\n");
  69. TEXTCONST(szPicsOpening,"(PICS-");
  70. TEXTCONST(szWhitespace," \t\r\n");
  71. TEXTCONST(szExtendedAlphaNum,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-.,;:&=?!*~@#/");
  72. TEXTCONST(szSingleCharTokens,"()\"");
  73. TEXTCONST(szLeftParen,"(");
  74. TEXTCONST(szRightParen,")");
  75. TEXTCONST(szOptional,"optional");
  76. TEXTCONST(szMandatory,"mandatory");
  77. TEXTCONST(szAtOption,"at");
  78. TEXTCONST(szByOption,"by");
  79. TEXTCONST(szCommentOption,"comment");
  80. TEXTCONST(szCompleteLabelOption,"complete-label");
  81. TEXTCONST(szFullOption,"full");
  82. TEXTCONST(szExtensionOption,"extension");
  83. TEXTCONST(szGenericOption,"generic");
  84. TEXTCONST(szShortGenericOption,"gen");
  85. TEXTCONST(szForOption,"for");
  86. TEXTCONST(szMICOption,"MIC-md5");
  87. TEXTCONST(szMD5Option,"md5");
  88. TEXTCONST(szOnOption,"on");
  89. TEXTCONST(szSigOption,"signature-PKCS");
  90. TEXTCONST(szUntilOption,"until");
  91. TEXTCONST(szExpOption,"exp");
  92. TEXTCONST(szRatings,"ratings");
  93. TEXTCONST(szShortRatings,"r");
  94. TEXTCONST(szError,"error");
  95. TEXTCONST(szNoRatings,"no-ratings");
  96. TEXTCONST(szLabelWord,"labels");
  97. TEXTCONST(szShortLabelWord,"l");
  98. TEXTCONST(szShortTrue,"t");
  99. TEXTCONST(szTrue,"true");
  100. TEXTCONST(szShortFalse,"f");
  101. TEXTCONST(szFalse,"false");
  102. TEXTCONST(szNegInf,"-INF");
  103. TEXTCONST(szPosInf,"+INF");
  104. TEXTCONST(szLabel,"label");
  105. TEXTCONST(szName,"name");
  106. TEXTCONST(szValue,"value");
  107. TEXTCONST(szIcon,"icon");
  108. TEXTCONST(szDescription, "description");
  109. TEXTCONST(szCategory, "category");
  110. TEXTCONST(szTransmitAs, "transmit-as");
  111. TEXTCONST(szMin,"min");
  112. TEXTCONST(szMax,"max");
  113. TEXTCONST(szMultivalue,"multivalue");
  114. TEXTCONST(szInteger,"integer");
  115. TEXTCONST(szLabelOnly, "label-only");
  116. TEXTCONST(szPicsVersion,"PICS-version");
  117. TEXTCONST(szRatingSystem,"rating-system");
  118. TEXTCONST(szRatingService,"rating-service");
  119. TEXTCONST(szRatingBureau,"rating-bureau");
  120. TEXTCONST(szBureauRequired,"bureau-required");
  121. TEXTCONST(szDefault,"default");
  122. TEXTCONST(szMultiValue,"multivalue");
  123. TEXTCONST(szUnordered,"unordered");
  124. TEXTCONST(szRatingBureauExtension,"www.w3.org/PICS/service-extensions/label-bureau");
  125. /* define some error codes */
  126. const HRESULT RAT_E_BASE = 0x80050000; /* just a guess at a free area for internal use */
  127. const HRESULT RAT_E_EXPECTEDLEFT = RAT_E_BASE + 1; /* expected left paren */
  128. const HRESULT RAT_E_EXPECTEDRIGHT = RAT_E_BASE + 2; /* expected right paren */
  129. const HRESULT RAT_E_EXPECTEDTOKEN = RAT_E_BASE + 3; /* expected unquoted token */
  130. const HRESULT RAT_E_EXPECTEDSTRING = RAT_E_BASE + 4; /* expected quoted string */
  131. const HRESULT RAT_E_EXPECTEDNUMBER = RAT_E_BASE + 5; /* expected number */
  132. const HRESULT RAT_E_EXPECTEDBOOL = RAT_E_BASE + 6; /* expected boolean */
  133. const HRESULT RAT_E_DUPLICATEITEM = RAT_E_BASE + 7; /* AO_SINGLE item appeared twice */
  134. const HRESULT RAT_E_MISSINGITEM = RAT_E_BASE + 8; /* AO_MANDATORY item not found */
  135. const HRESULT RAT_E_UNKNOWNITEM = RAT_E_BASE + 9; /* unrecognized token */
  136. const HRESULT RAT_E_UNKNOWNMANDATORY= RAT_E_BASE + 10; /* unrecognized mandatory extension */
  137. char PicsDelimChar='/';
  138. class RatFileParser
  139. {
  140. public:
  141. UINT m_nLine;
  142. RatFileParser() { m_nLine = 1; }
  143. LPSTR EatQuotedString(LPSTR pIn);
  144. HRESULT ParseToOpening(LPSTR *ppIn, AllowableOption *paoExpected,
  145. AllowableOption **ppFound);
  146. HRESULT ParseParenthesizedObject(
  147. LPSTR *ppIn, /* where we are in the text stream */
  148. AllowableOption aao[], /* allowable things inside this object */
  149. PicsObjectBase *pObject /* object to set parameters into */
  150. );
  151. char* FindNonWhite(char *pc);
  152. };
  153. /* White returns a pointer to the first whitespace character starting at pc.
  154. */
  155. char* White(char *pc){
  156. assert(pc);
  157. while (1){
  158. if (*pc == '\0' ||
  159. *pc ==' ' ||
  160. *pc == '\t' ||
  161. *pc == '\r' ||
  162. *pc == '\n')
  163. {
  164. return pc;
  165. }
  166. pc++;
  167. }
  168. }
  169. /* NonWhite returns a pointer to the first non-whitespace character starting
  170. * at pc.
  171. */
  172. char* NonWhite(char *pc){
  173. assert(pc);
  174. while (1){
  175. if (*pc != ' ' &&
  176. *pc != '\t' &&
  177. *pc != '\r' &&
  178. *pc != '\n') /* includes null terminator */
  179. {
  180. return pc;
  181. }
  182. pc++;
  183. }
  184. }
  185. /* FindNonWhite returns a pointer to the first non-whitespace character starting
  186. * at pc.
  187. */
  188. char* RatFileParser::FindNonWhite(char *pc)
  189. {
  190. assert(pc);
  191. while (1)
  192. {
  193. if (*pc != ' ' &&
  194. *pc != '\t' &&
  195. *pc != '\r' &&
  196. *pc != '\n') /* includes null terminator */
  197. {
  198. return pc;
  199. }
  200. if (*pc == '\n')
  201. m_nLine++;
  202. pc++;
  203. }
  204. }
  205. /* AppendSlash forces pszString to end in a single slash if it doesn't
  206. * already. This may produce a technically invalid URL (for example,
  207. * "http://gregj/default.htm/", but we're only using the result for
  208. * comparisons against other paths similarly mangled.
  209. */
  210. void AppendSlash(LPSTR pszString)
  211. {
  212. LPSTR pszSlash = ::strrchrf(pszString, '/');
  213. if (pszSlash == NULL || *(pszSlash + 1) != '\0')
  214. ::strcatf(pszString, "/");
  215. }
  216. /* SkipWhitespace(&pszString)
  217. *
  218. * advances pszString past whitespace characters
  219. */
  220. void SkipWhitespace(LPSTR *ppsz)
  221. {
  222. UINT cchWhitespace = ::strspnf(*ppsz, szWhitespace);
  223. *ppsz += cchWhitespace;
  224. }
  225. /* FindTokenEnd(pszStart)
  226. *
  227. * Returns a pointer to the end of a contiguous range of similarly-typed
  228. * characters (whitespace, quote mark, punctuation, or alphanumerics).
  229. */
  230. LPSTR FindTokenEnd(LPSTR pszStart)
  231. {
  232. LPSTR pszEnd = pszStart;
  233. if (*pszEnd == '\0') {
  234. return pszEnd;
  235. }
  236. else if (strchr(szSingleCharTokens, *pszEnd)) {
  237. return ++pszEnd;
  238. }
  239. UINT cch;
  240. cch = ::strspnf(pszEnd, szWhitespace);
  241. if (cch > 0)
  242. return pszEnd + cch;
  243. cch = ::strspnf(pszEnd, szExtendedAlphaNum);
  244. if (cch > 0)
  245. return pszEnd + cch;
  246. return pszEnd; /* unrecognized characters */
  247. }
  248. /* GetBool(LPSTR *ppszToken, BOOL *pfOut)
  249. *
  250. * Parses a boolean value at the given token and returns its value in *pfOut.
  251. * Legal values are 't', 'f', 'true', and 'false'. If success, *ppszToken
  252. * is advanced past the boolean token and any following whitespace. If failure,
  253. * *ppszToken is not modified.
  254. *
  255. * pfOut may be NULL if the caller just wants to eat the token and doesn't
  256. * care about its value.
  257. */
  258. HRESULT GetBool(LPSTR *ppszToken, BOOL *pfOut)
  259. {
  260. BOOL bValue;
  261. LPSTR pszTokenEnd = FindTokenEnd(*ppszToken);
  262. if (IsEqualToken(*ppszToken, pszTokenEnd, szShortTrue) ||
  263. IsEqualToken(*ppszToken, pszTokenEnd, szTrue)) {
  264. bValue = TRUE;
  265. }
  266. else if (IsEqualToken(*ppszToken, pszTokenEnd, szShortFalse) ||
  267. IsEqualToken(*ppszToken, pszTokenEnd, szFalse)) {
  268. bValue = FALSE;
  269. }
  270. else
  271. return ResultFromScode(MK_E_SYNTAX);
  272. if (pfOut != NULL)
  273. *pfOut = bValue;
  274. *ppszToken = pszTokenEnd;
  275. SkipWhitespace(ppszToken);
  276. return NOERROR;
  277. }
  278. /* GetQuotedToken(&pszThisToken, &pszQuotedToken)
  279. *
  280. * Sets pszQuotedToken to point to the contents of the doublequotes.
  281. * pszQuotedToken may be NULL if the caller just wants to eat the token.
  282. * Sets pszThisToken to point to the first character after the closing
  283. * doublequote.
  284. * Fails if pszThisToken doesn't start with a doublequote or doesn't
  285. * contain a closing doublequote.
  286. * The closing doublequote is replaced with a null terminator, iff the
  287. * function does not fail.
  288. */
  289. HRESULT GetQuotedToken(LPSTR *ppszThisToken, LPSTR *ppszQuotedToken)
  290. {
  291. HRESULT hres = ResultFromScode(MK_E_SYNTAX);
  292. LPSTR pszStart = *ppszThisToken;
  293. if (*pszStart != '\"')
  294. return hres;
  295. pszStart++;
  296. LPSTR pszEndQuote = strchrf(pszStart, '\"');
  297. if (pszEndQuote == NULL)
  298. return hres;
  299. *pszEndQuote = '\0';
  300. if (ppszQuotedToken != NULL)
  301. *ppszQuotedToken = pszStart;
  302. *ppszThisToken = pszEndQuote+1;
  303. return NOERROR;
  304. }
  305. BOOL IsEqualToken(LPCSTR pszTokenStart, LPCSTR pszTokenEnd, LPCSTR pszTokenToMatch)
  306. {
  307. UINT cbToken = strlenf(pszTokenToMatch);
  308. if (cbToken != (UINT)(pszTokenEnd - pszTokenStart) || strnicmpf(pszTokenStart, pszTokenToMatch, cbToken))
  309. return FALSE;
  310. return TRUE;
  311. }
  312. /* ParseLiteralToken(ppsz, pszToken) tries to match *ppsz against pszToken.
  313. * If they don't match, an error is returned. If they do match, then *ppsz
  314. * is advanced past the token and any following whitespace.
  315. *
  316. * If ppszInvalid is NULL, then the function is non-destructive in the error
  317. * path, so it's OK to call ParseLiteralToken just to see if a possible literal
  318. * token is what's next; if the token isn't found, whatever was there didn't
  319. * get eaten or anything.
  320. *
  321. * If ppszInvalid is not NULL, then if the token doesn't match, *ppszInvalid
  322. * will be set to *ppsz.
  323. */
  324. HRESULT ParseLiteralToken(LPSTR *ppsz, LPCSTR pszToken, LPCSTR *ppszInvalid)
  325. {
  326. LPSTR pszTokenEnd = FindTokenEnd(*ppsz);
  327. if (!IsEqualToken(*ppsz, pszTokenEnd, pszToken)) {
  328. if (ppszInvalid != NULL)
  329. *ppszInvalid = *ppsz;
  330. return ResultFromScode(MK_E_SYNTAX);
  331. }
  332. *ppsz = pszTokenEnd;
  333. SkipWhitespace(ppsz);
  334. return NOERROR;
  335. }
  336. /* ParseNumber parses a numeric token at the specified position. If the
  337. * number makes sense, the pointer is advanced to the end of the number
  338. * and past any following whitespace, and the numeric value is returned
  339. * in *pnOut. Any non-numeric characters are considered to terminate the
  340. * number without error; it is assumed that higher-level parsing code
  341. * will eventually reject such characters if they're not supposed to be
  342. * there.
  343. *
  344. * pnOut may be NULL if the caller doesn't care about the number being
  345. * returned and just wants to eat it.
  346. *
  347. * Floating point numbers of the form nnn.nnn are rounded to the next
  348. * higher integer and returned as such.
  349. */
  350. HRESULT ParseNumber(LPSTR *ppszNumber, INT *pnOut)
  351. {
  352. HRESULT hres = ResultFromScode(MK_E_SYNTAX);
  353. BOOL fNegative = FALSE;
  354. INT nAccum = 0;
  355. BOOL fNonZeroDecimal = FALSE;
  356. BOOL fInDecimal = FALSE;
  357. BOOL fFoundDigits = FALSE;
  358. LPSTR pszCurrent = *ppszNumber;
  359. /* Handle one sign character. */
  360. if (*pszCurrent == '+') {
  361. pszCurrent++;
  362. }
  363. else if (*pszCurrent == '-') {
  364. pszCurrent++;
  365. fNegative = TRUE;
  366. }
  367. for (;;) {
  368. if (*pszCurrent == '.') {
  369. fInDecimal = TRUE;
  370. }
  371. else if (*pszCurrent >= '0' && *pszCurrent <= '9') {
  372. fFoundDigits = TRUE;
  373. if (fInDecimal) {
  374. if (*pszCurrent > '0') {
  375. fNonZeroDecimal = TRUE;
  376. }
  377. }
  378. else {
  379. nAccum = nAccum * 10 + (*pszCurrent - '0');
  380. }
  381. }
  382. else
  383. break;
  384. pszCurrent++;
  385. }
  386. if (fFoundDigits) {
  387. hres = NOERROR;
  388. if (fNonZeroDecimal)
  389. nAccum++; /* round away from zero if decimal present */
  390. if (fNegative)
  391. nAccum = -nAccum;
  392. }
  393. if (SUCCEEDED(hres)) {
  394. if (pnOut != NULL)
  395. *pnOut = nAccum;
  396. *ppszNumber = pszCurrent;
  397. SkipWhitespace(ppszNumber);
  398. }
  399. return hres;
  400. }
  401. HRESULT ParsePseudoFloat(LPSTR *ppszNumber, INT *pnOut)
  402. {
  403. HRESULT hres = ResultFromScode(MK_E_SYNTAX);
  404. INT val1, val2;
  405. BOOL fInDecimal = FALSE;
  406. CHAR achBuffer[ 256 ]; // ugly
  407. LPSTR pszCurrent = *ppszNumber;
  408. *achBuffer = '\0';
  409. /* Handle one sign character. */
  410. if (*pszCurrent == '+') {
  411. pszCurrent++;
  412. }
  413. else if (*pszCurrent == '-') {
  414. strcatf( achBuffer, "-" );
  415. pszCurrent++;
  416. }
  417. for (;;) {
  418. if (*pszCurrent == '.') {
  419. if ( fInDecimal ) break;
  420. fInDecimal = TRUE;
  421. strcatf( achBuffer, "." );
  422. }
  423. else if (*pszCurrent >= '0' && *pszCurrent <= '9') {
  424. CHAR achFoo[ 2 ] = { '\0', '\0' };
  425. achFoo[ 0 ] = *pszCurrent;
  426. strcatf( achBuffer, achFoo );
  427. }
  428. else
  429. break;
  430. pszCurrent++;
  431. }
  432. if ( !fInDecimal )
  433. {
  434. strcatf( achBuffer, ".0" );
  435. }
  436. if ( sscanf( achBuffer, "%d.%d", &val1, &val2 ) == 2 )
  437. {
  438. hres = NOERROR;
  439. }
  440. if (SUCCEEDED(hres)) {
  441. if (pnOut != NULL)
  442. *pnOut = ( ( val1 << 16 ) & 0xFFFF0000 ) | ( val2 & 0x0000FFFF );
  443. *ppszNumber = pszCurrent;
  444. SkipWhitespace(ppszNumber);
  445. }
  446. return hres;
  447. }
  448. const char szPicsVersionLabel[] = "PICS-";
  449. const UINT cchLabel = (sizeof(szPicsVersionLabel)-1) / sizeof(szPicsVersionLabel[0]);
  450. /* Returns a pointer to the closing doublequote of a quoted string, counting
  451. * linefeeds as we go. Returns NULL if no closing doublequote found.
  452. */
  453. LPSTR RatFileParser::EatQuotedString(LPSTR pIn)
  454. {
  455. LPSTR pszQuote = strchrf(pIn, '\"');
  456. if (pszQuote == NULL)
  457. return NULL;
  458. pIn = strchrf(pIn, '\n');
  459. while (pIn != NULL && pIn < pszQuote) {
  460. m_nLine++;
  461. pIn = strchrf(pIn+1, '\n');
  462. }
  463. return pszQuote;
  464. }
  465. /***************************************************************************
  466. Member functions for ET* classes
  467. ***************************************************************************/
  468. /* ETN */
  469. #ifdef DEBUG
  470. void ETN::Set(int rIn){
  471. Init();
  472. r = rIn;
  473. }
  474. int ETN::Get(){
  475. assert(fIsInit());
  476. return r;
  477. }
  478. #endif
  479. ETN* ETN::Duplicate(){
  480. ETN *pETN=new ETN;
  481. if (fIsInit()) pETN->Set(Get());
  482. return pETN;
  483. }
  484. /* ETB */
  485. #ifdef DEBUG
  486. BOOL ETB::Get()
  487. {
  488. assert(fIsInit());
  489. return m_nFlags & ETB_VALUE;
  490. }
  491. void ETB::Set(BOOL b)
  492. {
  493. m_nFlags = ETB_ISINIT | (b ? ETB_VALUE : 0);
  494. }
  495. #endif
  496. ETB* ETB::Duplicate()
  497. {
  498. assert(fIsInit());
  499. ETB *pETB = new ETB;
  500. if (pETB != NULL)
  501. pETB->m_nFlags = m_nFlags;
  502. return pETB;
  503. }
  504. /* ETS */
  505. ETS::~ETS()
  506. {
  507. if (pc != NULL) {
  508. delete pc;
  509. pc = NULL;
  510. }
  511. }
  512. #ifdef DEBUG
  513. char* ETS::Get()
  514. {
  515. assert(fIsInit());
  516. return pc;
  517. }
  518. #endif
  519. void ETS::Set(const char *pIn)
  520. {
  521. if (pc != NULL)
  522. delete pc;
  523. if (pIn != NULL) {
  524. pc = new char[strlenf(pIn) + 1];
  525. if (pc != NULL) {
  526. strcpyf(pc, pIn);
  527. }
  528. }
  529. else {
  530. pc = NULL;
  531. }
  532. }
  533. void ETS::SetTo(char *pIn)
  534. {
  535. if (pc != NULL)
  536. delete pc;
  537. pc = pIn;
  538. }
  539. ETS* ETS::Duplicate()
  540. {
  541. ETS *pETS=new ETS;
  542. if (pETS != NULL)
  543. pETS->Set(Get());
  544. return pETS;
  545. }
  546. /***************************************************************************
  547. Worker functions for inheriting category properties and other
  548. miscellaneous category stuff.
  549. ***************************************************************************/
  550. HRESULT PicsCategory::InitializeMyDefaults(PicsCategory *pCategory)
  551. {
  552. if (!pCategory->etnMin.fIsInit() && etnMin.fIsInit())
  553. pCategory->etnMin.Set(etnMin.Get());
  554. if (!pCategory->etnMax.fIsInit() && etnMax.fIsInit())
  555. pCategory->etnMax.Set(etnMax.Get());
  556. if (!pCategory->etfMulti.fIsInit() && etfMulti.fIsInit())
  557. pCategory->etfMulti.Set(etfMulti.Get());
  558. if (!pCategory->etfInteger.fIsInit() && etfInteger.fIsInit())
  559. pCategory->etfInteger.Set(etfInteger.Get());
  560. if (!pCategory->etfLabelled.fIsInit() && etfLabelled.fIsInit())
  561. pCategory->etfLabelled.Set(etfLabelled.Get());
  562. if (!pCategory->etfUnordered.fIsInit() && etfUnordered.fIsInit())
  563. pCategory->etfUnordered.Set(etfUnordered.Get());
  564. return NOERROR;
  565. }
  566. HRESULT PicsRatingSystem::InitializeMyDefaults(PicsCategory *pCategory)
  567. {
  568. if (m_pDefaultOptions != NULL)
  569. return m_pDefaultOptions->InitializeMyDefaults(pCategory);
  570. return NOERROR; /* no defaults to initialize */
  571. }
  572. HRESULT PicsDefault::InitializeMyDefaults(PicsCategory *pCategory)
  573. {
  574. if (!pCategory->etnMin.fIsInit() && etnMin.fIsInit())
  575. pCategory->etnMin.Set(etnMin.Get());
  576. if (!pCategory->etnMax.fIsInit() && etnMax.fIsInit())
  577. pCategory->etnMax.Set(etnMax.Get());
  578. if (!pCategory->etfMulti.fIsInit() && etfMulti.fIsInit())
  579. pCategory->etfMulti.Set(etfMulti.Get());
  580. if (!pCategory->etfInteger.fIsInit() && etfInteger.fIsInit())
  581. pCategory->etfInteger.Set(etfInteger.Get());
  582. if (!pCategory->etfLabelled.fIsInit() && etfLabelled.fIsInit())
  583. pCategory->etfLabelled.Set(etfLabelled.Get());
  584. if (!pCategory->etfUnordered.fIsInit() && etfUnordered.fIsInit())
  585. pCategory->etfUnordered.Set(etfUnordered.Get());
  586. return NOERROR;
  587. }
  588. HRESULT PicsEnum::InitializeMyDefaults(PicsCategory *pCategory)
  589. {
  590. return E_NOTIMPL; /* should never have a category inherit from an enum */
  591. }
  592. PicsExtension::PicsExtension()
  593. : m_pszRatingBureau(NULL)
  594. {
  595. /* nothing else */
  596. }
  597. PicsExtension::~PicsExtension()
  598. {
  599. delete m_pszRatingBureau;
  600. }
  601. HRESULT PicsExtension::InitializeMyDefaults(PicsCategory *pCategory)
  602. {
  603. return E_NOTIMPL; /* should never have a category inherit from an extension */
  604. }
  605. void PicsCategory::FixupLimits()
  606. {
  607. BOOL fLabelled = (etfLabelled.fIsInit() && etfLabelled.Get());
  608. /*fix up max and min values*/
  609. if (fLabelled ||
  610. (arrpPE.Length()>0 && (!etnMax.fIsInit() || !etnMax.fIsInit())))
  611. {
  612. if (arrpPE.Length() > 0)
  613. {
  614. if (!etnMax.fIsInit())
  615. etnMax.Set(N_INFINITY);
  616. if (!etnMin.fIsInit())
  617. etnMin.Set(P_INFINITY);
  618. for (int z=0;z<arrpPE.Length();++z)
  619. {
  620. if (arrpPE[z]->etnValue.Get() > etnMax.Get()) etnMax.Set(arrpPE[z]->etnValue.Get());
  621. if (arrpPE[z]->etnValue.Get() < etnMin.Get()) etnMin.Set(arrpPE[z]->etnValue.Get());
  622. }
  623. }
  624. else {
  625. etfLabelled.Set(FALSE); /* no enum labels? better not have labelled flag then */
  626. fLabelled = FALSE;
  627. }
  628. }
  629. /*sort labels by value*/
  630. if (fLabelled)
  631. {
  632. int x,y;
  633. PicsEnum *pPE;
  634. for (x=0;x<arrpPE.Length()-1;++x){
  635. for (y=x+1;y<arrpPE.Length();++y){
  636. if (arrpPE[y]->etnValue.Get() < arrpPE[x]->etnValue.Get()){
  637. pPE = arrpPE[x];
  638. arrpPE[x] = arrpPE[y];
  639. arrpPE[y] = pPE;
  640. }
  641. }
  642. }
  643. }
  644. }
  645. void PicsCategory::SetParents(PicsRatingSystem *pOwner)
  646. {
  647. pPRS = pOwner;
  648. UINT cSubCategories = arrpPC.Length();
  649. for (UINT i = 0; i < cSubCategories; i++) {
  650. InitializeMyDefaults(arrpPC[i]); /* subcategory inherits our defaults */
  651. arrpPC[i]->SetParents(pOwner); /* process all subcategories */
  652. }
  653. FixupLimits(); /* inheritance is done, make sure limits make sense */
  654. }
  655. /***************************************************************************
  656. Handler functions which know how to parse the various kinds of content
  657. which can occur within a parenthesized object.
  658. ***************************************************************************/
  659. HRESULT RatParseString(LPSTR *ppszIn, LPVOID *ppOut, RatFileParser *pParser)
  660. {
  661. *ppOut = NULL;
  662. LPSTR pszCurrent = *ppszIn;
  663. if (*pszCurrent != '\"')
  664. return RAT_E_EXPECTEDSTRING;
  665. pszCurrent++;
  666. LPSTR pszEnd = pParser->EatQuotedString(pszCurrent);
  667. if (pszEnd == NULL)
  668. return RAT_E_EXPECTEDSTRING;
  669. UINT cbString = DIFF(pszEnd - pszCurrent);
  670. LPSTR pszNew = new char[cbString + 1];
  671. if (pszNew == NULL)
  672. return E_OUTOFMEMORY;
  673. memcpyf(pszNew, pszCurrent, cbString);
  674. pszNew[cbString] = '\0';
  675. *ppOut = (LPVOID)pszNew;
  676. *ppszIn = pParser->FindNonWhite(pszEnd + 1);
  677. return NOERROR;
  678. }
  679. HRESULT RatParseNumber(LPSTR *ppszIn, LPVOID *ppOut, RatFileParser *pParser)
  680. {
  681. int n;
  682. LPSTR pszCurrent = *ppszIn;
  683. HRESULT hres = ::ParseNumber(&pszCurrent, &n);
  684. if (FAILED(hres))
  685. return RAT_E_EXPECTEDNUMBER;
  686. *(int *)ppOut = n;
  687. LPSTR pszNewline = strchrf(*ppszIn, '\n');
  688. while (pszNewline != NULL && pszNewline < pszCurrent) {
  689. pParser->m_nLine++;
  690. pszNewline = strchrf(pszNewline+1, '\n');
  691. }
  692. *ppszIn = pszCurrent;
  693. return NOERROR;
  694. }
  695. HRESULT RatParsePseudoFloat(LPSTR *ppszIn, LPVOID *ppOut, RatFileParser *pParser)
  696. {
  697. INT n;
  698. LPSTR pszCurrent = *ppszIn;
  699. HRESULT hres = ::ParsePseudoFloat(&pszCurrent, &n);
  700. if (FAILED(hres))
  701. return RAT_E_EXPECTEDNUMBER;
  702. *(INT *)ppOut = n;
  703. LPSTR pszNewline = strchrf(*ppszIn, '\n');
  704. while (pszNewline != NULL && pszNewline < pszCurrent) {
  705. pParser->m_nLine++;
  706. pszNewline = strchrf(pszNewline+1, '\n');
  707. }
  708. *ppszIn = pszCurrent;
  709. return NOERROR;
  710. }
  711. HRESULT RatParseBool(LPSTR *ppszIn, LPVOID *ppOut, RatFileParser *pParser)
  712. {
  713. BOOL b;
  714. /* PICS spec allows a terse way of specifying a TRUE boolean -- leaving
  715. * out the value entirely. In a .RAT file, the result looks like
  716. *
  717. * (unordered)
  718. * (multivalue)
  719. *
  720. * and so on. Called has pointed us at non-whitespace, so if we see
  721. * a closing paren, we know the .RAT file author used this syntax.
  722. */
  723. if (**ppszIn == ')') {
  724. b = TRUE;
  725. }
  726. else {
  727. LPSTR pszCurrent = *ppszIn;
  728. HRESULT hres = ::GetBool(&pszCurrent, &b);
  729. if (FAILED(hres))
  730. return RAT_E_EXPECTEDBOOL;
  731. LPSTR pszNewline = strchrf(*ppszIn, '\n');
  732. while (pszNewline != NULL && pszNewline < pszCurrent) {
  733. pParser->m_nLine++;
  734. pszNewline = strchrf(pszNewline+1, '\n');
  735. }
  736. *ppszIn = pszCurrent;
  737. }
  738. *(LPBOOL)ppOut = b;
  739. return NOERROR;
  740. }
  741. AllowableOption aaoPicsCategory[] = {
  742. { ROID_TRANSMITAS, AO_SINGLE | AO_MANDATORY },
  743. { ROID_NAME, AO_SINGLE },
  744. { ROID_DESCRIPTION, AO_SINGLE },
  745. { ROID_ICON, AO_SINGLE },
  746. { ROID_EXTENSION, 0 },
  747. { ROID_INTEGER, AO_SINGLE },
  748. { ROID_LABELONLY, AO_SINGLE },
  749. { ROID_MIN, AO_SINGLE },
  750. { ROID_MAX, AO_SINGLE },
  751. { ROID_MULTIVALUE, AO_SINGLE },
  752. { ROID_UNORDERED, AO_SINGLE },
  753. { ROID_LABEL, 0 },
  754. { ROID_CATEGORY, 0 },
  755. { ROID_INVALID, 0 }
  756. };
  757. const UINT caoPicsCategory = sizeof(aaoPicsCategory) / sizeof(aaoPicsCategory[0]);
  758. HRESULT RatParseCategory(LPSTR *ppszIn, LPVOID *ppOut, RatFileParser *pParser)
  759. {
  760. /* We must make a copy of the allowable options array because the
  761. * parser will fiddle with the flags in the entries -- specifically,
  762. * setting AO_SEEN. It wouldn't be thread-safe to do this to a
  763. * static array.
  764. */
  765. AllowableOption aao[caoPicsCategory];
  766. ::memcpyf(aao, ::aaoPicsCategory, sizeof(aao));
  767. PicsCategory *pCategory = new PicsCategory;
  768. if (pCategory == NULL)
  769. return E_OUTOFMEMORY;
  770. HRESULT hres = pParser->ParseParenthesizedObject(
  771. ppszIn, /* var containing current ptr */
  772. aao, /* what's legal in this object */
  773. pCategory); /* object to add items back to */
  774. if (FAILED(hres)) {
  775. delete pCategory;
  776. return hres;
  777. }
  778. *ppOut = (LPVOID)pCategory;
  779. return NOERROR;
  780. }
  781. AllowableOption aaoPicsEnum[] = {
  782. { ROID_NAME, AO_SINGLE },
  783. { ROID_DESCRIPTION, AO_SINGLE },
  784. { ROID_VALUE, AO_SINGLE | AO_MANDATORY },
  785. { ROID_ICON, AO_SINGLE },
  786. { ROID_INVALID, 0 }
  787. };
  788. const UINT caoPicsEnum = sizeof(aaoPicsEnum) / sizeof(aaoPicsEnum[0]);
  789. HRESULT RatParseLabel(LPSTR *ppszIn, LPVOID *ppOut, RatFileParser *pParser)
  790. {
  791. /* We must make a copy of the allowable options array because the
  792. * parser will fiddle with the flags in the entries -- specifically,
  793. * setting AO_SEEN. It wouldn't be thread-safe to do this to a
  794. * static array.
  795. */
  796. AllowableOption aao[caoPicsEnum];
  797. ::memcpyf(aao, ::aaoPicsEnum, sizeof(aao));
  798. PicsEnum *pEnum = new PicsEnum;
  799. if (pEnum == NULL)
  800. return E_OUTOFMEMORY;
  801. HRESULT hres = pParser->ParseParenthesizedObject(
  802. ppszIn, /* var containing current ptr */
  803. aao, /* what's legal in this object */
  804. pEnum); /* object to add items back to */
  805. if (FAILED(hres)) {
  806. delete pEnum;
  807. return hres;
  808. }
  809. *ppOut = (LPVOID)pEnum;
  810. return NOERROR;
  811. }
  812. AllowableOption aaoPicsDefault[] = {
  813. { ROID_EXTENSION, 0 },
  814. { ROID_INTEGER, AO_SINGLE },
  815. { ROID_LABELONLY, AO_SINGLE },
  816. { ROID_MAX, AO_SINGLE },
  817. { ROID_MIN, AO_SINGLE },
  818. { ROID_MULTIVALUE, AO_SINGLE },
  819. { ROID_UNORDERED, AO_SINGLE },
  820. { ROID_INVALID, 0 }
  821. };
  822. const UINT caoPicsDefault = sizeof(aaoPicsDefault) / sizeof(aaoPicsDefault[0]);
  823. HRESULT RatParseDefault(LPSTR *ppszIn, LPVOID *ppOut, RatFileParser *pParser)
  824. {
  825. /* We must make a copy of the allowable options array because the
  826. * parser will fiddle with the flags in the entries -- specifically,
  827. * setting AO_SEEN. It wouldn't be thread-safe to do this to a
  828. * static array.
  829. */
  830. AllowableOption aao[caoPicsDefault];
  831. ::memcpyf(aao, ::aaoPicsDefault, sizeof(aao));
  832. PicsDefault *pDefault = new PicsDefault;
  833. if (pDefault == NULL)
  834. return E_OUTOFMEMORY;
  835. HRESULT hres = pParser->ParseParenthesizedObject(
  836. ppszIn, /* var containing current ptr */
  837. aao, /* what's legal in this object */
  838. pDefault); /* object to add items back to */
  839. if (FAILED(hres)) {
  840. delete pDefault;
  841. return hres;
  842. }
  843. *ppOut = (LPVOID)pDefault;
  844. return NOERROR;
  845. }
  846. AllowableOption aaoPicsExtension[] = {
  847. { ROID_MANDATORY, AO_SINGLE },
  848. { ROID_OPTIONAL, AO_SINGLE },
  849. { ROID_INVALID, 0 }
  850. };
  851. const UINT caoPicsExtension = sizeof(aaoPicsExtension) / sizeof(aaoPicsExtension[0]);
  852. HRESULT RatParseExtension(LPSTR *ppszIn, LPVOID *ppOut, RatFileParser *pParser)
  853. {
  854. /* We must make a copy of the allowable options array because the
  855. * parser will fiddle with the flags in the entries -- specifically,
  856. * setting AO_SEEN. It wouldn't be thread-safe to do this to a
  857. * static array.
  858. */
  859. AllowableOption aao[caoPicsExtension];
  860. ::memcpyf(aao, ::aaoPicsExtension, sizeof(aao));
  861. PicsExtension *pExtension = new PicsExtension;
  862. if (pExtension == NULL)
  863. return E_OUTOFMEMORY;
  864. HRESULT hres = pParser->ParseParenthesizedObject(
  865. ppszIn, /* var containing current ptr */
  866. aao, /* what's legal in this object */
  867. pExtension); /* object to add items back to */
  868. if (FAILED(hres)) {
  869. delete pExtension;
  870. return hres;
  871. }
  872. *ppOut = (LPVOID)pExtension;
  873. return NOERROR;
  874. }
  875. /* Since the only extension we support right now is the one for a label
  876. * bureau, we just return the first quoted string we find if the caller
  877. * wants it. If ppOut is NULL, then it's some other extension and the
  878. * caller doesn't care about the data, he just wants it eaten.
  879. */
  880. HRESULT ParseRatExtensionData(LPSTR *ppszIn, RatFileParser *pParser, LPSTR *ppOut)
  881. {
  882. HRESULT hres = NOERROR;
  883. LPSTR pszCurrent = *ppszIn;
  884. /* Must look for closing ')' ourselves to terminate */
  885. while (*pszCurrent != ')') {
  886. if (*pszCurrent == '(') {
  887. pszCurrent = pParser->FindNonWhite(pszCurrent+1); /* skip paren and whitespace */
  888. hres = ParseRatExtensionData(&pszCurrent, pParser, ppOut); /* parentheses contain data */
  889. if (FAILED(hres))
  890. return hres;
  891. if (*pszCurrent != ')')
  892. return RAT_E_EXPECTEDRIGHT;
  893. pszCurrent = pParser->FindNonWhite(pszCurrent+1); /* skip close ) and whitespace */
  894. }
  895. else if (*pszCurrent == '\"') { /* should be just a quoted string */
  896. if (ppOut != NULL && *ppOut == NULL) {
  897. hres = RatParseString(&pszCurrent, (LPVOID *)ppOut, pParser);
  898. }
  899. else {
  900. ++pszCurrent;
  901. LPSTR pszEndQuote = pParser->EatQuotedString(pszCurrent);
  902. if (pszEndQuote == NULL)
  903. return RAT_E_EXPECTEDSTRING;
  904. pszCurrent = pParser->FindNonWhite(pszEndQuote+1); /* skip close " and whitespace */
  905. }
  906. }
  907. else
  908. return RAT_E_UNKNOWNITEM; /* general bad syntax */
  909. }
  910. /* Caller will skip over final ')' for us. */
  911. *ppszIn = pszCurrent;
  912. return NOERROR;
  913. }
  914. HRESULT RatParseMandatory(LPSTR *ppszIn, LPVOID *ppOut, RatFileParser *pParser)
  915. {
  916. LPSTR pszCurrent = *ppszIn;
  917. /* First thing better be a quoted URL identifying the extension. */
  918. if (*pszCurrent != '\"')
  919. return RAT_E_EXPECTEDSTRING;
  920. pszCurrent++;
  921. LPSTR pszEnd = pParser->EatQuotedString(pszCurrent);
  922. if (pszCurrent == NULL)
  923. return RAT_E_EXPECTEDSTRING; /* missing closing " */
  924. /* See if it's the extension for a label bureau. */
  925. LPSTR pszBureau = NULL;
  926. LPSTR *ppData = NULL;
  927. if (IsEqualToken(pszCurrent, pszEnd, ::szRatingBureauExtension)) {
  928. ppData = &pszBureau;
  929. }
  930. pszCurrent = pParser->FindNonWhite(pszEnd+1); /* skip closing " and whitespace */
  931. HRESULT hres = ParseRatExtensionData(&pszCurrent, pParser, ppData);
  932. if (FAILED(hres))
  933. return hres;
  934. *ppOut = pszBureau; /* return label bureau string if that's what we found */
  935. *ppszIn = pszCurrent;
  936. if (ppData == NULL)
  937. return RAT_E_UNKNOWNMANDATORY; /* we didn't recognize it */
  938. else
  939. return NOERROR;
  940. }
  941. /* RatParseOptional uses the code in RatParseMandatory to parse the extension
  942. * data, in case an extension that should be optional comes in as mandatory.
  943. * We then detect RatParseMandatory rejecting the thing as unrecognized and
  944. * allow it through, since here it's optional.
  945. */
  946. HRESULT RatParseOptional(LPSTR *ppszIn, LPVOID *ppOut, RatFileParser *pParser)
  947. {
  948. HRESULT hres = RatParseMandatory(ppszIn, ppOut, pParser);
  949. if (hres == RAT_E_UNKNOWNMANDATORY)
  950. hres = S_OK;
  951. return hres;
  952. }
  953. /***************************************************************************
  954. Code to identify the opening keyword of a parenthesized object and
  955. associate it with content.
  956. ***************************************************************************/
  957. /* The following array is indexed by RatObjectID values. */
  958. struct {
  959. LPCSTR pszToken; /* token by which we identify it */
  960. RatObjectHandler pHandler; /* function which parses the object's contents */
  961. } aObjectDescriptions[] = {
  962. { szNULL, NULL },
  963. { NULL, NULL }, /* placeholder for comparing against no token */
  964. { szPicsVersion, RatParsePseudoFloat },
  965. { szRatingSystem, RatParseString },
  966. { szRatingService, RatParseString },
  967. { szRatingBureau, RatParseString },
  968. { szBureauRequired, RatParseBool },
  969. { szCategory, RatParseCategory },
  970. { szTransmitAs, RatParseString },
  971. { szLabel, RatParseLabel },
  972. { szValue, RatParseNumber },
  973. { szDefault, RatParseDefault },
  974. { szDescription, RatParseString },
  975. { szExtensionOption, RatParseExtension },
  976. { szMandatory, RatParseMandatory },
  977. { szOptional, RatParseOptional },
  978. { szIcon, RatParseString },
  979. { szInteger, RatParseBool },
  980. { szLabelOnly, RatParseBool },
  981. { szMax, RatParseNumber },
  982. { szMin, RatParseNumber },
  983. { szMultiValue, RatParseBool },
  984. { szName, RatParseString },
  985. { szUnordered, RatParseBool }
  986. };
  987. /* ParseToOpening eats the opening '(' of a parenthesized object, and
  988. * verifies that the token just inside it is one of the expected ones.
  989. * If so, *ppIn is advanced past that token to the next non-whitespace
  990. * character; otherwise, an error is returned.
  991. *
  992. * For example, if *ppIn is pointing at "(PICS-version 1.1)", and
  993. * ROID_PICSVERSION is in the allowable option table supplied, then
  994. * NOERROR is returned and *ppIn will point at "1.1)".
  995. *
  996. * If the function is successful, *ppFound is set to point to the element
  997. * in the allowable-options table which matches the type of thing this
  998. * object actually is.
  999. */
  1000. HRESULT RatFileParser::ParseToOpening(LPSTR *ppIn, AllowableOption *paoExpected,
  1001. AllowableOption **ppFound)
  1002. {
  1003. LPSTR pszCurrent = *ppIn;
  1004. pszCurrent = FindNonWhite(pszCurrent);
  1005. if (*pszCurrent != '(')
  1006. return RAT_E_EXPECTEDLEFT;
  1007. pszCurrent = FindNonWhite(pszCurrent+1); /* skip '(' and whitespace */
  1008. LPSTR pszTokenEnd = FindTokenEnd(pszCurrent);
  1009. for (; paoExpected->roid != ROID_INVALID; paoExpected++) {
  1010. LPCSTR pszThisToken = aObjectDescriptions[paoExpected->roid].pszToken;
  1011. /* Special case for beginning of RAT file structure: no token at all. */
  1012. if (pszThisToken == NULL) {
  1013. if (*pszCurrent == '(') {
  1014. *ppIn = pszCurrent;
  1015. *ppFound = paoExpected;
  1016. return NOERROR;
  1017. }
  1018. else {
  1019. return RAT_E_EXPECTEDLEFT;
  1020. }
  1021. }
  1022. else if (IsEqualToken(pszCurrent, pszTokenEnd, pszThisToken))
  1023. break;
  1024. }
  1025. if (paoExpected->roid != ROID_INVALID) {
  1026. *ppIn = FindNonWhite(pszTokenEnd); /* skip token and whitespace */
  1027. *ppFound = paoExpected;
  1028. return NOERROR;
  1029. }
  1030. else
  1031. return RAT_E_UNKNOWNITEM;
  1032. }
  1033. /***************************************************************************
  1034. The top-level entrypoint for parsing out a whole rating system.
  1035. ***************************************************************************/
  1036. AllowableOption aaoPicsRatingSystem[] = {
  1037. { ROID_PICSVERSION, AO_SINGLE | AO_MANDATORY },
  1038. { ROID_RATINGSYSTEM, AO_SINGLE | AO_MANDATORY },
  1039. { ROID_RATINGSERVICE, AO_SINGLE | AO_MANDATORY },
  1040. { ROID_RATINGBUREAU, AO_SINGLE },
  1041. { ROID_BUREAUREQUIRED, AO_SINGLE },
  1042. { ROID_DEFAULT, 0 },
  1043. { ROID_DESCRIPTION, AO_SINGLE },
  1044. { ROID_EXTENSION, 0 },
  1045. { ROID_ICON, AO_SINGLE },
  1046. { ROID_NAME, AO_SINGLE },
  1047. { ROID_CATEGORY, AO_MANDATORY },
  1048. { ROID_INVALID, 0 }
  1049. };
  1050. const UINT caoPicsRatingSystem = sizeof(aaoPicsRatingSystem) / sizeof(aaoPicsRatingSystem[0]);
  1051. HRESULT PicsRatingSystem::Parse(LPSTR pIn)
  1052. {
  1053. /* This guy is small enough to just init directly on the stack */
  1054. AllowableOption aaoRoot[] = { { ROID_PICSDOCUMENT, 0 }, { ROID_INVALID, 0 } };
  1055. AllowableOption aao[caoPicsRatingSystem];
  1056. ::memcpyf(aao, ::aaoPicsRatingSystem, sizeof(aao));
  1057. AllowableOption *pFound;
  1058. RatFileParser parser;
  1059. HRESULT hres = parser.ParseToOpening(&pIn, aaoRoot, &pFound);
  1060. if (FAILED(hres))
  1061. return hres; /* some error early on */
  1062. hres = parser.ParseParenthesizedObject(
  1063. &pIn, /* var containing current ptr */
  1064. aao, /* what's legal in this object */
  1065. this); /* object to add items back to */
  1066. if (FAILED(hres))
  1067. nErrLine = parser.m_nLine;
  1068. return hres;
  1069. }
  1070. /***************************************************************************
  1071. Callbacks into the various class objects to add parsed properties.
  1072. ***************************************************************************/
  1073. HRESULT PicsRatingSystem::AddItem(RatObjectID roid, LPVOID pData)
  1074. {
  1075. HRESULT hres = S_OK;
  1076. switch (roid) {
  1077. case ROID_PICSVERSION:
  1078. etnPicsVersion.Set((INT_PTR)pData);
  1079. break;
  1080. case ROID_RATINGSYSTEM:
  1081. etstrRatingSystem.SetTo((LPSTR)pData);
  1082. break;
  1083. case ROID_RATINGSERVICE:
  1084. etstrRatingService.SetTo((LPSTR)pData);
  1085. break;
  1086. case ROID_RATINGBUREAU:
  1087. etstrRatingBureau.SetTo((LPSTR)pData);
  1088. break;
  1089. case ROID_BUREAUREQUIRED:
  1090. etbBureauRequired.Set((INT_PTR)pData);
  1091. break;
  1092. case ROID_DEFAULT:
  1093. m_pDefaultOptions = (PicsDefault *)pData;
  1094. break;
  1095. case ROID_DESCRIPTION:
  1096. etstrDesc.SetTo((LPSTR)pData);
  1097. break;
  1098. case ROID_EXTENSION:
  1099. {
  1100. /* just eat extensions for now */
  1101. PicsExtension *pExtension = (PicsExtension *)pData;
  1102. if (pExtension != NULL) {
  1103. /* If this is a rating bureau extension, take his bureau
  1104. * string and store it in this PicsRatingSystem. We now
  1105. * own the memory, so NULL out the extension's pointer to
  1106. * it so he won't delete it.
  1107. */
  1108. if (pExtension->m_pszRatingBureau != NULL) {
  1109. etstrRatingBureau.SetTo(pExtension->m_pszRatingBureau);
  1110. pExtension->m_pszRatingBureau = NULL;
  1111. }
  1112. delete pExtension;
  1113. }
  1114. }
  1115. break;
  1116. case ROID_ICON:
  1117. etstrIcon.SetTo((LPSTR)pData);
  1118. break;
  1119. case ROID_NAME:
  1120. etstrName.SetTo((LPSTR)pData);
  1121. break;
  1122. case ROID_CATEGORY:
  1123. {
  1124. PicsCategory *pCategory = (PicsCategory *)pData;
  1125. hres = arrpPC.Append(pCategory) ? S_OK : E_OUTOFMEMORY;
  1126. if (FAILED(hres)) {
  1127. delete pCategory;
  1128. }
  1129. else {
  1130. InitializeMyDefaults(pCategory); /* category inherits default settings */
  1131. pCategory->SetParents(this); /* set pPRS fields in whole tree */
  1132. }
  1133. }
  1134. break;
  1135. default:
  1136. assert(FALSE); /* shouldn't have been given a ROID that wasn't in
  1137. * the table we passed to the parser! */
  1138. hres = E_UNEXPECTED;
  1139. break;
  1140. }
  1141. return hres;
  1142. }
  1143. void PicsRatingSystem::Dump( void )
  1144. {
  1145. fprintf( stdout,
  1146. "Rating system: %s\n"
  1147. "Version: %d.%d\n"
  1148. "Rating Service: %s\n"
  1149. "Rating bureau: %s\n"
  1150. "Bureau required: %s\n"
  1151. "Description: %s\n"
  1152. "Icon: %s\n"
  1153. "Name: %s\n"
  1154. "Number of categories: %d\n",
  1155. etstrRatingSystem.Get(),
  1156. ( etnPicsVersion.Get() & 0xFFFF0000 ) >> 16,
  1157. etnPicsVersion.Get() & 0x0000FFFF,
  1158. etstrRatingService.Get(),
  1159. etstrRatingBureau.Get(),
  1160. etbBureauRequired.Get() ? "TRUE" : "FALSE",
  1161. etstrDesc.Get(),
  1162. etstrIcon.Get(),
  1163. etstrName.Get(),
  1164. arrpPC.Length() );
  1165. int iCounter = 0;
  1166. for( ; iCounter < arrpPC.Length(); iCounter++ )
  1167. {
  1168. arrpPC[ iCounter ]->Dump();
  1169. }
  1170. }
  1171. //---------------------------------------------------------------
  1172. // boydm
  1173. void PicsRatingSystem::OutputLabels( CString &sz, CString szURL, CString szName, CString szStart, CString szEnd )
  1174. {
  1175. CString szScratch;
  1176. CString szTemp;
  1177. INT_PTR dwVersion = etnPicsVersion.Get();
  1178. // start with the name, and the version number
  1179. szTemp = szPicsOpening;
  1180. szScratch.Format( _T("%s%d.%d"), szTemp, HIWORD(dwVersion), LOWORD(dwVersion) );
  1181. sz += szScratch;
  1182. // add in the URL string - surrounded by quotes and a return
  1183. sz += _T(" \"http://www.rsac.org/ratingsv01.html\" ");
  1184. // start the labels
  1185. sz += szShortLabelWord;
  1186. sz += _T(" ");
  1187. // if it is there, add the by name string
  1188. if ( !szName.IsEmpty() )
  1189. {
  1190. sz += szByOption;
  1191. sz += _T(" \"");
  1192. sz += szName;
  1193. sz += _T("\" ");
  1194. }
  1195. // if it is there, add the start string
  1196. if ( !szStart.IsEmpty() )
  1197. {
  1198. sz += szOnOption;
  1199. sz += _T(" \"");
  1200. sz += szStart;
  1201. sz += _T("\" ");
  1202. }
  1203. // if it is there, add the expiration string
  1204. if ( !szEnd.IsEmpty() )
  1205. {
  1206. sz += szExpOption;
  1207. sz += _T(" \"");
  1208. sz += szEnd;
  1209. sz += _T("\" ");
  1210. }
  1211. // add in the categorical ratings
  1212. DWORD nCat = arrpPC.Length();
  1213. sz += szShortRatings;
  1214. sz += _T(" (");
  1215. for ( DWORD iCat = 0; iCat < nCat; iCat++ )
  1216. {
  1217. arrpPC[iCat]->OutputLabel( sz );
  1218. }
  1219. // trim any trailing whitespace
  1220. sz.TrimRight();
  1221. // close with a parenthesis
  1222. sz += _T(')');
  1223. // end with the closing parenthesis
  1224. sz += _T(")");
  1225. }
  1226. //---------------------------------------------------------------
  1227. HRESULT PicsCategory::AddItem(RatObjectID roid, LPVOID pData)
  1228. {
  1229. HRESULT hres = S_OK;
  1230. switch (roid) {
  1231. case ROID_TRANSMITAS:
  1232. etstrTransmitAs.SetTo((LPSTR)pData);
  1233. break;
  1234. case ROID_NAME:
  1235. etstrName.SetTo((LPSTR)pData);
  1236. break;
  1237. case ROID_DESCRIPTION:
  1238. etstrDesc.SetTo((LPSTR)pData);
  1239. break;
  1240. case ROID_ICON:
  1241. etstrIcon.SetTo((LPSTR)pData);
  1242. break;
  1243. case ROID_EXTENSION:
  1244. { /* we support no extensions below the rating system level */
  1245. PicsExtension *pExtension = (PicsExtension *)pData;
  1246. if (pExtension != NULL)
  1247. delete pExtension;
  1248. }
  1249. break;
  1250. case ROID_INTEGER:
  1251. etfInteger.Set((INT_PTR)pData);
  1252. break;
  1253. case ROID_LABELONLY:
  1254. etfLabelled.Set((INT_PTR)pData);
  1255. break;
  1256. case ROID_MULTIVALUE:
  1257. etfMulti.Set((INT_PTR)pData);
  1258. break;
  1259. case ROID_UNORDERED:
  1260. etfUnordered.Set((INT_PTR)pData);
  1261. break;
  1262. case ROID_MIN:
  1263. etnMin.Set((INT_PTR)pData);
  1264. break;
  1265. case ROID_MAX:
  1266. etnMax.Set((INT_PTR)pData);
  1267. break;
  1268. case ROID_LABEL:
  1269. {
  1270. PicsEnum *pEnum = (PicsEnum *)pData;
  1271. hres = arrpPE.Append(pEnum) ? S_OK : E_OUTOFMEMORY;
  1272. if (FAILED(hres))
  1273. delete pEnum;
  1274. }
  1275. break;
  1276. case ROID_CATEGORY:
  1277. {
  1278. PicsCategory *pCategory = (PicsCategory *)pData;
  1279. /* For a nested category, synthesize the transmit-name from
  1280. * ours and the child's (e.g., parent category 'color' plus
  1281. * child category 'hue' becomes 'color/hue'.
  1282. *
  1283. * Note that the memory we allocate for the new name will be
  1284. * owned by pCategory->etstrTransmitAs. There is no memory
  1285. * leak there.
  1286. */
  1287. UINT cbCombined = strlenf(etstrTransmitAs.Get()) +
  1288. strlenf(pCategory->etstrTransmitAs.Get()) +
  1289. 2; /* for PicsDelimChar + null */
  1290. LPSTR pszTemp = new char[cbCombined];
  1291. if (pszTemp == NULL)
  1292. hres = E_OUTOFMEMORY;
  1293. else {
  1294. sprintf(pszTemp, "%s%c%s", etstrTransmitAs.Get(),
  1295. PicsDelimChar, pCategory->etstrTransmitAs.Get());
  1296. pCategory->etstrTransmitAs.SetTo(pszTemp);
  1297. hres = arrpPC.Append(pCategory) ? S_OK : E_OUTOFMEMORY;
  1298. }
  1299. if (FAILED(hres)) {
  1300. delete pCategory;
  1301. }
  1302. }
  1303. break;
  1304. default:
  1305. assert(FALSE); /* shouldn't have been given a ROID that wasn't in
  1306. * the table we passed to the parser! */
  1307. hres = E_UNEXPECTED;
  1308. break;
  1309. }
  1310. return hres;
  1311. }
  1312. void PicsCategory::Dump( void )
  1313. {
  1314. fprintf( stdout,
  1315. " Transmit As: %s Name: %s Description: %s Icon: %s\n",
  1316. etstrTransmitAs.Get(),
  1317. etstrName.Get(),
  1318. etstrDesc.Get(),
  1319. etstrIcon.Get() );
  1320. int iCounter = 0;
  1321. for( ; iCounter < arrpPE.Length(); iCounter++ )
  1322. {
  1323. arrpPE[ iCounter ]->Dump();
  1324. }
  1325. }
  1326. //---------------------------------------------------------------
  1327. void PicsCategory::OutputLabel( CString &sz )
  1328. {
  1329. CString szCat;
  1330. CString szTransmit = etstrTransmitAs.Get();
  1331. // prepare the category string
  1332. szCat.Format( _T("%s %d "), szTransmit, currentValue );
  1333. sz += szCat;
  1334. }
  1335. //---------------------------------------------------------------
  1336. BOOL PicsCategory::FSetValuePair( CHAR chCat, WORD value )
  1337. {
  1338. CString szCat = etstrTransmitAs.Get();
  1339. // first check to see if this is the right category
  1340. if ( szCat == chCat )
  1341. {
  1342. // success! set the value and return
  1343. currentValue = value;
  1344. return TRUE;
  1345. }
  1346. // try its categorical children
  1347. DWORD nCat = arrpPC.Length();
  1348. for ( DWORD iCat = 0; iCat < nCat; iCat++ )
  1349. {
  1350. // stop at the first successful setting
  1351. if ( arrpPC[iCat]->FSetValuePair(chCat, value) )
  1352. return TRUE;
  1353. }
  1354. // nope
  1355. return FALSE;
  1356. }
  1357. //---------------------------------------------------------------
  1358. HRESULT PicsEnum::AddItem(RatObjectID roid, LPVOID pData)
  1359. {
  1360. HRESULT hres = S_OK;
  1361. switch (roid) {
  1362. case ROID_NAME:
  1363. etstrName.SetTo((LPSTR)pData);
  1364. break;
  1365. case ROID_DESCRIPTION:
  1366. etstrDesc.SetTo((LPSTR)pData);
  1367. break;
  1368. case ROID_ICON:
  1369. etstrIcon.SetTo((LPSTR)pData);
  1370. break;
  1371. case ROID_VALUE:
  1372. etnValue.Set((INT_PTR)pData);
  1373. break;
  1374. default:
  1375. assert(FALSE); /* shouldn't have been given a ROID that wasn't in
  1376. * the table we passed to the parser! */
  1377. hres = E_UNEXPECTED;
  1378. break;
  1379. }
  1380. return hres;
  1381. }
  1382. void PicsEnum::Dump( void )
  1383. {
  1384. fprintf( stdout,
  1385. " %s %s %s %d\n",
  1386. etstrName.Get(),
  1387. etstrDesc.Get(),
  1388. etstrIcon.Get(),
  1389. etnValue.Get() );
  1390. }
  1391. HRESULT PicsDefault::AddItem(RatObjectID roid, LPVOID pData)
  1392. {
  1393. HRESULT hres = S_OK;
  1394. switch (roid) {
  1395. case ROID_EXTENSION:
  1396. { /* we support no extensions below the rating system level */
  1397. PicsExtension *pExtension = (PicsExtension *)pData;
  1398. if (pExtension != NULL)
  1399. delete pExtension;
  1400. }
  1401. break;
  1402. case ROID_INTEGER:
  1403. etfInteger.Set((INT_PTR)pData);
  1404. break;
  1405. case ROID_LABELONLY:
  1406. etfLabelled.Set((INT_PTR)pData);
  1407. break;
  1408. case ROID_MULTIVALUE:
  1409. etfMulti.Set((INT_PTR)pData);
  1410. break;
  1411. case ROID_UNORDERED:
  1412. etfUnordered.Set((INT_PTR)pData);
  1413. break;
  1414. case ROID_MIN:
  1415. etnMin.Set((INT_PTR)pData);
  1416. break;
  1417. case ROID_MAX:
  1418. etnMax.Set((INT_PTR)pData);
  1419. break;
  1420. default:
  1421. assert(FALSE); /* shouldn't have been given a ROID that wasn't in
  1422. * the table we passed to the parser! */
  1423. hres = E_UNEXPECTED;
  1424. break;
  1425. }
  1426. return hres;
  1427. }
  1428. void PicsDefault::Dump( void )
  1429. {
  1430. fprintf( stdout,
  1431. " Default?\n" );
  1432. }
  1433. HRESULT PicsExtension::AddItem(RatObjectID roid, LPVOID pData)
  1434. {
  1435. HRESULT hres = S_OK;
  1436. switch (roid) {
  1437. case ROID_OPTIONAL:
  1438. case ROID_MANDATORY:
  1439. /* Only data we should get is a label bureau string. */
  1440. if (pData != NULL)
  1441. m_pszRatingBureau = (LPSTR)pData;
  1442. break;
  1443. default:
  1444. assert(FALSE); /* shouldn't have been given a ROID that wasn't in
  1445. * the table we passed to the parser! */
  1446. hres = E_UNEXPECTED;
  1447. break;
  1448. }
  1449. return hres;
  1450. }
  1451. void PicsExtension::Dump( void )
  1452. {
  1453. fprintf( stdout,
  1454. " Extension?\n" );
  1455. }
  1456. /***************************************************************************
  1457. The main loop of the parser.
  1458. ***************************************************************************/
  1459. /* ParseParenthesizedObjectContents is called with a text pointer pointing at
  1460. * the first non-whitespace thing following the token identifying the type of
  1461. * object. It parses the rest of the contents of the object, up to and
  1462. * including the ')' which closes it. The array of AllowableOption structures
  1463. * specifies which understood options are allowed to occur within this object.
  1464. */
  1465. HRESULT RatFileParser::ParseParenthesizedObject(
  1466. LPSTR *ppIn, /* where we are in the text stream */
  1467. AllowableOption aao[], /* allowable things inside this object */
  1468. PicsObjectBase *pObject /* object to set parameters into */
  1469. )
  1470. {
  1471. HRESULT hres = S_OK;
  1472. LPSTR pszCurrent = *ppIn;
  1473. AllowableOption *pFound;
  1474. for (pFound = aao; pFound->roid != ROID_INVALID; pFound++) {
  1475. pFound->fdwOptions &= ~AO_SEEN;
  1476. }
  1477. pFound = NULL;
  1478. while (*pszCurrent != ')' && *pszCurrent != '\0' && SUCCEEDED(hres)) {
  1479. hres = ParseToOpening(&pszCurrent, aao, &pFound);
  1480. if (SUCCEEDED(hres)) {
  1481. LPVOID pData;
  1482. hres = (*(aObjectDescriptions[pFound->roid].pHandler))(&pszCurrent, &pData, this);
  1483. if (SUCCEEDED(hres)) {
  1484. if ((pFound->fdwOptions & (AO_SINGLE | AO_SEEN)) == (AO_SINGLE | AO_SEEN))
  1485. hres = RAT_E_DUPLICATEITEM;
  1486. else {
  1487. pFound->fdwOptions |= AO_SEEN;
  1488. hres = pObject->AddItem(pFound->roid, pData);
  1489. if (SUCCEEDED(hres)) {
  1490. if (*pszCurrent != ')')
  1491. hres = RAT_E_EXPECTEDRIGHT;
  1492. else
  1493. pszCurrent = FindNonWhite(pszCurrent+1);
  1494. }
  1495. }
  1496. }
  1497. }
  1498. }
  1499. if (FAILED(hres))
  1500. return hres;
  1501. for (pFound = aao; pFound->roid != ROID_INVALID; pFound++) {
  1502. if ((pFound->fdwOptions & (AO_MANDATORY | AO_SEEN)) == AO_MANDATORY)
  1503. return RAT_E_MISSINGITEM; /* mandatory item not found */
  1504. }
  1505. *ppIn = pszCurrent;
  1506. return hres;
  1507. }
  1508. /*
  1509. int __cdecl main(int argc, char **argv)
  1510. {
  1511. PicsRatingSystem Rating;
  1512. HANDLE hFile;
  1513. HANDLE hFileMapping;
  1514. VOID * pMem;
  1515. assert( argc > 1 );
  1516. hFile = CreateFile( argv[ 1 ],
  1517. GENERIC_READ,
  1518. 0,
  1519. NULL,
  1520. OPEN_EXISTING,
  1521. FILE_ATTRIBUTE_NORMAL,
  1522. NULL );
  1523. if ( hFile == INVALID_HANDLE_VALUE )
  1524. {
  1525. fprintf( stderr, "Error opening file %s\n", argv[ 1 ] );
  1526. return 1;
  1527. }
  1528. hFileMapping = CreateFileMapping( hFile,
  1529. NULL,
  1530. PAGE_READONLY,
  1531. 0,
  1532. 0,
  1533. NULL );
  1534. if ( hFileMapping == NULL )
  1535. {
  1536. fprintf( stderr, "Error creating mapping for %s\n", argv[ 1 ] );
  1537. CloseHandle( hFile );
  1538. return 2;
  1539. }
  1540. pMem = MapViewOfFile( hFileMapping,
  1541. FILE_MAP_READ,
  1542. 0,
  1543. 0,
  1544. 0 );
  1545. if ( pMem == NULL )
  1546. {
  1547. fprintf( stderr, "Error mapping view to file %s\n", argv [1 ] );
  1548. CloseHandle( hFileMapping );
  1549. CloseHandle( hFile );
  1550. return 3;
  1551. }
  1552. Rating.Parse( argv[ 1 ], (LPSTR) pMem );
  1553. fprintf( stdout,
  1554. "Dumping contents of RAT\n" );
  1555. Rating.Dump();
  1556. UnmapViewOfFile( pMem );
  1557. CloseHandle( hFileMapping );
  1558. CloseHandle( hFile );
  1559. return 0;
  1560. }
  1561. */