home *** CD-ROM | disk | FTP | other *** search
/ Programming Win32 Under the API / ProgrammingWin32UnderTheApiPatVillani.iso / Chapter9 / cmd32 / Cmd.c < prev    next >
C/C++ Source or Header  |  2000-07-10  |  16KB  |  717 lines

  1. //
  2. /*                                */
  3. /*                   cmd.c                */
  4. /*                                */
  5. /*             command.com Top Level Driver         */
  6. /*                                */
  7. /*             Copyright (c) 2000            */
  8. /*            Pasquale J. Villani            */
  9. /*            All Rights Reserved            */
  10. /*                                */
  11. /*                                */
  12. /* CMD32 is free software; you can redistribute it and/or    */
  13. /* modify it under the terms of the GNU General Public License    */
  14. /* as published by the Free Software Foundation; either version    */
  15. /* 2, or (at your option) any later version.            */
  16. /*                                */
  17. /* CMD32 is distributed in the hope that it will be useful, but    */
  18. /* WITHOUT ANY WARRANTY; without even the implied warranty of    */
  19. /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
  20. /* the GNU General Public License for more details.        */
  21. /*                                */
  22. /* You should have received a copy of the GNU General Public    */
  23. /* License along with CMD32; see the file COPYING.  If not,    */
  24. /* write to the Free Software Foundation, 675 Mass Ave,        */
  25. /* Cambridge, MA 02139, USA.                    */
  26. //
  27.  
  28.  
  29. // $Logfile$
  30.  
  31. /* $Log$ 
  32.  * $EndLog$ */
  33.  
  34. #include <windows.h>
  35. #include <stdarg.h>
  36. #include <stdlib.h>
  37. #include <ctype.h>
  38. #define MAIN
  39. #include "globals.h"
  40. #include "proto.h"
  41.  
  42. #ifdef VERSION_STRINGS
  43. static BYTE *RcsId = "$Header$";
  44. #endif
  45.  
  46. #define SIZE 4096
  47.  
  48. struct table
  49. {
  50.     BYTE    *str;
  51.     int    (*func)(INT, BYTE **);
  52. };
  53.  
  54. static BOOL
  55.     bCFlag;
  56.  
  57. struct table *lookup(struct table *, BYTE *);
  58. VOID err_report(INT);
  59. VOID put_prompt(BYTE *);
  60. VOID Redirect(BYTE *, BYTE *, BYTE *, BOOL *);
  61. VOID RestoreIO(HANDLE, HANDLE);
  62. static BOOL MatchCommand(BYTE *pszPattern, BYTE *pszCmd, BOOL *pbBatch);
  63.  
  64. BOOL ExecCmd(INT argc, BYTE *argv[]);
  65. BOOL CmdExit(INT argc, BYTE *argv[]);
  66. BOOL cd(INT argc, BYTE *argv[]);
  67. BOOL cmd_date(INT argc, BYTE *argv[]);
  68. BOOL cmd_time(INT argc, BYTE *argv[]);
  69. BOOL copy(INT argc, BYTE *argv[]);
  70. BOOL del(INT argc, BYTE *argv[]);
  71. BOOL dir(INT argc, BYTE *argv[]);
  72. BOOL echo_bat(INT argc, BYTE *argv[]);
  73. BOOL echo_dot_bat(INT argc, BYTE *argv[]);
  74. BOOL mkdir(INT argc, BYTE *argv[]);
  75. BOOL ren(INT argc, BYTE *argv[]);
  76. BOOL rmdir(INT argc, BYTE *argv[]);
  77. BOOL type(INT argc, BYTE *argv[]);
  78. BOOL ver(INT argc, BYTE *argv[]);
  79. BOOL Prompt(INT argc, BYTE *argv[]);
  80. BOOL cmd_path(INT argc, BYTE *argv[]);
  81. BOOL set_bat(INT argc, BYTE *argv[]);
  82. BOOL rem_bat(INT argc, BYTE *argv[]);
  83.  
  84. INT GetDrive(void);
  85.  
  86. // External commands
  87.  
  88. struct table  commands[] =
  89. {
  90.     {"cd",        cd},
  91.     {"copy",    copy},
  92.     {"date",    cmd_date},
  93.     {"del",        del},
  94.     {"dir",        dir},
  95.     {"echo",    echo_bat},
  96.     {"echo.",    echo_dot_bat},
  97.     {"echo+",    echo_dot_bat},
  98.     {"echo\"",    echo_dot_bat},
  99.     {"echo/",    echo_dot_bat},
  100.     {"echo[",    echo_dot_bat},
  101.     {"echo]",    echo_dot_bat},
  102.     {"echo:",    echo_dot_bat},
  103.     {"exit",    CmdExit},
  104.     {"md",        mkdir},
  105.     {"mkdir",    mkdir},
  106.     {"path",    cmd_path},
  107.     {"prompt",    Prompt},
  108.     {"rem",        rem_bat},
  109.     {"ren",        ren},
  110.     {"rmdir",    rmdir},
  111.     {"rd",        rmdir},
  112.     {"set",        set_bat},
  113.     {"time",    cmd_time},
  114.     {"type",    type},
  115.     {"ver",        ver},
  116.     {"",        ExecCmd}
  117. };
  118.  
  119. WORD printf (CONST BYTE *fmt, ...);
  120.  
  121. static INT argc;
  122. static BYTE *argv[NPARAMS];
  123.  
  124. static BOOL cflag = FALSE, bootup = FALSE;
  125.  
  126. INT
  127. main(INT argc, BYTE *argv[])
  128. {
  129.     BYTE szPath[SIZE] = "";
  130.     DWORD nRead;
  131.     BYTE *cmd_tail;
  132.  
  133.     /* First, establish stdin and stdout */
  134.     hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
  135.     hStdin  = GetStdHandle(STD_INPUT_HANDLE);
  136.     hStderr = GetStdHandle(STD_ERROR_HANDLE);
  137.     if(hStdin  == INVALID_HANDLE_VALUE
  138.     || hStdout == INVALID_HANDLE_VALUE
  139.     || hStderr == INVALID_HANDLE_VALUE)
  140.         return 0;
  141.  
  142.     /* Initialize our program */
  143.     hInput = hStdin;
  144.     hOutput = hStdout;
  145.     bCFlag = FALSE;
  146.     echo_FLAG = FALSE;
  147.     batch_FLAG = FALSE;
  148.     strcpy(szDfltPrompt, "$p$g ");
  149.     strcpy(szDfltPath, "");
  150.     hHeap = GetProcessHeap();
  151.     if(!hHeap)
  152.     {
  153.         return 1;
  154.     }
  155.     cmd_tail = GetCommandLine();
  156.  
  157.     /* Check what PROMPT is set in env to over ride default */
  158.     nRead = GetEnvironmentVariable("PROMPT", szPrompt, sizeof(szPrompt));
  159.     if(!nRead)
  160.     {
  161.         strcpy(szPrompt, szDfltPrompt);
  162.     }
  163.  
  164.  
  165.     /* Check what PATH is set in env to over ride default         */
  166.     pszPath = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, SIZE);
  167.     nRead = GetEnvironmentVariable("PATH", szPath, SIZE);
  168.     if(nRead)
  169.     {
  170.         strcpy(pszPath, szPath);
  171.     }
  172.     else
  173.     {
  174.         strcpy(pszPath, szDfltPath);
  175.     }
  176.  
  177.     /* Main body for code.   Either read from Stdin and execute    */
  178.     /* commands or simply execute just once if -c specified.    */
  179.     if(!cflag)
  180.     {
  181.         /* Announce our version                */
  182.         printf(ANNOUNCE, copyright);
  183.         printf("\n\n");
  184.  
  185.         FOREVER
  186.         {
  187.             default_drive = GetDrive();
  188.             put_prompt(szPrompt);
  189.             if(!ReadFile(hStdin, szCmdLine, MAX_CMDLINE, &nRead, 0))
  190.                 continue;
  191.             do_command(nRead);
  192.         }
  193.     }
  194.     else
  195.     {
  196.         BYTE FAR *p;
  197.  
  198.         default_drive = GetDrive();
  199.         for(p = cmd_tail; *p != '\r'; p++)
  200.         {
  201.             if(*p == '/' && (*(p + 1) == 'c' || *(p + 1) == 'C'))
  202.                 break;
  203.         }
  204.         p += 2;
  205.         strncpy(szCmdLine, p, 0x7f);
  206.         for(nRead = 0; *p != '\r'; nRead++, p++)
  207.             ;
  208.         ++nRead;
  209.         do_command(nRead);
  210.     }
  211.     return 0;
  212. }
  213.  
  214.  
  215. VOID Redirect(BYTE *pszCmdLine,
  216.     BYTE *pszInput, BYTE *pszOutput, 
  217.     BOOL *pbAppendMode)
  218. {
  219.     BYTE
  220.         szLocalBuffer[MAX_CMDLINE],
  221.         *pszLine, *pszDest = pszCmdLine;
  222.     SECURITY_ATTRIBUTES saAttr;
  223.  
  224.     // First - create an image, since we'll be copying back into
  225.     // the original buffer.
  226.     strcpy(szLocalBuffer, pszCmdLine);
  227.  
  228.     // Initialize the destination names for later use.
  229.     *pszInput = *pszOutput = '\0';
  230.  
  231.     // Next, start looking for redirect symbols.
  232.     pszLine = skipwh(szLocalBuffer);
  233.     while(*pszLine != '\0')
  234.     {
  235.         switch(*pszLine)
  236.         {
  237.         case '<':
  238.             pszLine = scan(++pszLine, pszInput);
  239.             break;
  240.  
  241.         case '>':
  242.             if(*(pszLine + 1) == '>')
  243.             {
  244.                 ++pszLine;
  245.                 *pbAppendMode = TRUE;
  246.             }
  247.             else
  248.             {
  249.                 *pbAppendMode = FALSE;
  250.             }
  251.             pszLine = scan(++pszLine, pszOutput);
  252.             break;
  253.  
  254.         default:
  255.             *pszDest++ = *pszLine++;
  256.             break;
  257.         }
  258.     }
  259.     *pszDest = '\0';
  260.  
  261.     // Set the bInheritHandle flag so file handles are inherited.
  262.     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
  263.     saAttr.bInheritHandle = TRUE;
  264.     saAttr.lpSecurityDescriptor = NULL;
  265.  
  266.     // Now that we have the requested redirection, time
  267.     // to perform it on the users behalh.
  268.     if(*pszInput)
  269.     {
  270.         hInput = CreateFile(
  271.             pszInput, // pointer to name of the file  
  272.             GENERIC_READ, // access (read-write) mode  
  273.             FILE_SHARE_READ, // share mode 
  274.             &saAttr, // pointer to security attributes  
  275.             OPEN_EXISTING, // how to open  
  276.             FILE_ATTRIBUTE_NORMAL, // file attributes  
  277.             0);  // handle to file with attributes to copy  
  278.         if(hInput == INVALID_HANDLE_VALUE)
  279.         {
  280.             hInput = hStdin;
  281.         }
  282.         else
  283.         {
  284.             SetStdHandle(STD_INPUT_HANDLE, hInput);
  285.         }
  286.     }
  287.     if(*pszOutput)
  288.     {
  289.         hOutput = CreateFile(
  290.             pszOutput,           // create output file 
  291.             GENERIC_WRITE,                // open for writing 
  292.             0,                            // do not share 
  293.             NULL,                         // no security 
  294.             CREATE_ALWAYS,                // overwrite existing 
  295.             FILE_ATTRIBUTE_NORMAL |       // normal file 
  296.             FILE_FLAG_OVERLAPPED,         // asynchronous I/O 
  297.             NULL);                        // no attr. template 
  298.         if(hOutput == INVALID_HANDLE_VALUE)
  299.         {
  300.             hOutput = hStdout;
  301.         }
  302.         else
  303.         {
  304.             SetStdHandle(STD_OUTPUT_HANDLE, hOutput);
  305.         }
  306.     }
  307. }
  308.  
  309.  
  310.  
  311.  
  312. VOID do_command(INT nRead)
  313. {
  314.     BYTE *pszLine;
  315.     struct table *pTable;
  316.     INT nIndex = 0;
  317.     BOOL IORedirected = FALSE;
  318.     BYTE szInput[MAX_CMDLINE], szOutput[MAX_CMDLINE];
  319.     BOOL bAppendMode;
  320.  
  321.     // If nothing to do, just return
  322.     if(nRead <= 0)
  323.         return;
  324.  
  325.     // Initialize local variables
  326.     szCmdLine[nRead] = '\0';
  327.     bAppendMode = FALSE;
  328.  
  329.     // Parse for command line redirection.
  330.     Redirect(szCmdLine, szInput, szOutput, &bAppendMode);
  331.     IORedirected = (*szInput != '\0' || *szOutput != '\0');
  332.  
  333.     // Now parse for local command arguments.
  334.     for(argc = 0; argc < 16; argc++)
  335.     {
  336.         argv[argc] = (BYTE *)0;
  337.         args[argc][0] = '\0';
  338.     }
  339.     pszLine = scanspl(szCmdLine, args[0], '/');
  340.  
  341.     if(args[0][0] == '@')
  342.     {
  343.         at_FLAG = TRUE;
  344.         nIndex++;
  345.     }
  346.     else
  347.         at_FLAG = FALSE;
  348.  
  349.     // If preceeded by a @, swallow it, it was taken care of
  350.     // elsewhere.  Also, change case so that our command verb is
  351.     // case insensitive.
  352.     while(args[0][nIndex] != '\0')
  353.     {
  354.  
  355.         if(at_FLAG)
  356.             args[0][nIndex-1] = tolower(args[0][nIndex]);
  357.         else
  358.             args[0][nIndex] = tolower(args[0][nIndex]);
  359.         nIndex++;
  360.     }
  361.     if(at_FLAG)
  362.         args[0][nIndex-1] = '\0';
  363.  
  364.     argv[0] = args[0];
  365.     // this kludge is for a win32 wart emulation (see ExecCmd)
  366.     tail = skipwh(pszLine);
  367.  
  368.     for(argc = 1; argc < NPARAMS; argc++)
  369.     {
  370.         pszLine = scan(pszLine, args[argc]);
  371.         if(*args[argc] == '\0')
  372.             break;
  373.         else
  374.             argv[argc] = args[argc];
  375.     }
  376.  
  377.     if(*argv[0] != '\0')
  378.     {
  379.         /* Look for just a drive change command, and execute    */
  380.         /* it if found.                        */
  381.         if(argv[0][1] == ':' && argv[0][2] == '\0')
  382.         {
  383.             BYTE c = argv[0][0];
  384.  
  385.             if(c >= 'a' && c <= 'z')
  386.                 c = c - 'a' + 'A';
  387.             if(c >= 'A' && c <= 'Z')
  388.                 default_drive = (c - 'A');
  389.         }
  390.  
  391.         /* It may be a help command request.            */
  392.         else if( (argc > 1) &&
  393.              (argv[1][0] == switchchar) &&
  394.              (argv[1][1] == '?'))
  395.         {
  396.             strcpy(tail, " ");
  397.             strcat(tail, argv[0]);
  398.             strcat(tail, "\r\n");
  399.             argc = 2;
  400.             argv[1] = argv[0];
  401.             argv[0] = "help";
  402.             argv[2] = 0;
  403.             ExecCmd(argc, argv);
  404.             if(IORedirected)
  405.                 RestoreIO(hStdin, hStdout);
  406.         }
  407.         /* do a normal command execution            */
  408.         else
  409.         {
  410. #ifdef DEBUG
  411.             printf("Looking up %s\n", argv[0]);
  412. #endif
  413.             pTable = lookup(commands, argv[0]);
  414.             (*(pTable -> func))(argc, argv);
  415.             if(IORedirected)
  416.                 RestoreIO(hStdin, hStdout);
  417.         }
  418.     }
  419. }
  420.  
  421.  
  422. BOOL Prompt(INT argc, BYTE *argv[])
  423. {
  424.     BYTE *p;
  425.     BYTE *cmd = "PROMPT";
  426.  
  427.     if(argc == 1)
  428.     {
  429.         strcpy(szPrompt, dflt_pr_string);
  430.         SetEnvironmentVariable(cmd, szPrompt);
  431.     }
  432.     else
  433.     {
  434.         /* Trim trailing newline                */
  435.         for(p = tail; (*p != '\r') && (*p != '\n'); p++)
  436.             ;
  437.         *p = '\0';
  438.  
  439.         /* should be scopy(argv[1], &pr_string[1]); but to    */
  440.         /* emulate an MS-DOS wart, is                */
  441.         strcpy(szPrompt, tail);
  442.  
  443.         /* Now set the environment variable for all children to    */
  444.         /* see.                            */
  445.         SetEnvironmentVariable(cmd, szPrompt);
  446.     }
  447.     return TRUE;
  448. }
  449.  
  450.  
  451. struct table *lookup(p, token)
  452. struct table *p;
  453. BYTE *token;
  454. {
  455.     while(*(p -> str) != '\0')
  456.     {
  457.         if(strcmp(p -> str, token) == 0)
  458.             break;
  459.         else
  460.             ++p;
  461.     }
  462.     return p;
  463. }
  464.  
  465.  
  466. VOID RestoreIO(HANDLE hDupStdin, HANDLE hDupStdout)
  467. {
  468.     // After process creation, restore the saved STDIN and STDOUT.  
  469.     if (!SetStdHandle(STD_INPUT_HANDLE, hDupStdin))
  470.         error_message(CANNOT_RESTORE);
  471.     
  472.     if (!SetStdHandle(STD_OUTPUT_HANDLE, hDupStdout))
  473.         error_message(CANNOT_RESTORE);
  474.     if(hOutput != hStdout)
  475.     {
  476.         CloseHandle(hOutput);
  477.         hOutput = hDupStdout;
  478.     }
  479.     if(hInput != hStdin)
  480.     {
  481.         CloseHandle(hInput);
  482.         hInput = hDupStdin;
  483.     }
  484. }
  485.  
  486.  
  487. static BOOL MatchCommand(BYTE *pszPattern, BYTE *pszCmd, BOOL *pbBatch)
  488. {
  489.     WORD nIdx;
  490.     BYTE szPattern[SIZE];
  491.     WIN32_FIND_DATA  dmp;
  492.     HANDLE hDir;
  493.     struct _Ext
  494.     {
  495.         BYTE  *pszExt;
  496.         BOOL   bBatch;
  497.     };
  498. #define NUMEXT 3
  499.     static struct _Ext exTable[NUMEXT] =
  500.     {
  501.         {".bat", TRUE},
  502.         {".exe", FALSE},
  503.         {".com", FALSE}
  504.     };
  505.  
  506.     // First, see if we already have a desired extension.  If so, try finding it.
  507.     for(nIdx = 0; nIdx < NUMEXT; nIdx++)
  508.     {
  509.         if((strlen(pszPattern) >4)
  510.             && (!_stricmp(&pszPattern[strlen(pszPattern)-4], exTable[nIdx].pszExt)))
  511.         {
  512.             if((hDir = FindFirstFile((LPCTSTR)pszPattern, (LPWIN32_FIND_DATA)&dmp))
  513.              != INVALID_HANDLE_VALUE)
  514.             {
  515.                 if(pszCmd)
  516.                 {
  517.                     strcpy(pszCmd, pszPattern);
  518.                 }
  519.                 CloseHandle(hDir);
  520.                 return TRUE;
  521.             }
  522.             else
  523.             {
  524.                 return FALSE;
  525.             }
  526.         }
  527.     }
  528.  
  529.     // OK, we need to check, in order, for the command.
  530.     for(nIdx = 0; nIdx < NUMEXT; nIdx++)
  531.     {
  532.         strcpy(szPattern, pszPattern);
  533.         strcat(szPattern, exTable[nIdx].pszExt);
  534.         if((hDir = FindFirstFile((LPCTSTR)szPattern, (LPWIN32_FIND_DATA)&dmp))
  535.          != INVALID_HANDLE_VALUE)
  536.         {
  537.             if(pszCmd)
  538.             {
  539.                 strcpy(pszCmd, szPattern);
  540.             }
  541.             CloseHandle(hDir);
  542.             return TRUE;
  543.         }
  544.     }
  545.  
  546.     // Didn't find it, return an error.
  547.     return FALSE;
  548. }
  549.  
  550.  
  551. BOOL ExecCmd(INT argc, BYTE *argv[])
  552. {
  553.     PROCESS_INFORMATION piProcInfo;
  554.     SECURITY_ATTRIBUTES saAttr;
  555.     BOOL bRetVal, bFound = FALSE, bBatch = FALSE;
  556.     STARTUPINFO siStartInfo;
  557.     WORD nIdx;
  558.     BYTE szChildCmdLine[MAX_CMDLINE];
  559.     BYTE szPath[SIZE] = "", *pszPath, *pszTerm;
  560.     DWORD nRead;
  561.  
  562.     // See if there's a PATH spec out there somewhere.  If not, use the default.
  563.     nRead = GetEnvironmentVariable("PATH", szPath, SIZE);
  564.     if(!nRead)
  565.     {
  566.         strcpy(szPath, szDfltPath);
  567.     }
  568.  
  569.  
  570.     // Go through and search the path.  Loop through the path looking
  571.     // for the command.  Look in the current directory
  572.     // first. If it matches a local file, copy in actual file name.
  573.     if(MatchCommand(argv[0], szChildCmdLine, &bBatch))
  574.     {
  575.         bFound = TRUE;
  576.     }
  577.     else
  578.     {
  579.         for(pszPath = szPath, pszTerm = szPath; *pszTerm ; pszPath = pszTerm)
  580.         {
  581.             // Isolate a path component to search
  582.             while(*pszTerm && (*pszTerm != ';'))
  583.             {
  584.                 ++pszTerm;
  585.             }
  586.             if(*pszTerm == ';')
  587.             {
  588.                 *pszTerm = '\0';
  589.                 ++pszTerm;
  590.             }
  591.  
  592.             strcpy(szChildCmdLine, pszPath);
  593.             strcat(szChildCmdLine, "\\");
  594.             strcat(szChildCmdLine, argv[0]);
  595.             if(MatchCommand(szChildCmdLine, 0, &bBatch))
  596.             {
  597.                 bFound = TRUE;
  598.                 break;
  599.             }
  600.         }
  601.     }
  602.  
  603.     // Did we find it?  If not, report an error and return
  604.     if(!bFound)
  605.     {
  606.         error_message(FILE_NOT_FOUND);
  607.         return FALSE;
  608.     }
  609.  
  610.     // Build a command line to execute 
  611.     for(nIdx = 1; nIdx < argc; ++nIdx)
  612.     {
  613.         strcat(szChildCmdLine, " ");
  614.         strcat(szChildCmdLine, argv[nIdx]);
  615.     }
  616.  
  617.     // Set the bInheritHandle flag so file handles are inherited.
  618.     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
  619.     saAttr.bInheritHandle = TRUE;
  620.     saAttr.lpSecurityDescriptor = NULL;
  621.  
  622.     // Set up members of STARTUPINFO structure.  
  623.     ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
  624.     siStartInfo.cb = sizeof(STARTUPINFO);    // Create the child process. 
  625.     
  626.     // The following is somewhat inetersting.  We don't specify a module
  627.     // name because of NT stream operating systems.  These require a
  628.     // null as the module in order to execute 16-bit programs.
  629.     // Yet another Microsoft "design feature."
  630.     bRetVal = CreateProcess(NULL,
  631.         szChildCmdLine,    // command line 
  632.         &saAttr,        // process security attributes 
  633.         NULL,        // primary thread security attributes 
  634.         TRUE,        // handles are inherited 
  635.         0,        // creation flags 
  636.         NULL,        // use parent's environment 
  637.         NULL,        // use parent's current directory 
  638.         &siStartInfo,    // STARTUPINFO pointer 
  639.         &piProcInfo);    // receives PROCESS_INFORMATION
  640.  
  641.     // Wait for the process to exit, if we didn't have an error
  642.     if(bRetVal)
  643.     {
  644.         WaitForSingleObject(piProcInfo.hProcess, INFINITE);
  645.     }
  646.     else
  647.     {
  648.         error_message(EXEC_ERR);
  649.     }
  650.  
  651.     return bRetVal;
  652. }
  653.  
  654.  
  655. BOOL CmdExit(INT argc, BYTE FAR *argv[])
  656. {
  657. #ifdef DEBUG
  658.     printf("Entered CmdExit\n");
  659. #endif
  660.     /* If no values passed, return errorvalue = 0            */
  661.     if(argc == 1)
  662.         ExitProcess(0);
  663.  
  664.     /* otherwise return what the user asked for            */
  665.     else
  666.     {
  667.         INT nRetVal;
  668.         static BYTE szNums[] = "0123456789";
  669.         BYTE *pszNum;
  670.  
  671.         for(nRetVal = 0, pszNum = argv[1]; isdigit(*pszNum); pszNum++)
  672.         {
  673.             INT j;
  674.  
  675.             for(j = 0; j < 10; j++)
  676.                 if(szNums[j] == *pszNum)
  677.                     break;
  678.             nRetVal += j;
  679.         }
  680.         ExitProcess(nRetVal);
  681.     }
  682.         return TRUE;
  683. }
  684.  
  685.  
  686.  
  687. INT GetDrive(void)
  688. {
  689.     BYTE directory[NAMEMAX];
  690.  
  691.     GetCurrentDirectory(NAMEMAX, directory);
  692.     return (directory[0] - 'A');
  693.  
  694. }
  695.  
  696.  
  697. /* Win32 adaptation of printf generic output routine */
  698. void HandleChar(BYTE **pszChar, INT cChar)
  699. {
  700.     BYTE szBuffer[2];
  701.     DWORD cWritten;
  702.  
  703.     if(pszChar && *pszChar != 0)
  704.     {
  705.             *(*pszChar)++ = cChar;
  706.             **pszChar = '\0';
  707.     }
  708.     else
  709.     {
  710.         szBuffer[0] = cChar;
  711.         szBuffer[1] = '\0';
  712.         WriteFile(hOutput, szBuffer, 1, &cWritten, NULL);
  713.     }
  714. }
  715.  
  716.  
  717.