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.

612 lines
21 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. /*
  3. * installb.c - creates synthesized features and options and
  4. * associated constraints and links to the installable
  5. * feature or options.
  6. */
  7. #include "gpdparse.h"
  8. // ---- functions defined in installb.c ---- //
  9. DWORD DwCountSynthFeatures(
  10. IN BOOL (*fnBCreateFeature)(DWORD, DWORD, DWORD, PGLOBL ), // callback
  11. IN OUT PGLOBL pglobl
  12. ) ;
  13. BOOL BCreateSynthFeatures(
  14. IN DWORD dwFea, // index of installable feature
  15. IN DWORD dwOpt, // index of installable Option or set to INVALID_INDEX
  16. IN DWORD dwSynFea,
  17. IN OUT PGLOBL pglobl) ;
  18. BOOL BEnableInvInstallableCombos(
  19. PGLOBL pglobl) ;
  20. // ---------------------------------------------------- //
  21. DWORD DwCountSynthFeatures(
  22. IN BOOL (*fnBCreateFeature)(DWORD, DWORD, DWORD, PGLOBL ), // callback
  23. IN OUT PGLOBL pglobl
  24. )
  25. /*
  26. This function is called twice by PostProcess().
  27. the first pass sets fnBCreateFeature = NULL
  28. we just need to find out how many installable features and options
  29. exist. Then we allocate this many synthesized features (outside of
  30. this function)
  31. In the second pass, we actually initialize the synthesized features
  32. and all the constraints applicable to that feature. This is the
  33. job of fnBCreateFeature.
  34. */
  35. {
  36. DWORD dwOpt , dwHeapOffset, dwNodeIndex,
  37. dwFea, dwNumFea, dwNumOpt, dwNumSynFea ;
  38. PDFEATURE_OPTIONS pfo ;
  39. PATTRIB_TREE patt ; // start of ATTRIBUTE tree array.
  40. PATREEREF patr ;
  41. if(fnBCreateFeature &&
  42. !gMasterTable[MTI_SYNTHESIZED_FEATURES].dwArraySize)
  43. return(0) ; // May skip 2nd pass if dwNumSynFea == 0
  44. patt = (PATTRIB_TREE) gMasterTable[MTI_ATTRIBTREE].pubStruct ;
  45. pfo = (PDFEATURE_OPTIONS) gMasterTable[MTI_DFEATURE_OPTIONS].pubStruct ;
  46. dwNumFea = gMasterTable[MTI_DFEATURE_OPTIONS].dwArraySize ;
  47. dwNumSynFea = 0 ;
  48. for(dwFea = 0 ; dwFea < dwNumFea ; dwFea++)
  49. {
  50. if(!fnBCreateFeature)
  51. { // first pass clear all links to synthesiszed features.
  52. // this will catch errors if gpd writer attempts to
  53. // reference non-installable feature/options in
  54. // InstalledConstraints and invalidInstallableCombinations.
  55. pfo[dwFea].dwInstallableFeatureIndex = // backlink to Feature/Option
  56. pfo[dwFea].dwInstallableOptionIndex = // that prompted this feature.
  57. pfo[dwFea].dwFeatureSpawnsFeature = INVALID_INDEX;
  58. // If this feature is installable, this points to the
  59. // index of the resulting synthesized feature.
  60. }
  61. if(BReadDataInGlobalNode(&pfo[dwFea].atrFeaInstallable,
  62. &dwHeapOffset, pglobl) &&
  63. *(PDWORD)(mpubOffRef + dwHeapOffset) == BT_TRUE)
  64. {
  65. if(fnBCreateFeature)
  66. {
  67. if(!fnBCreateFeature(dwFea, INVALID_INDEX, dwNumSynFea, pglobl) )
  68. // featureIndex, optionIndex, index of SynFea
  69. {
  70. ERR(("DwCountSynthFeatures: Unable to create synthesized feature for installable Feature index %d.\n",
  71. dwFea));
  72. pfo[dwFea].dwFeatureSpawnsFeature = INVALID_INDEX;
  73. }
  74. }
  75. dwNumSynFea++ ;
  76. }
  77. dwNumOpt = pfo[dwFea].dwNumOptions ;
  78. patr = &pfo[dwFea].atrOptInstallable ;
  79. if(*patr == ATTRIB_UNINITIALIZED)
  80. continue ;
  81. if(*patr & ATTRIB_HEAP_VALUE)
  82. {
  83. ERR(("Internal error: DwCountSynthFeatures - atrOptInstallable should never be branchless.\n"));
  84. continue ;
  85. }
  86. for(dwOpt = 0 ; dwOpt < dwNumOpt ; dwOpt++)
  87. {
  88. DWORD dwNodeIndex ;
  89. dwNodeIndex = *patr ; // to avoid overwriting
  90. // the attribute tree.
  91. if(BfindMatchingOrDefaultNode(
  92. patt , // start of ATTRIBUTE tree array.
  93. &dwNodeIndex, // Points to first node in chain
  94. dwOpt // may even take on the value DEFAULT_INIT
  95. ) )
  96. {
  97. if((patt[dwNodeIndex].eOffsetMeans == VALUE_AT_HEAP) &&
  98. *(PDWORD)(mpubOffRef + patt[dwNodeIndex].dwOffset) == BT_TRUE )
  99. {
  100. if(fnBCreateFeature)
  101. {
  102. if(!fnBCreateFeature(dwFea, dwOpt, dwNumSynFea, pglobl) )
  103. // featureIndex, optionIndex, index of SynFea
  104. {
  105. ERR(("DwCountSynthFeatures: Unable to create synthesized feature for installable option: fea=%d, opt=%d.\n",
  106. dwFea, dwOpt));
  107. pfo[dwFea].atrOptionSpawnsFeature = ATTRIB_UNINITIALIZED ;
  108. // destroys the entire attribute tree for this feature,
  109. // but what choice do we have? Something has gone terribly
  110. // wrong.
  111. }
  112. }
  113. dwNumSynFea++ ;
  114. }
  115. }
  116. }
  117. }
  118. if(fnBCreateFeature)
  119. BEnableInvInstallableCombos(pglobl) ;
  120. return(dwNumSynFea) ;
  121. }
  122. BOOL BCreateSynthFeatures(
  123. IN DWORD dwFea, // index of installable feature
  124. IN DWORD dwOpt, // index of installable Option or set to INVALID_INDEX
  125. IN DWORD dwSynFea, // index of synthesized feature
  126. IN OUT PGLOBL pglobl)
  127. {
  128. DWORD dwOptI , dwHeapOffset, dwNodeIndex, dwValue,
  129. dwPrevsNode, dwNewCnstRoot, dwJ, dwCNode ,
  130. dwNumFea, dwNumOpt, dwOut, dwIn ;
  131. BOOL bPrevsExists, bStatus = TRUE ;
  132. PDFEATURE_OPTIONS pfo, pfoSyn ;
  133. PGLOBALATTRIB pga ;
  134. PATTRIB_TREE patt ; // start of ATTRIBUTE tree array.
  135. PATREEREF patr ;
  136. PCONSTRAINTS pcnstr ; // start of CONSTRAINTS array.
  137. pcnstr = (PCONSTRAINTS) gMasterTable[MTI_CONSTRAINTS].pubStruct ;
  138. patt = (PATTRIB_TREE) gMasterTable[MTI_ATTRIBTREE].pubStruct ;
  139. pga = (PGLOBALATTRIB)gMasterTable[MTI_GLOBALATTRIB].pubStruct ;
  140. pfo = (PDFEATURE_OPTIONS) gMasterTable[MTI_DFEATURE_OPTIONS].pubStruct ;
  141. pfoSyn = (PDFEATURE_OPTIONS) gMasterTable[MTI_SYNTHESIZED_FEATURES].pubStruct ;
  142. dwNumFea = gMasterTable[MTI_DFEATURE_OPTIONS].dwArraySize ;
  143. // initialize all fields with UNINITIALIZED just like normal fea/opt;
  144. for(dwJ = 0 ; dwJ < gMasterTable[MTI_SYNTHESIZED_FEATURES].dwElementSiz /
  145. sizeof(ATREEREF) ; dwJ++)
  146. {
  147. ((PATREEREF)( (PDFEATURE_OPTIONS)gMasterTable[MTI_SYNTHESIZED_FEATURES].
  148. pubStruct + dwSynFea))[dwJ] =
  149. ATTRIB_UNINITIALIZED ; // the DFEATURE_OPTIONS struct is
  150. // comprised entirely of ATREEREFs.
  151. }
  152. // create links between installable feature/option and
  153. // the synthesized feature.
  154. if(dwOpt != INVALID_INDEX)
  155. {
  156. if(!BexchangeDataInFOATNode(
  157. dwFea,
  158. dwOpt,
  159. offsetof(DFEATURE_OPTIONS, atrOptionSpawnsFeature),
  160. &dwOut, // previous contents of attribute node
  161. &dwSynFea, FALSE, pglobl)) // new contents of attribute node.
  162. return(FALSE);
  163. // If this option is installable, this points to the
  164. // index of the resulting synthesized feature.
  165. }
  166. else
  167. pfo[dwFea].dwFeatureSpawnsFeature = dwSynFea;
  168. // If this feature is installable, this points to the
  169. // index of the resulting synthesized feature.
  170. // note because this is temporary information,
  171. // the index is stored directly into the atr node without
  172. // even a HEAP_OFFSET flag.
  173. // backlink to Feature/Option that created this syn feature.
  174. // note dwOpt always initializes properly even if invalid.
  175. pfoSyn[dwSynFea].dwInstallableFeatureIndex = dwFea ;
  176. pfoSyn[dwSynFea].dwInstallableOptionIndex = dwOpt ;
  177. // now initialize all other fields needed to establish
  178. // a legitimate feature with 2 options!
  179. // -------- Synthesize a Feature name. ------ //
  180. if(dwOpt == INVALID_INDEX)
  181. { // installable Feature
  182. pfoSyn[dwSynFea].atrFeaDisplayName =
  183. pfo[dwFea].atrInstallableFeaDisplayName ;
  184. pfoSyn[dwSynFea].atrFeaRcNameID =
  185. pfo[dwFea].atrInstallableFeaRcNameID ;
  186. }
  187. else // installable Option
  188. {
  189. if(!BexchangeDataInFOATNode(
  190. dwFea,
  191. dwOpt,
  192. offsetof(DFEATURE_OPTIONS, atrInstallableOptDisplayName ) ,
  193. &dwHeapOffset, // previous contents of attribute node
  194. NULL, FALSE, pglobl)) // NULL means Don't overwrite.
  195. return(FALSE) ;
  196. if(dwHeapOffset != INVALID_INDEX)
  197. {
  198. pfoSyn[dwSynFea].atrFeaDisplayName =
  199. dwHeapOffset | ATTRIB_HEAP_VALUE ;
  200. }
  201. if(!BexchangeDataInFOATNode(
  202. dwFea,
  203. dwOpt,
  204. offsetof(DFEATURE_OPTIONS, atrInstallableOptRcNameID ) ,
  205. &dwHeapOffset, // previous contents of attribute node
  206. NULL, FALSE, pglobl)) // NULL means Don't overwrite.
  207. return(FALSE) ;
  208. if(dwHeapOffset != INVALID_INDEX)
  209. {
  210. pfoSyn[dwSynFea].atrFeaRcNameID =
  211. dwHeapOffset | ATTRIB_HEAP_VALUE ;
  212. }
  213. }
  214. { // !!! new stuff
  215. PBYTE pubBaseKeyword = "SynthesizedFea_";
  216. BYTE aubNum[4] ;
  217. DWORD dwBaselen, dwDummy , dwI, dwNum = dwSynFea;
  218. ARRAYREF arSymbolName ;
  219. // compose featurekeyword string incorporating dwSynFea
  220. // convert dwSynFea into 3 digit number.
  221. for(dwI = 0 ; dwI < 3 ; dwI++)
  222. {
  223. aubNum[2 - dwI] = '0' + (BYTE)(dwNum % 10);
  224. dwNum /= 10 ;
  225. }
  226. aubNum[3] = '\0' ; // null terminate
  227. dwBaselen = strlen(pubBaseKeyword);
  228. if(!BwriteToHeap(&arSymbolName.loOffset,
  229. pubBaseKeyword, dwBaselen, 1, pglobl))
  230. return(FALSE);
  231. if(!BwriteToHeap(&dwDummy,
  232. aubNum, 4, 1, pglobl)) // append 3 digit number to base + null terminator
  233. return(FALSE);
  234. arSymbolName.dwCount = dwBaselen + 3 ;
  235. gmrbd.dwMaxPrnKeywordSize += arSymbolName.dwCount + 2 ;
  236. // add 2 bytes for every feature
  237. if(!BwriteToHeap(&(pfoSyn[dwSynFea].atrFeaKeyWord),
  238. (PBYTE)&arSymbolName, sizeof(ARRAYREF), 4, pglobl))
  239. return(FALSE);
  240. pfoSyn[dwSynFea].atrFeaKeyWord |= ATTRIB_HEAP_VALUE ;
  241. } // !!! end new stuff
  242. #if 0
  243. pfoSyn[dwSynFea].atrFeaKeyWord =
  244. pfo[dwFea].atrFeaKeyWord ; // just to fill something in.
  245. #endif
  246. // grab offsets to "Installed" and "Not Installed"
  247. // option name templates:
  248. if(BReadDataInGlobalNode(&pga->atrNameInstalled, &dwHeapOffset, pglobl) )
  249. {
  250. if(!BexchangeDataInFOATNode(
  251. dwSynFea,
  252. 1,
  253. offsetof(DFEATURE_OPTIONS, atrOptDisplayName) ,
  254. &dwOut, // previous contents of attribute node
  255. &dwHeapOffset, TRUE, pglobl) ) // new contents of attribute node.
  256. return(FALSE) ;
  257. }
  258. if(BReadDataInGlobalNode(&pga->atrNameNotInstalled, &dwHeapOffset, pglobl) )
  259. {
  260. if(!BexchangeDataInFOATNode(
  261. dwSynFea,
  262. 0,
  263. offsetof(DFEATURE_OPTIONS, atrOptDisplayName) ,
  264. &dwOut, // previous contents of attribute node
  265. &dwHeapOffset, TRUE, pglobl) ) // new contents of attribute node.
  266. return(FALSE) ;
  267. }
  268. if(BReadDataInGlobalNode(&pga->atrNameIDInstalled, &dwHeapOffset, pglobl) )
  269. {
  270. if(!BexchangeDataInFOATNode(
  271. dwSynFea,
  272. 1,
  273. offsetof(DFEATURE_OPTIONS, atrOptRcNameID) ,
  274. &dwOut, // previous contents of attribute node
  275. &dwHeapOffset, TRUE, pglobl) ) // new contents of attribute node.
  276. return(FALSE) ;
  277. }
  278. if(BReadDataInGlobalNode(&pga->atrNameIDNotInstalled, &dwHeapOffset, pglobl) )
  279. {
  280. if(!BexchangeDataInFOATNode(
  281. dwSynFea,
  282. 0,
  283. offsetof(DFEATURE_OPTIONS, atrOptRcNameID) ,
  284. &dwOut, // previous contents of attribute node
  285. &dwHeapOffset, TRUE, pglobl) ) // new contents of attribute node.
  286. return(FALSE) ;
  287. }
  288. pfoSyn[dwSynFea].dwGID = GID_UNKNOWN ;
  289. pfoSyn[dwSynFea].dwNumOptions = 2 ;
  290. // label this FeatureType as PrinterSticky
  291. dwValue = FT_PRINTERPROPERTY ;
  292. patr = &pfoSyn[dwSynFea].atrFeatureType ;
  293. if(!BwriteToHeap(patr, (PBYTE)&dwValue ,
  294. sizeof(DWORD), 4, pglobl) )
  295. {
  296. bStatus = FALSE ; // heap overflow start over.
  297. }
  298. *patr |= ATTRIB_HEAP_VALUE ;
  299. // leave optionID, atrFeaKeyWord, atrOptKeyWord uninitialized.
  300. { // !!! new stuff init atrOptKeyWord , hardcode to ON and OFF
  301. ARRAYREF arSymbolName ;
  302. if(!BwriteToHeap(&arSymbolName.loOffset,
  303. "OFF", 4, 1, pglobl))
  304. return(FALSE);
  305. arSymbolName.dwCount = 3 ;
  306. if(!BwriteToHeap(&dwHeapOffset,
  307. (PBYTE)&arSymbolName, sizeof(ARRAYREF), 4, pglobl))
  308. return(FALSE);
  309. if(!BexchangeDataInFOATNode(
  310. dwSynFea,
  311. 0,
  312. offsetof(DFEATURE_OPTIONS, atrOptKeyWord) ,
  313. &dwOut, // previous contents of attribute node
  314. &dwHeapOffset, TRUE, pglobl) ) // new contents of attribute node.
  315. return(FALSE) ;
  316. // ----- init "ON" -----
  317. if(!BwriteToHeap(&arSymbolName.loOffset,
  318. "ON", 3, 1, pglobl))
  319. return(FALSE);
  320. arSymbolName.dwCount = 2 ;
  321. if(!BwriteToHeap(&dwHeapOffset,
  322. (PBYTE)&arSymbolName, sizeof(ARRAYREF), 4, pglobl))
  323. return(FALSE);
  324. if(!BexchangeDataInFOATNode(
  325. dwSynFea,
  326. 1,
  327. offsetof(DFEATURE_OPTIONS, atrOptKeyWord) ,
  328. &dwOut, // previous contents of attribute node
  329. &dwHeapOffset, TRUE, pglobl) ) // new contents of attribute node.
  330. return(FALSE) ;
  331. gmrbd.dwMaxPrnKeywordSize += 4 ; // sufficient to hold "OFF\0".
  332. // note synthesized features are always pick_one.
  333. }
  334. // transfer atrOptInstallConstraints etc to atrConstraints.
  335. if(dwOpt != INVALID_INDEX)
  336. {
  337. if(!BexchangeDataInFOATNode(
  338. dwFea,
  339. dwOpt,
  340. offsetof(DFEATURE_OPTIONS, atrOptInstallConstraints ),
  341. &dwOut, // previous contents of attribute node
  342. NULL, // don't change contents of attribute node.
  343. FALSE , pglobl) ) // not synthetic feature
  344. return(FALSE);
  345. dwIn = dwOut ;
  346. if(dwIn != INVALID_INDEX)
  347. BexchangeDataInFOATNode(
  348. dwSynFea,
  349. 1, // "Installed"
  350. offsetof(DFEATURE_OPTIONS, atrConstraints) ,
  351. &dwOut, // previous contents of attribute node
  352. &dwIn, // new contents of attribute node.
  353. TRUE , pglobl) ;
  354. if(!BexchangeDataInFOATNode(
  355. dwFea,
  356. dwOpt,
  357. offsetof(DFEATURE_OPTIONS, atrOptNotInstallConstraints ),
  358. &dwOut, // previous contents of attribute node
  359. NULL, // don't change contents of attribute node.
  360. FALSE , pglobl) ) // not synthetic feature
  361. return(FALSE);
  362. dwIn = dwOut ;
  363. if(dwIn != INVALID_INDEX)
  364. BexchangeDataInFOATNode(
  365. dwSynFea,
  366. 0, // "Not Installed"
  367. offsetof(DFEATURE_OPTIONS, atrConstraints) ,
  368. &dwOut, // previous contents of attribute node
  369. &dwIn, // new contents of attribute node.
  370. TRUE , pglobl) ;
  371. }
  372. else
  373. {
  374. if(BReadDataInGlobalNode(&pfo[dwFea].atrFeaInstallConstraints, &dwHeapOffset, pglobl) )
  375. BexchangeDataInFOATNode(
  376. dwSynFea,
  377. 1, // "Installed"
  378. offsetof(DFEATURE_OPTIONS, atrConstraints) ,
  379. &dwOut, // previous contents of attribute node
  380. &dwHeapOffset, // new contents of attribute node.
  381. TRUE , pglobl) ;
  382. if(BReadDataInGlobalNode(&pfo[dwFea].atrFeaNotInstallConstraints , &dwHeapOffset, pglobl) )
  383. BexchangeDataInFOATNode(
  384. dwSynFea,
  385. 0, // "Not Installed"
  386. offsetof(DFEATURE_OPTIONS, atrConstraints) ,
  387. &dwOut, // previous contents of attribute node
  388. &dwHeapOffset, // new contents of attribute node.
  389. TRUE , pglobl) ;
  390. }
  391. // now synthesize: selecting option 0 constrains all
  392. // options of an Installable feature except option 0.
  393. // Selecting option 0 constrains an installable option.
  394. if(bStatus)
  395. bStatus = BallocElementFromMasterTable(MTI_CONSTRAINTS, &dwNewCnstRoot, pglobl) ;
  396. if(!bStatus)
  397. return(FALSE);
  398. dwCNode = dwNewCnstRoot ;
  399. if(dwOpt != INVALID_INDEX)
  400. { // installable option
  401. pcnstr[dwCNode].dwFeature = dwFea ;
  402. pcnstr[dwCNode].dwOption = dwOpt ;
  403. }
  404. else // installable feature
  405. {
  406. dwNumOpt = pfo[dwFea].dwNumOptions ;
  407. for(dwOptI = 1 ; bStatus && dwOptI < dwNumOpt ; dwOptI++)
  408. {
  409. pcnstr[dwCNode].dwFeature = dwFea ;
  410. pcnstr[dwCNode].dwOption = dwOptI ;
  411. if(dwOptI + 1 < dwNumOpt)
  412. {
  413. bStatus = BallocElementFromMasterTable(MTI_CONSTRAINTS,
  414. &pcnstr[dwCNode].dwNextCnstrnt, pglobl) ;
  415. dwCNode = pcnstr[dwCNode].dwNextCnstrnt ;
  416. }
  417. }
  418. }
  419. // get existing list and prepend new list to it.
  420. bStatus = BexchangeArbDataInFOATNode(
  421. dwSynFea,
  422. 0, // "Not Installed"
  423. offsetof(DFEATURE_OPTIONS, atrConstraints) ,
  424. sizeof(DWORD) , // number bytes to copy.
  425. (PBYTE)&dwPrevsNode, // pubOut
  426. (PBYTE)&dwNewCnstRoot, // pubIn
  427. &bPrevsExists, // previous contents existed?
  428. TRUE, // access the synthetic features.
  429. pglobl
  430. ) ;
  431. if(bPrevsExists)
  432. { // tack existing list onto new list.
  433. pcnstr[dwCNode].dwNextCnstrnt = dwPrevsNode ;
  434. }
  435. else
  436. {
  437. pcnstr[dwCNode].dwNextCnstrnt = END_OF_LIST ;
  438. }
  439. return(bStatus) ;
  440. }
  441. BOOL BEnableInvInstallableCombos(
  442. PGLOBL pglobl)
  443. {
  444. DWORD dwPrevsNode, dwRootNode, dwNewCombo , dwCurNode,
  445. dwFeaInstallable, dwOpt, dwSynFea, dwFeaOffset ;
  446. PDFEATURE_OPTIONS pfo;
  447. PGLOBALATTRIB pga ;
  448. PINVALIDCOMBO pinvc ; // start of InvalidCombo array
  449. pga = (PGLOBALATTRIB)gMasterTable[MTI_GLOBALATTRIB].pubStruct ;
  450. pfo = (PDFEATURE_OPTIONS) gMasterTable[MTI_DFEATURE_OPTIONS].pubStruct ;
  451. pinvc = (PINVALIDCOMBO) gMasterTable[MTI_INVALIDCOMBO].pubStruct ;
  452. dwFeaOffset = gMasterTable[MTI_DFEATURE_OPTIONS].dwArraySize ;
  453. dwRootNode = pga->atrInvldInstallCombo ;
  454. if(dwRootNode == ATTRIB_UNINITIALIZED)
  455. return(TRUE) ; // no InvalidInstallableCombos found.
  456. while(dwRootNode != END_OF_LIST)
  457. {
  458. dwNewCombo = pinvc[dwRootNode].dwNewCombo ;
  459. dwCurNode = dwRootNode ;
  460. while(dwCurNode != END_OF_LIST)
  461. {
  462. dwFeaInstallable = pinvc[dwCurNode].dwFeature ;
  463. dwOpt = pinvc[dwCurNode].dwOption ;
  464. if(dwOpt != (WORD)DEFAULT_INIT)
  465. { // this option is installable
  466. if(!BexchangeDataInFOATNode(
  467. dwFeaInstallable,
  468. dwOpt,
  469. offsetof(DFEATURE_OPTIONS, atrOptionSpawnsFeature),
  470. &dwSynFea, // previous contents of attribute node
  471. NULL, FALSE, pglobl)) // new contents of attribute node.
  472. return(FALSE);
  473. }
  474. else
  475. dwSynFea = pfo[dwFeaInstallable].dwFeatureSpawnsFeature ;
  476. pinvc[dwCurNode].dwFeature = dwSynFea + dwFeaOffset ;
  477. // dwSynFea is the index of the resulting
  478. // synthesized feature.
  479. pinvc[dwCurNode].dwOption = 1 ; // can't tolerate
  480. // all these things installed at the same time.
  481. if(!BexchangeDataInFOATNode(dwSynFea , 1,
  482. offsetof(DFEATURE_OPTIONS, atrInvalidCombos),
  483. &dwPrevsNode, &dwRootNode, TRUE, pglobl))
  484. return(FALSE);
  485. if(dwPrevsNode == INVALID_INDEX)
  486. pinvc[dwCurNode].dwNewCombo = END_OF_LIST ;
  487. else
  488. pinvc[dwCurNode].dwNewCombo = dwPrevsNode ;
  489. dwCurNode = pinvc[dwCurNode].dwNextElement ; // last line
  490. }
  491. dwRootNode = dwNewCombo ; // last line
  492. }
  493. return(TRUE);
  494. }