home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / netds / sna / shared / cpicport.c < prev    next >
Text File  |  1997-04-09  |  20KB  |  575 lines

  1. /*****************************************************************************
  2.  *
  3.  *  MODULE NAME: CPICPORT.C
  4.  *
  5.  *  COPYRIGHTS:
  6.  *             This module contains code made available by IBM
  7.  *             Corporation on an AS IS basis.  Any one receiving the
  8.  *             module is considered to be licensed under IBM copyrights
  9.  *             to use the IBM-provided source code in any way he or she
  10.  *             deems fit, including copying it, compiling it, modifying
  11.  *             it, and redistributing it, with or without
  12.  *             modifications.  No license under any IBM patents or
  13.  *             patent applications is to be implied from this copyright
  14.  *             license.
  15.  *
  16.  *             A user of the module should understand that IBM cannot
  17.  *             provide technical support for the module and will not be
  18.  *             responsible for any consequences of use of the program.
  19.  *
  20.  *             Any notices, including this one, are not to be removed
  21.  *             from the module without the prior written consent of
  22.  *             IBM.
  23.  *
  24.  *  AUTHOR:    Peter J. Schwaller
  25.  *             VNET:     PJS at RALVM6           Tie Line: 444-4376
  26.  *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
  27.  *
  28.  *  FUNCTION:  Contains procedures to that may have to be rewritten for
  29.  *             different environments.
  30.  *
  31.  *  AVAILABILITY:
  32.  *             These sample programs and source are also available on
  33.  *             CompuServe through the APPC Information Exchange.  To get
  34.  *             to the APPC forum just type 'GO APPC' from any CompuServe
  35.  *             prompt.  The samples are available in the Sample Programs
  36.  *             library section.  Just search on the keyword CPICPGMS to
  37.  *             find all the samples in this series.
  38.  *
  39.  *             Updates for the sample programs and support for many more
  40.  *             CPI-C platforms will also be made available on CompuServe.
  41.  *
  42.  *  RELATED FILES:
  43.  *             CPICPORT.H
  44.  *
  45.  *  PORTABILITY NOTES:
  46.  *             This file is the home of all operating system specific
  47.  *             functions.  The following is a summary of the routines
  48.  *             in this file and where they are used:
  49.  *
  50.  *             write_output()
  51.  *                 displays a text string to normal output
  52.  *
  53.  *             write_error()
  54.  *                 displays error text to error output
  55.  *
  56.  *             write_log()
  57.  *                 logs text to an opened log file
  58.  *                 if the log file was not open, will display to error output
  59.  *
  60.  *             display_message()
  61.  *                 Delivers a text message.
  62.  *                 Used by ATELLD.C.
  63.  *
  64.  *             get_time()
  65.  *                 Returns time in milliseconds.
  66.  *                 Used by APING.C.
  67.  *
  68.  *             alloc_cpic_buffer()
  69.  *                 Allocates the best memory buffer for CPI-C performance.
  70.  *                 Used by APING.C and APINGD.C.
  71.  *
  72.  *             show_info()
  73.  *                 Displays an array of text strings.
  74.  *                 Used by all files with a main() function.
  75.  *
  76.  *             get_machine_mode()
  77.  *                 For family API applications; determines whether we
  78.  *                 are running under OS/2 or DOS.
  79.  *
  80.  *             get_password()
  81.  *                 Used to request that the user enter a password.
  82.  *                 If possible, the password will not display while the
  83.  *                 user types it.
  84.  *                 Used by CPICINIT.C
  85.  *
  86.  *             execute_and_send_output()
  87.  *                 Execute the specified command and send the output back
  88.  *                 to the client side.
  89.  *                 This routine is called by AREXECD.C.
  90.  *
  91.  *             do_exit()
  92.  *                 Exit properly for the environment.  This is usually
  93.  *                 necessary for GUI environments.
  94.  *
  95.  *             free_cpic_buffer()
  96.  *                 Frees the memory allocated by alloc_cpic_buffer().
  97.  *                 Used by MPINGD.C.
  98.  *
  99.  *  CHANGE HISTORY:
  100.  *  Date       Description
  101.  *  08/05/92   Version 2.31 of APING, ATELL and AREXEC released to CompuServe.
  102.  *             This version was also distributed at the APPC/APPN Platform
  103.  *             Developer's Conference held in Raleigh, NC.
  104.  *  08/13/92   Added the write_*() calls.
  105.  *             Changed all printf and fprintf calls to use a write_*() call.
  106.  *  08/19/92   Added workaround for problem with system() returning non-null
  107.  *             even when command succeeded.  The fix is in DOS and FAPI
  108.  *             versions of execute_and_send_output().
  109.  *  08/20/92   Fixed alloc_cpic_buffer() so it will use a shared buffer
  110.  *             under OS/2 2.0.
  111.  *  08/23/92   Fixed extra rc definition for AS/400 definition of the
  112.  *             execute_and_send_output() routine.
  113.  *  08/24/92   Version 2.32 released to CompuServe.
  114.  *  08/25/92   Changed DOS and AIX execute_and_send_output() to use the
  115.  *             tempnam() function instead of the tmpnam() function.  This
  116.  *             results in tempfiles being written to the TMP directory.
  117.  *  09/22/92   Version 2.33 released to CompuServe.
  118.  *  01/07/93   Added OS2_20 to list of conditional compile directives in
  119.  *             the following routines:
  120.  *               get_time
  121.  *               get_password
  122.  *               set_echo
  123.  *               execute_and_send_output
  124.  *  11/11/94   Wrote free_cpic_buffer()
  125.  *
  126.  *****************************************************************************/
  127.  
  128. #if defined(WIN32) || defined(WINDOWS)
  129. #include <windows.h>
  130. #endif
  131.  
  132. #include "wincpic.h"
  133.  
  134. #include "cpicerrs.h"
  135.  
  136. /* Set up constant declarations */
  137. #include "cpicdefs.h"
  138.  
  139. #include "cpicport.h"
  140.  
  141.  
  142. /* standard C include files */
  143. #include <stdio.h>
  144. #include <stdlib.h>
  145. #include <string.h>
  146. #include <stdarg.h>
  147. #include <time.h>
  148.  
  149. extern char ebcdic_to_ascii_table[];
  150. extern char ascii_to_ebcdic_table[];
  151.  
  152.  
  153.  
  154. void write_error(char *fmt, ...)
  155. {
  156.     /*=========================================================================
  157.      *
  158.      *=======================================================================*/
  159.  
  160.     va_list args;
  161.  
  162.     va_start(args, fmt);
  163.     vfprintf(stderr, fmt, args);
  164.     va_end(args);
  165. }
  166.  
  167.  
  168. void write_output(char *fmt, ...)
  169. {
  170.     /*=========================================================================
  171.      *
  172.      *=======================================================================*/
  173.  
  174.     va_list args;
  175.  
  176.     va_start(args, fmt);
  177.     vfprintf(stdout, fmt, args);
  178.     va_end(args);
  179. }
  180.  
  181. void write_log(FILE * file, char *fmt, ...)
  182. {
  183.     /*=========================================================================
  184.      *
  185.      *=======================================================================*/
  186.  
  187.     va_list args;
  188.  
  189.     va_start(args, fmt);
  190.     if (file == NULL) {
  191.         write_error(fmt, args);
  192.     } else {
  193.         vfprintf(file, fmt, args);
  194.     }
  195.     va_end(args);
  196. }
  197.  
  198. /*
  199.  * display_message()
  200.  *       delivers a text message
  201.  *       used by ATELLD.C
  202.  *
  203.  *    default
  204.  *       Uses C library functions to print the message.
  205.  */
  206. void
  207. display_message(char * origin, char * dest_userid, char * message)
  208. {
  209.  
  210.     char timestamp[TIMELENGTH];
  211.     struct tm * newtime;
  212.     time_t ltime;
  213.  
  214.     time(<ime);
  215.     newtime = localtime(<ime);
  216.     strcpy(timestamp, asctime(newtime));
  217.  
  218.     /* eliminate new line character for display */
  219.     timestamp[strlen(timestamp)-1] = '\0';
  220.  
  221.     write_output("\n\n  msg from %s ", origin);
  222.     if (dest_userid[0] != '\0') {
  223.         write_output("to user %s ", dest_userid);
  224.     }
  225.     write_output("on %s:", timestamp);
  226.     write_output("\n\n     %s\n\n",message);
  227. }
  228.  
  229.  
  230.  
  231. /*
  232.  * get_time()
  233.  *       returns time in milliseconds
  234.  *       used by APING.C
  235.  *
  236.  */
  237. unsigned long
  238. get_time(void)
  239. {
  240.  
  241.     return GetTickCount();
  242.  
  243. }
  244.  
  245.  
  246. /*
  247.  * alloc_cpic_buffer()
  248.  */
  249.  
  250. char CM_PTR
  251. alloc_cpic_buffer (unsigned int size)
  252. {
  253.    return malloc(size);
  254. }
  255.  
  256. /*
  257.  * freec_cpic_buffer()
  258.  */
  259.  
  260. void
  261. free_cpic_buffer( void *memblock )
  262. {
  263.     free( memblock );
  264. }
  265.  
  266.  
  267. /*
  268.  * show_info()
  269.  *
  270.  *       This procedure displays a block of text information on the the
  271.  *       screen.  The input argument is an array of strings to be output,
  272.  *       one string per line.  A NULL array element indicates the end of
  273.  *       the strings.
  274.  *
  275.  *    default
  276.  *       print out all of the text strings in the array using the cpicport
  277.  *       write_output() call.
  278.  */
  279. void
  280. show_info(char * * text)
  281. {
  282.     int i;
  283.  
  284.     for ( i = 0; text[i] != NULL; i++ ) {
  285.         write_output("%s\n", text[i]);
  286.     }
  287.     return;
  288. }
  289.  
  290. /*
  291.  * get_password()
  292.  *
  293.  *    Gets a password from the user.  Where possible, this routine should
  294.  *    disable echoing of keystrokes for security reasons.
  295.  *
  296.  *    Returns
  297.  *       0 - password was successfully input
  298.  *       1 - password variable was not updated successfully
  299.  */
  300. int
  301. get_password(char * password, int max_length)
  302. {
  303.     int rc;
  304.     int length;
  305.  
  306.     /* There is no portable way to disable echoing of input keystrokes.      */
  307.     /* If a platform does support turning off echo, this section should be   */
  308.     /* rewritten and ifdef'ed.                                               */
  309.  
  310.     if (NULL != fgets(password, max_length+1, stdin)) {
  311.         length = strlen(password);
  312.         if (length > 0 && length < max_length) {
  313.             if (password[length-1] == '\n') { /* remove the trailing         */
  314.                 password[length-1] = '\0';    /* newline if it exists        */
  315.             }
  316.             rc = 0;
  317.         } else {
  318.             rc = 1;
  319.         }
  320.  
  321.     } else {
  322.         rc = 1;
  323.     }
  324.  
  325.     return rc;
  326. }
  327.  
  328. /*
  329.  * do_exit()
  330.  *
  331.  * Exit properly for the environment we're running in.
  332.  */
  333. void
  334. do_exit(int rc)
  335. {
  336. #ifdef EXITTHREAD
  337.     ExitThread(rc);
  338. #else
  339.     WinCPICCleanup();
  340.     exit(rc);
  341. #endif
  342. }
  343.  
  344. /*
  345.  * The ASCII<-->EBCDIC character set translation routines are implemented
  346.  * below.  These procedures should never be called directly, but should
  347.  * be accessed through the convert_to_ascii() and convert_from_ascii()
  348.  * macros.  This frees the application program from knowing whether
  349.  * it is being coded on an ASCII or EBCDIC computer (this must be
  350.  * determined in the macro definition in CPICPORT.H).
  351.  */
  352.  
  353. void   ascii_to_ebcdic_field  (unsigned char * ascii_field,
  354.                                unsigned int field_size)
  355. {
  356.     unsigned int i;
  357.  
  358.     for (i = 0;
  359.          i < field_size;
  360.          ascii_field[i] = ascii_to_ebcdic_table[(unsigned)ascii_field[i]],i++);
  361. }
  362.  
  363. void  ascii_to_ebcdic_string (unsigned char * ascii_string)
  364. {
  365.     ascii_to_ebcdic_field(ascii_string, strlen((char*)ascii_string));
  366. }
  367.  
  368. void   ebcdic_to_ascii_field  (unsigned char * ebcdic_field,
  369.                                unsigned int field_size)
  370. {
  371.     unsigned int i;
  372.  
  373.     for (i = 0;
  374.          i < field_size;
  375.          ebcdic_field[i] =
  376.                          ebcdic_to_ascii_table[(unsigned)ebcdic_field[i]],i++);
  377. }
  378.  
  379. void  ebcdic_to_ascii_string (unsigned char * ebcdic_string)
  380. {
  381.     ebcdic_to_ascii_field(ebcdic_string, strlen((char*)ebcdic_string));
  382. }
  383.  
  384.  
  385.  
  386. /* ASCII to EBCDIC translate table (only UGL character set) */
  387.  
  388. char ascii_to_ebcdic_table[] = {
  389. "\x00\x01\x02\x03\x37\x2D\x2E\x2F\x16\x05\x15\x0B\x0C\x0D\x0E\x0F"  /* 00-0F */
  390. "\x10\x11\x12\x13\x3C\x3D\x32\x26\x18\x19\x3F\x27\x22\x1D\x35\x1F"  /* 10-1F */
  391. "\x40\x5A\x7F\x7B\x5B\x6C\x50\x7D\x4D\x5D\x5C\x4E\x6B\x60\x4B\x61"  /* 20-2F */
  392. "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\x7A\x5E\x4C\x7E\x6E\x6F"  /* 30-3F */
  393. "\x7C\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xD1\xD2\xD3\xD4\xD5\xD6"  /* 40-4F */
  394. "\xD7\xD8\xD9\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xAD\xE0\xBD\x5F\x6D"  /* 50-5F */
  395. "\x79\x81\x82\x83\x84\x85\x86\x87\x88\x89\x91\x92\x93\x94\x95\x96"  /* 60-6F */
  396. "\x97\x98\x99\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xC0\x4F\xD0\xA1\x07"  /* 70-7F */
  397. "\x43\x20\x21\x1C\x23\xEB\x24\x9B\x71\x28\x38\x49\x90\xBA\xEC\xDF"  /* 80-8F */
  398. "\x45\x29\x2A\x9D\x72\x2B\x8A\x9A\x67\x56\x64\x4A\x53\x68\x59\x46"  /* 90-9F */
  399. "\xEA\xDA\x2C\xDE\x8B\x55\x41\xFE\x58\x51\x52\x48\x69\xDB\x8E\x8D"  /* A0-AF */
  400. "\x73\x74\x75\xFA\x15\xB0\xB1\xB3\xB4\xB5\x6A\xB7\xB8\xB9\xCC\xBC"  /* B0-BF */
  401. "\xAB\x3E\x3B\x0A\xBF\x8F\x3A\x14\xA0\x17\xCB\xCA\x1A\x1B\x9C\x04"  /* C0-CF */
  402. "\x34\xEF\x1E\x06\x08\x09\x77\x70\xBE\xBB\xAC\x54\x63\x65\x66\x62"  /* D0-DF */
  403. "\x30\x42\x47\x57\xEE\x33\xB6\xE1\xCD\xED\x36\x44\xCE\xCF\x31\xAA"  /* E0-EF */
  404. "\xFC\x9E\xAE\x8C\xDD\xDC\x39\xFB\x80\xAF\xFD\x78\x76\xB2\x9F\xFF"  /* F0-FF */
  405. };
  406.  
  407. /* EBCDIC to ASCII translate table (only UGL character set) */
  408.  
  409. char ebcdic_to_ascii_table[] = {
  410. "\x00\x01\x02\x03\xCF\x09\xD3\x7F\xD4\xD5\xC3\x0B\x0C\x0D\x0E\x0F"  /* 00-0F */
  411. "\x10\x11\x12\x13\xC7\x0A\x08\xC9\x18\x19\xCC\xCD\x83\x1D\xD2\x1F"  /* 10-1F */
  412. "\x81\x82\x1C\x84\x86\x0A\x17\x1B\x89\x91\x92\x95\xA2\x05\x06\x07"  /* 20-2F */
  413. "\xE0\xEE\x16\xE5\xD0\x1E\xEA\x04\x8A\xF6\xC6\xC2\x14\x15\xC1\x1A"  /* 30-3F */
  414. "\x20\xA6\xE1\x80\xEB\x90\x9F\xE2\xAB\x8B\x9B\x2E\x3C\x28\x2B\x7C"  /* 40-4F */
  415. "\x26\xA9\xAA\x9C\xDB\xA5\x99\xE3\xA8\x9E\x21\x24\x2A\x29\x3B\x5E"  /* 50-5F */
  416. "\x2D\x2F\xDF\xDC\x9A\xDD\xDE\x98\x9D\xAC\xBA\x2C\x25\x5F\x3E\x3F"  /* 60-6F */
  417. "\xD7\x88\x94\xB0\xB1\xB2\xFC\xD6\xFB\x60\x3A\x23\x40\x27\x3D\x22"  /* 70-7F */
  418. "\xF8\x61\x62\x63\x64\x65\x66\x67\x68\x69\x96\xA4\xF3\xAF\xAE\xC5"  /* 80-8F */
  419. "\x8C\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x97\x87\xCE\x93\xF1\xFE"  /* 90-9F */
  420. "\xC8\x7E\x73\x74\x75\x76\x77\x78\x79\x7A\xEF\xC0\xDA\x5B\xF2\xF9"  /* A0-AF */
  421. "\xB5\xB6\xFD\xB7\xB8\xB9\xE6\xBB\xBC\xBD\x8D\xD9\xBF\x5D\xD8\xC4"  /* B0-BF */
  422. "\x7B\x41\x42\x43\x44\x45\x46\x47\x48\x49\xCB\xCA\xBE\xE8\xEC\xED"  /* C0-CF */
  423. "\x7D\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\xA1\xAD\xF5\xF4\xA3\x8F"  /* D0-DF */
  424. "\x5C\xE7\x53\x54\x55\x56\x57\x58\x59\x5A\xA0\x85\x8E\xE9\xE4\xD1"  /* E0-EF */
  425. "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\xB3\xF7\xF0\xFA\xA7\xFF"  /* F0-FF */
  426. };
  427.  
  428. void
  429. execute_and_send_output(char * command,
  430.                         unsigned char * cm_conv_id,
  431.                         struct error_handler_cpicerr * cpicerr)
  432. {
  433.  
  434.    DWORD lasterror = 0;
  435.    HANDLE hWritePipe = NULL;
  436.    HANDLE hReadPipe = NULL;
  437.    SECURITY_ATTRIBUTES SecAttrib = {0};
  438.    STARTUPINFO StartInfo = {0};
  439.    PROCESS_INFORMATION ProcInfo = {0};
  440.    unsigned char * buffer=NULL;
  441.    DWORD BufferSize=10000;
  442.    DWORD BytesRead=0;
  443.    DWORD BytesInPipe=0;
  444.    DWORD WaitState=0;
  445.    BOOL ProcessDead=FALSE;
  446.    CM_INT32 rts_received;
  447.    CM_INT32 cm_rc;
  448.    char NewCommand[255] = {0};
  449.  
  450.    strcpy(NewCommand,"cmd /c ");
  451.    strcat(NewCommand,command);
  452.  
  453.    buffer=malloc(BufferSize+2);
  454.  
  455.    SecAttrib.nLength = sizeof(SECURITY_ATTRIBUTES);
  456.    SecAttrib.lpSecurityDescriptor = NULL;
  457.    SecAttrib.bInheritHandle = TRUE;
  458.  
  459.    /**************************************************************************/
  460.    /* Create named pipe and get handle to read end of the pipe.              */
  461.    /**************************************************************************/
  462.    if ((hReadPipe = CreateNamedPipe("\\\\.\\pipe\\arexecpipe",
  463.                PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE, 1, BufferSize,
  464.                BufferSize, 1000,&SecAttrib)) == INVALID_HANDLE_VALUE)
  465.    {
  466.       lasterror = GetLastError();
  467.       printf("CreateNamedPipe returned %d",lasterror);
  468.       return;
  469.    }
  470.  
  471.    /**************************************************************************/
  472.    /* Open a handle to the write end of the pipe, NB must be inheritable     */
  473.    /**************************************************************************/
  474.    if ((hWritePipe = CreateFile("\\\\.\\pipe\\arexecpipe",GENERIC_WRITE, 0,
  475.                 &SecAttrib, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))
  476.         == INVALID_HANDLE_VALUE)
  477.    {
  478.       lasterror = GetLastError();
  479.       printf("CreateFile returned %d",lasterror);
  480.       return;
  481.    }
  482.  
  483.    /**************************************************************************/
  484.    /* Now create the process, with its stdout and stderr pipes attatched to  */
  485.    /* the write end of the named pipe.                                       */
  486.    /**************************************************************************/
  487.    StartInfo.cb = sizeof(STARTUPINFO);
  488.    StartInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  489.    StartInfo.wShowWindow = SW_HIDE;
  490.    StartInfo.hStdInput = NULL;
  491.    StartInfo.hStdOutput = hWritePipe;
  492.    StartInfo.hStdError = hWritePipe;
  493.    ProcessDead = FALSE;
  494.  
  495.    if (!CreateProcess(NULL,NewCommand, NULL, NULL, TRUE, 0, NULL, NULL,
  496.                       &StartInfo, &ProcInfo))
  497.    {
  498.       lasterror=GetLastError();
  499.       printf("CreateProcess returned %d\n",lasterror);
  500.       return;
  501.    }
  502.  
  503.    /**************************************************************************/
  504.    /* Loop whilst the process is still running                               */
  505.    /**************************************************************************/
  506.    while (!ProcessDead)
  507.    {
  508.       /***********************************************************************/
  509.       /* Check the process handle to see if it is dead yet.                  */
  510.       /***********************************************************************/
  511.       WaitState = WaitForSingleObject(ProcInfo.hProcess,1);
  512.       if (WaitState != WAIT_TIMEOUT)
  513.       {
  514.          ProcessDead = TRUE;
  515.       }
  516.  
  517.       /***********************************************************************/
  518.       /* Peek in the named pipe for some data, don't just do a ReadFile as   */
  519.       /* we may never get any more data!                                     */
  520.       /* If there is some then read it out of the pipe and send it.          */
  521.       /***********************************************************************/
  522.       if (!PeekNamedPipe(hReadPipe, NULL, 0, NULL, &BytesInPipe, NULL))
  523.       {
  524.          lasterror=GetLastError();
  525.          printf("PeekNamePipe returned %d\n",lasterror);
  526.          return;
  527.       }
  528.       if (BytesInPipe != 0)
  529.       {
  530.          memset(buffer,'\0',BufferSize+1);
  531.          if (!ReadFile(hReadPipe, buffer, BufferSize, &BytesRead, NULL))
  532.          {
  533.             lasterror=GetLastError();
  534.             printf("ReadFile returned %d\n",lasterror);
  535.             return;
  536.          }
  537.          cmsend(cm_conv_id,(unsigned char *) buffer,(CM_INT32 *)&BytesRead,
  538.                 &rts_received,&cm_rc);
  539.          if (cm_rc) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);
  540.       }
  541.    }
  542.    do
  543.    {
  544.       /***********************************************************************/
  545.       /* The process is now dead, but there could still be some data in the  */
  546.       /* pipe, so we do a peek/read loop until the number of bytes left is 0 */
  547.       /***********************************************************************/
  548.       if (!PeekNamedPipe(hReadPipe, NULL, 0, NULL, &BytesInPipe, NULL))
  549.       {
  550.          lasterror=GetLastError();
  551.          printf("PeekNamePipe returned %d\n",lasterror);
  552.          return;
  553.       }
  554.       if (BytesInPipe != 0)
  555.       {
  556.          memset(buffer,'\0',BufferSize+1);
  557.          if (!ReadFile(hReadPipe, buffer, BufferSize, &BytesRead, NULL))
  558.          {
  559.             lasterror=GetLastError();
  560.             printf("ReadFile returned %d\n",lasterror);
  561.             return;
  562.          }
  563.          cmsend(cm_conv_id,(unsigned char *) buffer,(CM_INT32 *)&BytesRead,
  564.                 &rts_received,&cm_rc);
  565.          if (cm_rc) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);
  566.       }
  567.    } while (BytesInPipe != 0);
  568.  
  569.    CloseHandle(hReadPipe);
  570.    CloseHandle(hWritePipe);
  571.  
  572.    return;
  573.  
  574. }
  575.