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.

999 lines
30 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. prodfilt.c
  5. Abstract:
  6. This module implements a program that filters text files
  7. to produce a product-specific output file.
  8. See below for more information.
  9. Author:
  10. Ted Miller (tedm) 20-May-1995
  11. Revision History:
  12. --*/
  13. /*
  14. The input file to this program consists of a series of lines.
  15. Each line may be prefixed with one or more directives that
  16. indicate which product the line is a part of. Lines that are
  17. not prefixed are part of all products.
  18. The command line is as follows:
  19. prodfilt <input file> <output file> +tag
  20. For example,
  21. [Files]
  22. @w:wksprog1.exe
  23. @w:wksprog2.exe
  24. @s:srvprog1.exe
  25. @s:srvprog2.exe
  26. comprog1.exe
  27. @@:comprog2.exe
  28. The files wksprog1.exe and wksprog2.exe are part of product w
  29. and the files srvprog1.exe and srvprog2.exe are part of product s.
  30. Comprpg1.exe and comprog2.exe are part of both products.
  31. Specifying +w on the command line produces
  32. [Files]
  33. wksprog1.exe
  34. wksprog2.exe
  35. comprog1.ee
  36. comprog2.exe
  37. in the output.
  38. */
  39. #include <windows.h>
  40. #include <stdio.h>
  41. #include <tchar.h>
  42. //
  43. // Define program result codes (returned from main()).
  44. //
  45. #define SUCCESS 0
  46. #define FAILURE 1
  47. //
  48. // Tag definitions.
  49. //
  50. LPCTSTR TagPrefixStr = TEXT("@");
  51. TCHAR TagPrefix = TEXT('@');
  52. TCHAR EndTag = TEXT(':');
  53. TCHAR ExcludeTag = TEXT('!');
  54. TCHAR IncAllTag = TEXT('@');
  55. TCHAR NoIncTag = TEXT('*');
  56. #define TAG_PREFIX_LENGTH 1
  57. #define MIN_TAG_LEN 3
  58. #define EXCLUDE_PREFIX_LENGTH 2 // ! Prefix Length (!n)
  59. //
  60. // Here is the translation for the character symbols used in IncludeTag
  61. // and SynonymTag.
  62. //
  63. // products:
  64. // @w -> wks
  65. // @s -> srv
  66. // @p -> personal (NOT professional)
  67. // @t -> tablet
  68. // @b -> blade server
  69. // @l -> small business server
  70. // @e -> enterprise
  71. // @d -> datacenter
  72. //
  73. // architectures:
  74. // @i -> intel (i386)
  75. // @n -> intel (nec98)
  76. // @m -> intel (ia64)
  77. // @a -> AMD64 (amd64)
  78. //
  79. // Note that @n is a subset of @i. If @i is specified then you also
  80. // get the file include for @n unless you explicitly exclude them
  81. // with a token like @i!n
  82. //
  83. // @3 -> 32 bit (i386+?)
  84. // @6 -> 64 bit (ia64+amd64)
  85. //
  86. TCHAR IncludeTag[][3] = {
  87. { TEXT('i'), TEXT('n'), 0}, // @i -> i or n, @i!n -> only i
  88. { TEXT('s'), TEXT('e'), 0}, // @s -> s or e, @s!e -> only s
  89. { TEXT('s'), TEXT('b'), 0}, // @s -> s or b, @s!b -> only s
  90. { TEXT('s'), TEXT('d'), 0},
  91. { TEXT('s'), TEXT('l'), 0},
  92. { TEXT('e'), TEXT('d'), 0},
  93. { TEXT('w'), TEXT('p'), 0},
  94. { TEXT('w'), TEXT('t'), 0},
  95. { 0 , 0 , 0}
  96. };
  97. TCHAR SynonymTag[][2] = {
  98. { TEXT('3'), TEXT('i')},
  99. { TEXT('6'), TEXT('a')},
  100. { TEXT('6'), TEXT('m')},
  101. { 0, 0}
  102. };
  103. LPCTSTR InName,OutName;
  104. TCHAR Filter;
  105. BOOL
  106. FileExists(
  107. IN PCTSTR FileName,
  108. OUT PWIN32_FIND_DATA FindData OPTIONAL
  109. )
  110. /*++
  111. Routine Description:
  112. Determine if a file exists and is accessible.
  113. Errormode is set (and then restored) so the user will not see
  114. any pop-ups.
  115. Arguments:
  116. FileName - supplies full path of file to check for existance.
  117. FindData - if specified, receives find data for the file.
  118. Return Value:
  119. TRUE if the file exists and is accessible.
  120. FALSE if not. GetLastError() returns extended error info.
  121. --*/
  122. {
  123. WIN32_FIND_DATA findData;
  124. HANDLE FindHandle;
  125. UINT OldMode;
  126. DWORD Error;
  127. OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  128. FindHandle = FindFirstFile(FileName,&findData);
  129. if (FindHandle == INVALID_HANDLE_VALUE) {
  130. Error = GetLastError();
  131. } else {
  132. FindClose(FindHandle);
  133. if (FindData) {
  134. *FindData = findData;
  135. }
  136. Error = NO_ERROR;
  137. }
  138. SetErrorMode(OldMode);
  139. SetLastError(Error);
  140. return (Error == NO_ERROR);
  141. }
  142. void
  143. StripCommentsFromLine(
  144. TCHAR *Line
  145. )
  146. /*
  147. Routine Description:
  148. Strips comments (; Comment) from a line respecting ; inside quotes
  149. Arguments:
  150. Line - POinter to Line to process
  151. */
  152. {
  153. PWSTR p;
  154. BOOL Done = FALSE, InQuotes = FALSE;
  155. //
  156. // we need to insert a NULL at the first ';' we find,
  157. // thereby stripping the comments
  158. //
  159. p = Line;
  160. if ( !p )
  161. return;
  162. while (*p && !Done) {
  163. switch (*p) {
  164. case TEXT(';'):
  165. if ( !InQuotes ) {
  166. *p=L'\r';
  167. *(p+1)=L'\n';
  168. *(p+2)=L'\0';
  169. Done = TRUE;
  170. }
  171. break;
  172. case TEXT('\"'):
  173. if ( *(p+1) && (*(p+1) == TEXT('\"'))) // Ignore double-quoting as inside quotes
  174. p++;
  175. else
  176. InQuotes = !InQuotes;
  177. default:
  178. ;
  179. }
  180. p++;
  181. }// while
  182. return;
  183. }
  184. DWORD
  185. MakeSurePathExists(
  186. IN PCTSTR FullFilespec
  187. )
  188. /*++
  189. Routine Description:
  190. This routine ensures that a multi-level path exists by creating individual
  191. levels one at a time. It is assumed that the caller will pass in a *filename*
  192. whose path needs to exist. Some examples:
  193. c:\x - C:\ is assumes to always exist.
  194. c:\x\y\z - Ensure that c:\x\y exists.
  195. \x\y\z - \x\y on current drive
  196. x\y - x in current directory
  197. d:x\y - d:x
  198. \\server\share\p\file - \\server\share\p
  199. Arguments:
  200. FullFilespec - supplies the *filename* of a file that the caller wants to
  201. create. This routine creates the *path* to that file, in other words,
  202. the final component is assumed to be a filename, and is not a
  203. directory name. (This routine doesn't actually create this file.)
  204. If this is invalid, then the results are undefined (for example,
  205. passing \\server\share, C:\, or C:).
  206. Return Value:
  207. Win32 error code indicating outcome. If FullFilespec is invalid,
  208. *may* return ERROR_INVALID_NAME.
  209. --*/
  210. {
  211. TCHAR Buffer[MAX_PATH];
  212. PTCHAR p,q;
  213. BOOL Done;
  214. DWORD d;
  215. WIN32_FIND_DATA FindData;
  216. //
  217. // The first thing we do is locate and strip off the final component,
  218. // which is assumed to be the filename. If there are no path separator
  219. // chars then assume we have a filename in the current directory and
  220. // that we have nothing to do.
  221. //
  222. // Note that if the caller passed an invalid FullFilespec then this might
  223. // hose us up. For example, \\x\y will result in \\x. We rely on logic
  224. // in the rest of the routine to catch this.
  225. //
  226. lstrcpyn(Buffer,FullFilespec,MAX_PATH);
  227. if (Buffer[0] && (p = _tcsrchr(Buffer,TEXT('\\'))) && (p != Buffer)) {
  228. *p = 0;
  229. } else {
  230. return (NO_ERROR);
  231. }
  232. if (Buffer[0] == TEXT('\\')) {
  233. if (Buffer[1] == TEXT('\\')) {
  234. //
  235. // UNC. Locate the second component, which is the share name.
  236. // If there's no share name then the original FullFilespec
  237. // was invalid. Finally locate the first path-sep char in the
  238. // drive-relative part of the name. Note that there might not
  239. // be such a char (when the file is in the root). Then skip
  240. // the path-sep char.
  241. //
  242. if (!Buffer[2] || (Buffer[2] == TEXT('\\'))) {
  243. return (ERROR_INVALID_NAME);
  244. }
  245. p = _tcschr(&Buffer[3],TEXT('\\'));
  246. if (!p || (p[1] == 0) || (p[1] == TEXT('\\'))) {
  247. return (ERROR_INVALID_NAME);
  248. }
  249. if (q = _tcschr(p+2,TEXT('\\'))) {
  250. q++;
  251. } else {
  252. return (NO_ERROR);
  253. }
  254. } else {
  255. //
  256. // Assume it's a local root-based local path like \x\y.
  257. //
  258. q = Buffer+1;
  259. }
  260. } else {
  261. if (Buffer[1] == TEXT(':')) {
  262. //
  263. // Assume c:x\y or maybe c:\x\y
  264. //
  265. q = (Buffer[2] == TEXT('\\')) ? &Buffer[3] : &Buffer[2];
  266. } else {
  267. //
  268. // Assume path like x\y\z
  269. //
  270. q = Buffer;
  271. }
  272. }
  273. //
  274. // Ignore drive roots.
  275. //
  276. if (*q == 0) {
  277. return (NO_ERROR);
  278. }
  279. //
  280. // If it already exists do nothing.
  281. //
  282. if (FileExists(Buffer,&FindData)) {
  283. return ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? NO_ERROR : ERROR_DIRECTORY);
  284. }
  285. Done = FALSE;
  286. do {
  287. //
  288. // Locate the next path sep char. If there is none then
  289. // this is the deepest level of the path.
  290. //
  291. if (p = _tcschr(q,TEXT('\\'))) {
  292. *p = 0;
  293. } else {
  294. Done = TRUE;
  295. }
  296. //
  297. // Create this portion of the path.
  298. //
  299. if (CreateDirectory(Buffer,NULL)) {
  300. d = NO_ERROR;
  301. } else {
  302. d = GetLastError();
  303. if (d == ERROR_ALREADY_EXISTS) {
  304. d = NO_ERROR;
  305. }
  306. }
  307. if (d == NO_ERROR) {
  308. //
  309. // Put back the path sep and move to the next component.
  310. //
  311. if (!Done) {
  312. *p = TEXT('\\');
  313. q = p+1;
  314. }
  315. } else {
  316. Done = TRUE;
  317. }
  318. } while (!Done);
  319. return (d);
  320. }
  321. BOOL
  322. ParseArgs(
  323. IN int argc,
  324. IN TCHAR *argv[],
  325. OUT BOOL *Unicode,
  326. OUT BOOL *StripComments
  327. )
  328. {
  329. int argoffset = 0;
  330. int loopcount;
  331. *Unicode = FALSE;
  332. *StripComments = FALSE;
  333. if (argc == 5) {
  334. //
  335. // 1 switch possible
  336. //
  337. argoffset = 1;
  338. loopcount = 1;
  339. } else if (argc == 6) {
  340. //
  341. // 2 switches possible
  342. //
  343. argoffset = 2;
  344. loopcount = 2;
  345. } else if (argc != 4) {
  346. return (FALSE);
  347. } else {
  348. argoffset = 0;
  349. }
  350. if ((argc == 5) || (argc == 6)) {
  351. int i;
  352. for (i=0; i< loopcount; i++) {
  353. if (!_tcsicmp(argv[i+1],TEXT("/u")) || !_tcsicmp(argv[i+1],TEXT("-u"))) {
  354. *Unicode = TRUE;
  355. }
  356. if (!_tcsicmp(argv[i+1],TEXT("/s")) || !_tcsicmp(argv[i+1],TEXT("-s"))) {
  357. *StripComments = TRUE;
  358. }
  359. }
  360. }
  361. InName = argv[1+argoffset];
  362. OutName = argv[2+argoffset];
  363. Filter = argv[3+argoffset][1];
  364. if (argv[3+argoffset][0] != TEXT('+')) {
  365. return (FALSE);
  366. }
  367. return (TRUE);
  368. }
  369. BOOL
  370. DoTagsMatch(
  371. IN TCHAR LineChar,
  372. IN TCHAR Tag
  373. )
  374. {
  375. int i, j;
  376. BOOL ReturnValue = FALSE;
  377. BOOL TagIsInList = FALSE;
  378. BOOL LineCharIsInList = FALSE;
  379. //
  380. // If they match, we're done.
  381. //
  382. if ( LineChar == Tag ) {
  383. ReturnValue = TRUE;
  384. } else {
  385. //
  386. // Nope. See if we can match a synonym tag.
  387. //
  388. i = 0;
  389. while ( SynonymTag[i][0] ) {
  390. TagIsInList = FALSE;
  391. LineCharIsInList = FALSE;
  392. for ( j = 0; j < 2; j++ ) {
  393. if ( Tag == SynonymTag[i][j] ) {
  394. TagIsInList = TRUE;
  395. }
  396. if ( LineChar == SynonymTag[i][j] ) {
  397. LineCharIsInList = TRUE;
  398. }
  399. }
  400. if ( TagIsInList && LineCharIsInList ) {
  401. ReturnValue = TRUE;
  402. }
  403. i++;
  404. }
  405. }
  406. return ReturnValue;
  407. }
  408. BOOL
  409. DoFilter(
  410. IN FILE *InputFile,
  411. IN FILE *OutputFile,
  412. IN TCHAR Tag,
  413. IN BOOL UnicodeFileIO,
  414. IN BOOL StripComments
  415. )
  416. {
  417. TCHAR Line[1024];
  418. TCHAR *OutputLine;
  419. BOOL FirstLine=TRUE;
  420. BOOL WriteUnicodeHeader = TRUE;
  421. while (!feof(InputFile)) {
  422. //
  423. // read a line of data. if we're doing unicode IO, we just read the
  424. // data and use it otherwise we need to read the data and
  425. //
  426. if (UnicodeFileIO) {
  427. if (!fgetws(Line,sizeof(Line)/sizeof(Line[0]),InputFile)) {
  428. if (ferror(InputFile)) {
  429. _ftprintf(stderr,TEXT("Error reading from input file\n"));
  430. return (FALSE);
  431. } else {
  432. return (TRUE);
  433. }
  434. }
  435. //
  436. // Skip byte order mark if present
  437. //
  438. if (FirstLine) {
  439. if (Line[0] == 0xfeff) {
  440. MoveMemory(Line,Line+1,sizeof(Line)-sizeof(TCHAR));
  441. }
  442. }
  443. } else {
  444. char LineA[1024];
  445. if (!fgets(LineA,sizeof(LineA),InputFile)) {
  446. if (ferror(InputFile)) {
  447. _ftprintf(stderr,TEXT("Error reading from input file\n"));
  448. return (FALSE);
  449. } else {
  450. return (TRUE);
  451. }
  452. }
  453. if (!MultiByteToWideChar(
  454. CP_ACP,
  455. MB_PRECOMPOSED,
  456. LineA,
  457. -1,
  458. Line,
  459. sizeof(Line)/sizeof(WCHAR)
  460. )) {
  461. _ftprintf(stderr,TEXT("Error reading input file\n"));
  462. return (FALSE);
  463. }
  464. }
  465. //
  466. // ok, we've retreived the line. now let's see if we want to output
  467. // the line.
  468. //
  469. OutputLine = Line;
  470. //
  471. // if the line is too short, then we just want to include it no matter
  472. // what.
  473. //
  474. if (_tcslen(Line) >= MIN_TAG_LEN) {
  475. int i;
  476. //
  477. // if the line starts with our tag, then we need to look further
  478. // to see if it should be filtered.
  479. //
  480. if (!_tcsncmp(Line,TagPrefixStr,TAG_PREFIX_LENGTH)) {
  481. //
  482. // is the symbol string an @<char>: combination?
  483. //
  484. if (Line[TAG_PREFIX_LENGTH+1] == EndTag) {
  485. OutputLine = NULL;
  486. //
  487. // Do we have @@: or @<char>:, where <char> matches the tag
  488. // prodfilt was invoked with?
  489. //
  490. if ( (Line[TAG_PREFIX_LENGTH] == IncAllTag) ||
  491. DoTagsMatch(Line[TAG_PREFIX_LENGTH],Tag) ||
  492. (Tag == IncAllTag &&
  493. (Line[TAG_PREFIX_LENGTH] != NoIncTag)) ) {
  494. //
  495. // Yes. Include this line.
  496. //
  497. OutputLine = Line+MIN_TAG_LEN;
  498. } else {
  499. //
  500. // we don't have an explicit match, so let's look to
  501. // see if we have a match by virtue of another tag
  502. // including our tag
  503. //
  504. // To do this, we look at the include tag list to see
  505. // if the line matches the head of an include tag
  506. // entry. If we have a match, then we check if we have
  507. // a match in the specified inclusion entry for our tag
  508. // (or one if it's synonyms)
  509. //
  510. //
  511. int j;
  512. i = 0;
  513. while (IncludeTag[i][0] && !OutputLine) {
  514. j = 1;
  515. if (DoTagsMatch(Line[TAG_PREFIX_LENGTH],
  516. IncludeTag[i][0])) {
  517. //
  518. // we found a match at the start of an include
  519. // entry.
  520. //
  521. while (IncludeTag[i][j]) {
  522. if (DoTagsMatch(
  523. IncludeTag[i][j],
  524. Tag)) {
  525. //
  526. // We found an included match for our
  527. // tag. Include this line.
  528. //
  529. OutputLine = Line+MIN_TAG_LEN;
  530. break;
  531. }
  532. j++;
  533. }
  534. }
  535. i++;
  536. }
  537. }
  538. //
  539. // is the line long enough to have an @<char>!<char> sequence?
  540. //
  541. } else if (_tcslen(Line) >=
  542. (MIN_TAG_LEN+EXCLUDE_PREFIX_LENGTH)) {
  543. //
  544. // Does the line have @<char>!<char> syntax?
  545. //
  546. if (Line[TAG_PREFIX_LENGTH+1] == ExcludeTag) {
  547. TCHAR * tmpPtr = &Line[TAG_PREFIX_LENGTH+1];
  548. OutputLine = NULL;
  549. //
  550. // We have @<char_a>!<char_b> syntax. We first need to
  551. // see if the line is included by virtue of <char_a>.
  552. //
  553. // If that succeeds, then we proceed onto reading the
  554. // !<char>!<char> block, looking for another hit.
  555. //
  556. //
  557. // do we have an explicit match?
  558. //
  559. if ( Line[TAG_PREFIX_LENGTH] == IncAllTag ||
  560. DoTagsMatch(Line[TAG_PREFIX_LENGTH],Tag) ||
  561. (Tag == IncAllTag &&
  562. Line[TAG_PREFIX_LENGTH] != NoIncTag) ) {
  563. //
  564. // Yes, we have an explicit match. Remember this
  565. // so we can parse the !<char> sequence.
  566. //
  567. OutputLine = Line+MIN_TAG_LEN;
  568. } else {
  569. //
  570. // we don't have an explicit match, so let's look to
  571. // see if we have a match by virtue of another tag
  572. // including our tag
  573. //
  574. // To do this, we look at the include tag list to
  575. // see if the line matches the head of an include
  576. // tag entry. If we have a match, then we check if
  577. // we have a match in the specified inclusion entry
  578. // for our tag (or one if it's synonyms)
  579. //
  580. //
  581. int j;
  582. i = 0;
  583. while (IncludeTag[i][0] && !OutputLine) {
  584. j = 1;
  585. if (DoTagsMatch(Line[TAG_PREFIX_LENGTH],
  586. IncludeTag[i][0])) {
  587. //
  588. // we found a match at the start of an
  589. // include entry.
  590. //
  591. while (IncludeTag[i][j]) {
  592. if (DoTagsMatch(
  593. IncludeTag[i][j],
  594. Tag)) {
  595. //
  596. // We found an included match for
  597. // our tag. Include this line.
  598. //
  599. OutputLine = Line+MIN_TAG_LEN;
  600. break;
  601. }
  602. j++;
  603. }
  604. }
  605. i++;
  606. }
  607. }
  608. if (!OutputLine) {
  609. //
  610. // We didn't match teh initial @<char> sequence, so
  611. // there is no need to check further. goto the
  612. // next line.
  613. //
  614. goto ProcessNextLine;
  615. }
  616. //
  617. // The line has !<char>[!<char>] combination
  618. // Loop through the chain of exclude chars and see if
  619. // we have any hits. if we do, then we go onto the
  620. // next line.
  621. //
  622. while (tmpPtr[0] == ExcludeTag) {
  623. //
  624. // do we have an explicit match?
  625. //
  626. if ( (tmpPtr[TAG_PREFIX_LENGTH] == IncAllTag) ||
  627. DoTagsMatch(tmpPtr[TAG_PREFIX_LENGTH],Tag) ) {
  628. //
  629. // We have an explicit match, so we know we
  630. // do not want to include this line.
  631. //
  632. //
  633. OutputLine = NULL;
  634. goto ProcessNextLine;
  635. } else {
  636. //
  637. // we don't have an explicit match, so let's
  638. // look to see if we have a match by virtue
  639. // of another tag including our tag
  640. //
  641. // To do this, we look at the include tag list
  642. // to see if the line matches the head of an
  643. // include tag entry. If we have a match, then
  644. // we check if we have a match in the specified
  645. // inclusion entry for our tag (or one if it's
  646. // synonyms)
  647. //
  648. //
  649. int j;
  650. i = 0;
  651. while (IncludeTag[i][0]) {
  652. j = 1;
  653. if (DoTagsMatch(
  654. tmpPtr[TAG_PREFIX_LENGTH],
  655. IncludeTag[i][0])) {
  656. //
  657. // we found a match at the start of an
  658. // include entry.
  659. //
  660. while (IncludeTag[i][j]) {
  661. if (DoTagsMatch(
  662. IncludeTag[i][j],
  663. Tag)) {
  664. //
  665. // We found an included match
  666. // for our tag, so we know we
  667. // do not want to include this
  668. // line.
  669. //
  670. OutputLine = NULL;
  671. goto ProcessNextLine;
  672. }
  673. j++;
  674. }
  675. }
  676. i++;
  677. }
  678. }
  679. tmpPtr += 2;
  680. }
  681. //
  682. // Done parsing the @<char>!<char>!<char>!... tokens.
  683. // Look for the terminator
  684. //
  685. if (tmpPtr[0] != EndTag) {
  686. //
  687. // Malformed tokens. Let's error on the
  688. // conservative side and include the whole line.
  689. //
  690. OutputLine = Line;
  691. } else {
  692. //
  693. // Didn't find any exclusions, so include the tag.
  694. //
  695. OutputLine = &tmpPtr[1];
  696. }
  697. }
  698. }
  699. }
  700. }
  701. ProcessNextLine:
  702. //
  703. // write out the line if we're supposed to. for unicode io we just
  704. // write the file. for ansi i/o we have to convert back to ansi
  705. //
  706. if (OutputLine) {
  707. if (StripComments) {
  708. StripCommentsFromLine( OutputLine );
  709. }
  710. if (UnicodeFileIO) {
  711. if (WriteUnicodeHeader) {
  712. fputwc(0xfeff,OutputFile);
  713. WriteUnicodeHeader = FALSE;
  714. }
  715. if (fputws(OutputLine,OutputFile) == EOF) {
  716. _ftprintf(stderr,TEXT("Error writing to output file\n"));
  717. return (FALSE);
  718. }
  719. } else {
  720. CHAR OutputLineA[1024];
  721. if (!WideCharToMultiByte(
  722. CP_ACP,
  723. 0,
  724. OutputLine,
  725. -1,
  726. OutputLineA,
  727. sizeof(OutputLineA)/sizeof(CHAR),
  728. NULL,
  729. NULL)) {
  730. _ftprintf(
  731. stderr,
  732. TEXT("Error translating string for output file\n") );
  733. return (FALSE);
  734. }
  735. if (!fputs(OutputLineA,OutputFile) == EOF) {
  736. _ftprintf(stderr,TEXT("Error writing to output file\n"));
  737. return (FALSE);
  738. }
  739. }
  740. }
  741. }
  742. if (ferror(InputFile)) {
  743. _ftprintf(stderr,TEXT("Error reading from input file\n"));
  744. return (FALSE);
  745. }
  746. return (TRUE);
  747. }
  748. int
  749. __cdecl
  750. _tmain(
  751. IN int argc,
  752. IN TCHAR *argv[]
  753. )
  754. {
  755. FILE *InputFile;
  756. FILE *OutputFile;
  757. BOOL b;
  758. BOOL Unicode,StripComments;
  759. //
  760. // Assume failure.
  761. //
  762. b = FALSE;
  763. if (ParseArgs(argc,argv,&Unicode,&StripComments)) {
  764. //
  765. // Open the files. Have to open in binary mode or else
  766. // CRT converts unicode text to mbcs on way in and out.
  767. //
  768. if (InputFile = _tfopen(InName,TEXT("rb"))) {
  769. if (MakeSurePathExists(OutName) == NO_ERROR) {
  770. if (OutputFile = _tfopen(OutName,TEXT("wb"))) {
  771. //
  772. // Do the filtering operation.
  773. //
  774. _ftprintf(stdout,
  775. TEXT("%s: filtering %s to %s\n"),
  776. argv[0],
  777. InName,
  778. OutName);
  779. b = DoFilter(InputFile,
  780. OutputFile,
  781. Filter,
  782. Unicode,
  783. StripComments);
  784. fclose(OutputFile);
  785. } else {
  786. _ftprintf(stderr,
  787. TEXT("%s: Unable to create output file %s\n"),
  788. argv[0],
  789. OutName);
  790. }
  791. } else {
  792. _ftprintf(stderr,
  793. TEXT("%s: Unable to create output path %s\n"),
  794. argv[0],
  795. OutName);
  796. }
  797. fclose(InputFile);
  798. } else {
  799. _ftprintf(stderr,
  800. TEXT("%s: Unable to open input file %s\n"),
  801. argv[0],
  802. InName);
  803. }
  804. } else {
  805. _ftprintf(stderr,
  806. TEXT("Usage: %s [-u (unicode IO)] [-s (strip comments)] <input file> <output file> +<prodtag>\n"),
  807. argv[0]);
  808. }
  809. return (b ? SUCCESS : FAILURE);
  810. }