CPICPORT.C
/***************************************************************************** 
 * 
 *  MODULE NAME: CPICPORT.C 
 * 
 *  COPYRIGHTS: 
 *             This module contains code made available by IBM 
 *             Corporation on an AS IS basis.  Any one receiving the 
 *             module is considered to be licensed under IBM copyrights 
 *             to use the IBM-provided source code in any way he or she 
 *             deems fit, including copying it, compiling it, modifying 
 *             it, and redistributing it, with or without 
 *             modifications.  No license under any IBM patents or 
 *             patent applications is to be implied from this copyright 
 *             license. 
 * 
 *             A user of the module should understand that IBM cannot 
 *             provide technical support for the module and will not be 
 *             responsible for any consequences of use of the program. 
 * 
 *             Any notices, including this one, are not to be removed 
 *             from the module without the prior written consent of 
 *             IBM. 
 * 
 *  AUTHOR:    Peter J. Schwaller 
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376 
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376 
 * 
 *  FUNCTION:  Contains procedures to that may have to be rewritten for 
 *             different environments. 
 * 
 *  AVAILABILITY: 
 *             These sample programs and source are also available on 
 *             CompuServe through the APPC Information Exchange.  To get 
 *             to the APPC forum just type 'GO APPC' from any CompuServe 
 *             prompt.  The samples are available in the Sample Programs 
 *             library section.  Just search on the keyword CPICPGMS to 
 *             find all the samples in this series. 
 * 
 *             Updates for the sample programs and support for many more 
 *             CPI-C platforms will also be made available on CompuServe. 
 * 
 *  RELATED FILES: 
 *             CPICPORT.H 
 * 
 *  PORTABILITY NOTES: 
 *             This file is the home of all operating system specific 
 *             functions.  The following is a summary of the routines 
 *             in this file and where they are used: 
 * 
 *             write_output() 
 *                 displays a text string to normal output 
 * 
 *             write_error() 
 *                 displays error text to error output 
 * 
 *             write_log() 
 *                 logs text to an opened log file 
 *                 if the log file was not open, will display to error output 
 * 
 *             display_message() 
 *                 Delivers a text message. 
 *                 Used by ATELLD.C. 
 * 
 *             get_time() 
 *                 Returns time in milliseconds. 
 *                 Used by APING.C. 
 * 
 *             alloc_cpic_buffer() 
 *                 Allocates the best memory buffer for CPI-C performance. 
 *                 Used by APING.C and APINGD.C. 
 * 
 *             show_info() 
 *                 Displays an array of text strings. 
 *                 Used by all files with a main() function. 
 * 
 *             get_machine_mode() 
 *                 For family API applications; determines whether we 
 *                 are running under OS/2 or DOS. 
 * 
 *             get_password() 
 *                 Used to request that the user enter a password. 
 *                 If possible, the password will not display while the 
 *                 user types it. 
 *                 Used by CPICINIT.C 
 * 
 *             execute_and_send_output() 
 *                 Execute the specified command and send the output back 
 *                 to the client side. 
 *                 This routine is called by AREXECD.C. 
 * 
 *             do_exit() 
 *                 Exit properly for the environment.  This is usually 
 *                 necessary for GUI environments. 
 * 
 *             free_cpic_buffer() 
 *                 Frees the memory allocated by alloc_cpic_buffer(). 
 *                 Used by MPINGD.C. 
 * 
 *  CHANGE HISTORY: 
 *  Date       Description 
 *  08/05/92   Version 2.31 of APING, ATELL and AREXEC released to CompuServe. 
 *             This version was also distributed at the APPC/APPN Platform 
 *             Developer's Conference held in Raleigh, NC. 
 *  08/13/92   Added the write_*() calls. 
 *             Changed all printf and fprintf calls to use a write_*() call. 
 *  08/19/92   Added workaround for problem with system() returning non-null 
 *             even when command succeeded.  The fix is in DOS and FAPI 
 *             versions of execute_and_send_output(). 
 *  08/20/92   Fixed alloc_cpic_buffer() so it will use a shared buffer 
 *             under OS/2 2.0. 
 *  08/23/92   Fixed extra rc definition for AS/400 definition of the 
 *             execute_and_send_output() routine. 
 *  08/24/92   Version 2.32 released to CompuServe. 
 *  08/25/92   Changed DOS and AIX execute_and_send_output() to use the 
 *             tempnam() function instead of the tmpnam() function.  This 
 *             results in tempfiles being written to the TMP directory. 
 *  09/22/92   Version 2.33 released to CompuServe. 
 *  01/07/93   Added OS2_20 to list of conditional compile directives in 
 *             the following routines: 
 *               get_time 
 *               get_password 
 *               set_echo 
 *               execute_and_send_output 
 *  11/11/94   Wrote free_cpic_buffer() 
 * 
 *****************************************************************************/ 
 
#if defined(WIN32) || defined(WINDOWS) 
#include <windows.h> 
#endif 
 
#include "wincpic.h" 
 
#include "cpicerrs.h" 
 
/* Set up constant declarations */ 
#include "cpicdefs.h" 
 
#include "cpicport.h" 
 
 
/* standard C include files */ 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdarg.h> 
#include <time.h> 
 
extern char ebcdic_to_ascii_table[]; 
extern char ascii_to_ebcdic_table[]; 
 
 
 
void write_error(char *fmt, ...) 
{ 
    /*========================================================================= 
     * 
     *=======================================================================*/ 
 
    va_list args; 
 
    va_start(args, fmt); 
    vfprintf(stderr, fmt, args); 
    va_end(args); 
} 
 
 
void write_output(char *fmt, ...) 
{ 
    /*========================================================================= 
     * 
     *=======================================================================*/ 
 
    va_list args; 
 
    va_start(args, fmt); 
    vfprintf(stdout, fmt, args); 
    va_end(args); 
} 
 
void write_log(FILE * file, char *fmt, ...) 
{ 
    /*========================================================================= 
     * 
     *=======================================================================*/ 
 
    va_list args; 
 
    va_start(args, fmt); 
    if (file == NULL) { 
        write_error(fmt, args); 
    } else { 
        vfprintf(file, fmt, args); 
    } 
    va_end(args); 
} 
 
/* 
 * display_message() 
 *       delivers a text message 
 *       used by ATELLD.C 
 * 
 *    default 
 *       Uses C library functions to print the message. 
 */ 
void 
display_message(char * origin, char * dest_userid, char * message) 
{ 
 
    char timestamp[TIMELENGTH]; 
    struct tm * newtime; 
    time_t ltime; 
 
    time(<ime); 
    newtime = localtime(<ime); 
    strcpy(timestamp, asctime(newtime)); 
 
    /* eliminate new line character for display */ 
    timestamp[strlen(timestamp)-1] = '\0'; 
 
    write_output("\n\n  msg from %s ", origin); 
    if (dest_userid[0] != '\0') { 
        write_output("to user %s ", dest_userid); 
    } 
    write_output("on %s:", timestamp); 
    write_output("\n\n     %s\n\n",message); 
} 
 
 
 
/* 
 * get_time() 
 *       returns time in milliseconds 
 *       used by APING.C 
 * 
 */ 
unsigned long 
get_time(void) 
{ 
 
    return GetTickCount(); 
 
} 
 
 
/* 
 * alloc_cpic_buffer() 
 */ 
 
char CM_PTR 
alloc_cpic_buffer (unsigned int size) 
{ 
   return malloc(size); 
} 
 
/* 
 * freec_cpic_buffer() 
 */ 
 
void 
free_cpic_buffer( void *memblock ) 
{ 
free( memblock ); 
} 
 
 
/* 
 * show_info() 
 * 
 *       This procedure displays a block of text information on the the 
 *       screen.  The input argument is an array of strings to be output, 
 *       one string per line.  A NULL array element indicates the end of 
 *       the strings. 
 * 
 *    default 
 *       print out all of the text strings in the array using the cpicport 
 *       write_output() call. 
 */ 
void 
show_info(char * * text) 
{ 
    int i; 
 
    for ( i = 0; text[i] != NULL; i++ ) { 
        write_output("%s\n", text[i]); 
    } 
    return; 
} 
 
/* 
 * get_password() 
 * 
 *    Gets a password from the user.  Where possible, this routine should 
 *    disable echoing of keystrokes for security reasons. 
 * 
 *    Returns 
 *       0 - password was successfully input 
 *       1 - password variable was not updated successfully 
 */ 
int 
get_password(char * password, int max_length) 
{ 
    int rc; 
    int length; 
 
    /* There is no portable way to disable echoing of input keystrokes.      */ 
    /* If a platform does support turning off echo, this section should be   */ 
    /* rewritten and ifdef'ed.                                               */ 
 
    if (NULL != fgets(password, max_length+1, stdin)) { 
        length = strlen(password); 
        if (length > 0 && length < max_length) { 
            if (password[length-1] == '\n') { /* remove the trailing         */ 
                password[length-1] = '\0';    /* newline if it exists        */ 
            } 
            rc = 0; 
        } else { 
            rc = 1; 
        } 
 
    } else { 
        rc = 1; 
    } 
 
    return rc; 
} 
 
/* 
 * do_exit() 
 * 
 * Exit properly for the environment we're running in. 
 */ 
void 
do_exit(int rc) 
{ 
#ifdef EXITTHREAD 
    ExitThread(rc); 
#else 
    WinCPICCleanup(); 
    exit(rc); 
#endif 
} 
 
/* 
 * The ASCII<-->EBCDIC character set translation routines are implemented 
 * below.  These procedures should never be called directly, but should 
 * be accessed through the convert_to_ascii() and convert_from_ascii() 
 * macros.  This frees the application program from knowing whether 
 * it is being coded on an ASCII or EBCDIC computer (this must be 
 * determined in the macro definition in CPICPORT.H). 
 */ 
 
void   ascii_to_ebcdic_field  (unsigned char * ascii_field, 
                               unsigned int field_size) 
{ 
    unsigned int i; 
 
    for (i = 0; 
         i < field_size; 
         ascii_field[i] = ascii_to_ebcdic_table[(unsigned)ascii_field[i]],i++); 
} 
 
void  ascii_to_ebcdic_string (unsigned char * ascii_string) 
{ 
    ascii_to_ebcdic_field(ascii_string, strlen((char*)ascii_string)); 
} 
 
void   ebcdic_to_ascii_field  (unsigned char * ebcdic_field, 
                               unsigned int field_size) 
{ 
    unsigned int i; 
 
    for (i = 0; 
         i < field_size; 
         ebcdic_field[i] = 
                         ebcdic_to_ascii_table[(unsigned)ebcdic_field[i]],i++); 
} 
 
void  ebcdic_to_ascii_string (unsigned char * ebcdic_string) 
{ 
    ebcdic_to_ascii_field(ebcdic_string, strlen((char*)ebcdic_string)); 
} 
 
 
 
/* ASCII to EBCDIC translate table (only UGL character set) */ 
 
char ascii_to_ebcdic_table[] = { 
"\x00\x01\x02\x03\x37\x2D\x2E\x2F\x16\x05\x15\x0B\x0C\x0D\x0E\x0F"  /* 00-0F */ 
"\x10\x11\x12\x13\x3C\x3D\x32\x26\x18\x19\x3F\x27\x22\x1D\x35\x1F"  /* 10-1F */ 
"\x40\x5A\x7F\x7B\x5B\x6C\x50\x7D\x4D\x5D\x5C\x4E\x6B\x60\x4B\x61"  /* 20-2F */ 
"\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\x7A\x5E\x4C\x7E\x6E\x6F"  /* 30-3F */ 
"\x7C\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xD1\xD2\xD3\xD4\xD5\xD6"  /* 40-4F */ 
"\xD7\xD8\xD9\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xAD\xE0\xBD\x5F\x6D"  /* 50-5F */ 
"\x79\x81\x82\x83\x84\x85\x86\x87\x88\x89\x91\x92\x93\x94\x95\x96"  /* 60-6F */ 
"\x97\x98\x99\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xC0\x4F\xD0\xA1\x07"  /* 70-7F */ 
"\x43\x20\x21\x1C\x23\xEB\x24\x9B\x71\x28\x38\x49\x90\xBA\xEC\xDF"  /* 80-8F */ 
"\x45\x29\x2A\x9D\x72\x2B\x8A\x9A\x67\x56\x64\x4A\x53\x68\x59\x46"  /* 90-9F */ 
"\xEA\xDA\x2C\xDE\x8B\x55\x41\xFE\x58\x51\x52\x48\x69\xDB\x8E\x8D"  /* A0-AF */ 
"\x73\x74\x75\xFA\x15\xB0\xB1\xB3\xB4\xB5\x6A\xB7\xB8\xB9\xCC\xBC"  /* B0-BF */ 
"\xAB\x3E\x3B\x0A\xBF\x8F\x3A\x14\xA0\x17\xCB\xCA\x1A\x1B\x9C\x04"  /* C0-CF */ 
"\x34\xEF\x1E\x06\x08\x09\x77\x70\xBE\xBB\xAC\x54\x63\x65\x66\x62"  /* D0-DF */ 
"\x30\x42\x47\x57\xEE\x33\xB6\xE1\xCD\xED\x36\x44\xCE\xCF\x31\xAA"  /* E0-EF */ 
"\xFC\x9E\xAE\x8C\xDD\xDC\x39\xFB\x80\xAF\xFD\x78\x76\xB2\x9F\xFF"  /* F0-FF */ 
}; 
 
/* EBCDIC to ASCII translate table (only UGL character set) */ 
 
char ebcdic_to_ascii_table[] = { 
"\x00\x01\x02\x03\xCF\x09\xD3\x7F\xD4\xD5\xC3\x0B\x0C\x0D\x0E\x0F"  /* 00-0F */ 
"\x10\x11\x12\x13\xC7\x0A\x08\xC9\x18\x19\xCC\xCD\x83\x1D\xD2\x1F"  /* 10-1F */ 
"\x81\x82\x1C\x84\x86\x0A\x17\x1B\x89\x91\x92\x95\xA2\x05\x06\x07"  /* 20-2F */ 
"\xE0\xEE\x16\xE5\xD0\x1E\xEA\x04\x8A\xF6\xC6\xC2\x14\x15\xC1\x1A"  /* 30-3F */ 
"\x20\xA6\xE1\x80\xEB\x90\x9F\xE2\xAB\x8B\x9B\x2E\x3C\x28\x2B\x7C"  /* 40-4F */ 
"\x26\xA9\xAA\x9C\xDB\xA5\x99\xE3\xA8\x9E\x21\x24\x2A\x29\x3B\x5E"  /* 50-5F */ 
"\x2D\x2F\xDF\xDC\x9A\xDD\xDE\x98\x9D\xAC\xBA\x2C\x25\x5F\x3E\x3F"  /* 60-6F */ 
"\xD7\x88\x94\xB0\xB1\xB2\xFC\xD6\xFB\x60\x3A\x23\x40\x27\x3D\x22"  /* 70-7F */ 
"\xF8\x61\x62\x63\x64\x65\x66\x67\x68\x69\x96\xA4\xF3\xAF\xAE\xC5"  /* 80-8F */ 
"\x8C\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x97\x87\xCE\x93\xF1\xFE"  /* 90-9F */ 
"\xC8\x7E\x73\x74\x75\x76\x77\x78\x79\x7A\xEF\xC0\xDA\x5B\xF2\xF9"  /* A0-AF */ 
"\xB5\xB6\xFD\xB7\xB8\xB9\xE6\xBB\xBC\xBD\x8D\xD9\xBF\x5D\xD8\xC4"  /* B0-BF */ 
"\x7B\x41\x42\x43\x44\x45\x46\x47\x48\x49\xCB\xCA\xBE\xE8\xEC\xED"  /* C0-CF */ 
"\x7D\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\xA1\xAD\xF5\xF4\xA3\x8F"  /* D0-DF */ 
"\x5C\xE7\x53\x54\x55\x56\x57\x58\x59\x5A\xA0\x85\x8E\xE9\xE4\xD1"  /* E0-EF */ 
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\xB3\xF7\xF0\xFA\xA7\xFF"  /* F0-FF */ 
}; 
 
void 
execute_and_send_output(char * command, 
                        unsigned char * cm_conv_id, 
                        struct error_handler_cpicerr * cpicerr) 
{ 
 
   DWORD lasterror = 0; 
   HANDLE hWritePipe = NULL; 
   HANDLE hReadPipe = NULL; 
   SECURITY_ATTRIBUTES SecAttrib = {0}; 
   STARTUPINFO StartInfo = {0}; 
   PROCESS_INFORMATION ProcInfo = {0}; 
   unsigned char * buffer=NULL; 
   DWORD BufferSize=10000; 
   DWORD BytesRead=0; 
   DWORD BytesInPipe=0; 
   DWORD WaitState=0; 
   BOOL ProcessDead=FALSE; 
   CM_INT32 rts_received; 
   CM_INT32 cm_rc; 
   char NewCommand[255] = {0}; 
 
   strcpy(NewCommand,"cmd /c "); 
   strcat(NewCommand,command); 
 
   buffer=malloc(BufferSize+2); 
 
   SecAttrib.nLength = sizeof(SECURITY_ATTRIBUTES); 
   SecAttrib.lpSecurityDescriptor = NULL; 
   SecAttrib.bInheritHandle = TRUE; 
 
   /**************************************************************************/ 
   /* Create named pipe and get handle to read end of the pipe.              */ 
   /**************************************************************************/ 
   if ((hReadPipe = CreateNamedPipe("\\\\.\\pipe\\arexecpipe", 
               PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE, 1, BufferSize, 
               BufferSize, 1000,&SecAttrib)) == INVALID_HANDLE_VALUE) 
   { 
      lasterror = GetLastError(); 
      printf("CreateNamedPipe returned %d",lasterror); 
      return; 
   } 
 
   /**************************************************************************/ 
   /* Open a handle to the write end of the pipe, NB must be inheritable     */ 
   /**************************************************************************/ 
   if ((hWritePipe = CreateFile("\\\\.\\pipe\\arexecpipe",GENERIC_WRITE, 0, 
                &SecAttrib, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) 
        == INVALID_HANDLE_VALUE) 
   { 
      lasterror = GetLastError(); 
      printf("CreateFile returned %d",lasterror); 
      return; 
   } 
 
   /**************************************************************************/ 
   /* Now create the process, with its stdout and stderr pipes attatched to  */ 
   /* the write end of the named pipe.                                       */ 
   /**************************************************************************/ 
   StartInfo.cb = sizeof(STARTUPINFO); 
   StartInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; 
   StartInfo.wShowWindow = SW_HIDE; 
   StartInfo.hStdInput = NULL; 
   StartInfo.hStdOutput = hWritePipe; 
   StartInfo.hStdError = hWritePipe; 
   ProcessDead = FALSE; 
 
   if (!CreateProcess(NULL,NewCommand, NULL, NULL, TRUE, 0, NULL, NULL, 
                      &StartInfo, &ProcInfo)) 
   { 
      lasterror=GetLastError(); 
      printf("CreateProcess returned %d\n",lasterror); 
      return; 
   } 
 
   /**************************************************************************/ 
   /* Loop whilst the process is still running                               */ 
   /**************************************************************************/ 
   while (!ProcessDead) 
   { 
      /***********************************************************************/ 
      /* Check the process handle to see if it is dead yet.                  */ 
      /***********************************************************************/ 
      WaitState = WaitForSingleObject(ProcInfo.hProcess,1); 
      if (WaitState != WAIT_TIMEOUT) 
      { 
         ProcessDead = TRUE; 
      } 
 
      /***********************************************************************/ 
      /* Peek in the named pipe for some data, don't just do a ReadFile as   */ 
      /* we may never get any more data!                                     */ 
      /* If there is some then read it out of the pipe and send it.          */ 
      /***********************************************************************/ 
      if (!PeekNamedPipe(hReadPipe, NULL, 0, NULL, &BytesInPipe, NULL)) 
      { 
         lasterror=GetLastError(); 
         printf("PeekNamePipe returned %d\n",lasterror); 
         return; 
      } 
      if (BytesInPipe != 0) 
      { 
         memset(buffer,'\0',BufferSize+1); 
         if (!ReadFile(hReadPipe, buffer, BufferSize, &BytesRead, NULL)) 
         { 
            lasterror=GetLastError(); 
            printf("ReadFile returned %d\n",lasterror); 
            return; 
         } 
         cmsend(cm_conv_id,(unsigned char *) buffer,(CM_INT32 *)&BytesRead, 
                &rts_received,&cm_rc); 
         if (cm_rc) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc); 
      } 
   } 
   do 
   { 
      /***********************************************************************/ 
      /* The process is now dead, but there could still be some data in the  */ 
      /* pipe, so we do a peek/read loop until the number of bytes left is 0 */ 
      /***********************************************************************/ 
      if (!PeekNamedPipe(hReadPipe, NULL, 0, NULL, &BytesInPipe, NULL)) 
      { 
         lasterror=GetLastError(); 
         printf("PeekNamePipe returned %d\n",lasterror); 
         return; 
      } 
      if (BytesInPipe != 0) 
      { 
         memset(buffer,'\0',BufferSize+1); 
         if (!ReadFile(hReadPipe, buffer, BufferSize, &BytesRead, NULL)) 
         { 
            lasterror=GetLastError(); 
            printf("ReadFile returned %d\n",lasterror); 
            return; 
         } 
         cmsend(cm_conv_id,(unsigned char *) buffer,(CM_INT32 *)&BytesRead, 
                &rts_received,&cm_rc); 
         if (cm_rc) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc); 
      } 
   } while (BytesInPipe != 0); 
 
   CloseHandle(hReadPipe); 
   CloseHandle(hWritePipe); 
 
   return; 
 
}