home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1993 #2
/
Image.iso
/
os2
/
edmi3.zip
/
DPIPELN.ZIP
/
DPIPELN.C
< prev
next >
Wrap
Text File
|
1993-04-27
|
23KB
|
811 lines
/* dpipeln.c
* Copyright (C) 1993 Charles R. Oldham
* DPipeLn is distributed without any warranty.
* I am reasonably sure that it is without major bugs, but I assume no
* responsibility for anything it may do to your machine. You use this
* tool at your own risk.
*
* Allows a user to start a DOS VDM from an OS/2 prompt
* and pipe stdout back to the OS/2 session.
*
* $Log: dpipeln.c,v $
* Revision 1.11 1993/04/12 18:17:06 cro
* Cosmetic changes and code cleanup.
*
* Revision 1.10 1993/03/17 15:12:33 cro
* Modified the program so it doesn't start the DosSession as a related
* session now, but instead starts it as an unrelated session. This is
* because WF/2 spawns nmake as a child process, then nmake spawns
* dpipeln as a child process. If you have started any other process
* as a child process from within WF then subsequent DosStartSession
* calls will fail with "ERROR_SMG_NOT_PARENT" (or something like that).
* The downside is that if a session is not started as a related session
* a termination queue (that would contain the session's return code) is
* not created. So dpipeln now creates a batch file that is executed instead
* of the Dos program. The batch file pipes the program output to the
* original npipe, and the result code to a second npipe.
*
* Revision 1.9 1993/03/15 20:21:17 cro
* Changed BUFSIZE to 80 so messages would appear more frequently during
* makes.
*
* Revision 1.8 1993/03/09 17:16:43 cro
* Restructured some of the code to eliminate unnecessary global variables
* and move major functions out of main. Added more comments.
*
* Revision 1.7 1993/03/08 15:32:13 cro
* Re-enabled title-bar display (for testing had it forced to "DPipeLn").
*
* Revision 1.6 1993/03/05 21:17:06 cro
* Removed code that set the StartData.PgmName parameter. It was not
* necessary--DosStartSession invokes the appropriate shell if
* the parameter is 0 or NULL.
*
* Revision 1.5 1993/03/04 20:56:07 cro
* Added support for multiple instances of dpipeln by making the
* named pipe name out of dpipeln's PID. Also laid foundation
* for implementation of the termination queue so we can
* tell what the return code of the Dos command was.
*
* Revision 1.4 1993/03/04 19:20:47 cro
* Added code to process arguments without quotes. This changes
* the syntax of the command subtly--before you could place the Dos command
* anywhere on the command line. Now you must place it at the *end* of the
* command line.
* Before: dpipeln -v "mem /c"
* After: dpipeln -v mem /c
*
* Revision 1.3 1993/03/03 17:00:42 cro
* Added mucho comments, replaced some numeric constants with #defined
* constants.
*
* Revision 1.2 1993/03/03 16:05:03 cro
* Changed mode of stdout to binary to prevent translation of
* LF to CR/LF. Added debug code, but commented out in this version.
*
* Revision 1.1 1993/03/02 21:12:09 cro
* Initial revision
*
*
*/
#define INCL_DOSSESMGR /* Session Manager values */
#define INCL_DOS /* DOS Calls (not MS-DOS) */
#define INCL_DOSNMPIPES /* Named Pipe Support */
#define INCL_DOSPROCESS /* Process IDs */
#define INCL_DOSQUEUES /* Queue support */
#define BUFSIZE 80 /* Pipe buffer size */
#define SETSIZE 4096 /* Dos settings maximum size */
/* String macros to make life a little easier */
#define new_string(x) ((char *)malloc((x) + 1))
#define freemem(x) if ((x) != NULL) free((x))
#include <os2.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
PBYTE get_dos_settings(char *file);
char *get_basename();
char *make_npipename(char *PQbasename);
char *make_batname(char *base, char *tempdir);
int build_batchfile(char *Batname, char *Errpname, char *Command);
void process_cmdline(int argc, char *argv[]);
void usage();
void cleanup();
char *get_exename(char *arg);
void setup_startdata();
void transfer_data(HPIPE *piphand);
void read_errpipe();
char *make_rcdname(char *base);
char *get_tempdir();
PSZ PgmTitle = NULL; /* Title of window in which Dos program will execute */
PSZ PgmCmdLine = NULL; /* Command line for Dos program */
PSZ PgmSettingsFile = NULL; /* File containing DOS settings */
PSZ Exename = NULL; /* DPipeLn Executable name */
UCHAR ObjBuf[100]; /* Object buffer. Used by DosStartSession */
PSZ Command; /* The actual DOS command to be executed */
PSZ Errpname; /* Name of the pipe that contains the return code */
HPIPE Piphand, Rcdpipe; /* Named Pipe handles */
int Opt_visible; /* Run DOS session invisibly */
int Opt_settings; /* DOS session has a settings file */
int Opt_foreground; /* Run session in foreground */
char *PQbasename; /* Unique name for the named pipe */
char *Pname; /* Fully qualified path for named pipe */
char *Batname; /* Unique name of the batch file */
ULONG OwningPID; /* PID of dpipeln.exe */
char RCstring[BUFSIZE]; /* String to temporarily hold ret code from */
/* DOS program */
char *Tempdir = NULL; /* temporary directory in which to put batch file */
/*------------------------------------------------------------------------*/
/* begin main */
/*------------------------------------------------------------------------*/
USHORT main(int argc, char *argv[]) {
STARTDATA sd; /* Start session data structure */
ULONG sess_id; /* Session ID (returned) */
PID s_pid; /* Process ID (returned) */
ULONG outbuffer = 1, /* Size of out buffer */
inbuffer = BUFSIZE - 1, /* Size of in buffer */
timeout = 10000; /* Timeout before giving up */
APIRET rc = 0; /* Return code */
int dosreturn = -1; /* return code from the Dos program */
TID threadid;
ULONG targ;
ULONG tstack;
ULONG tflags;
RESULTCODES wp_results;
PID the_pid;
/* Get the name by which dpipeln was called out of the command line */
Exename = get_exename(argv[0]);
/* Force stdout into binary mode */
if (_fsetmode(stdout, "b") == -1) {
fprintf(stderr, "Could not set stdout to binary mode.\n");
exit(-1);
}
PQbasename = get_basename(); /* Get the unique name to be used for the Named */
/* Pipe and the termination queue */
Pname = make_npipename(PQbasename);
Errpname = make_rcdname(PQbasename);
/* Process the command line */
process_cmdline(argc, argv);
if (Tempdir == NULL) Tempdir = get_tempdir();
Batname = make_batname(PQbasename, Tempdir);
/* Create the named pipe */
rc = DosCreateNPipe(Pname, /* Pipe name */
&Piphand, /* returned pipe handle */
NP_ACCESS_DUPLEX, /* Open mode */
NP_WAIT || NP_TYPE_BYTE || NP_READMODE_BYTE ||
NP_UNLIMITED_INSTANCES, /* Pipe modes */
outbuffer, /* Size of out buffer */
inbuffer, /* Size of in buffer */
timeout); /* Timeout time */
/* Die if create pipe failed */
if (rc != 0) {
fprintf(stderr, "%s: DosCreateNPipe '%s' failed (%ld).\n", Exename, Pname, rc);
exit(-1);
}
rc = DosCreateNPipe(Errpname, /* Pipe name */
&Rcdpipe, /* returned pipe handle */
NP_ACCESS_DUPLEX, /* Open mode */
NP_WAIT || NP_TYPE_BYTE || NP_READMODE_BYTE ||
NP_UNLIMITED_INSTANCES, /* Pipe modes */
outbuffer, /* Size of out buffer */
inbuffer, /* Size of in buffer */
timeout); /* Timeout time */
/* Die if create pipe failed */
if (rc != 0) {
fprintf(stderr, "%s: DosCreateNPipe '%s' failed (%ld).\n", Exename, Pname, rc);
exit(-1);
}
/* Set the StartData parameters */
setup_startdata(&sd);
/* Create the batch file */
rc = build_batchfile(Batname, Errpname, Command);
if (rc != 0) {
fprintf(stderr, "%s: Batch file build failed (%ld).\n", Exename, rc);
exit(-1);
}
/* Start the Dos session */
rc = DosStartSession(&sd, &sess_id, &s_pid);
/* On successful return, the variable */
/* sess_id contains the session ID */
/* of the new session, and the */
/* variable s_pid contains the process */
/* ID of the new process */
if (rc != 0) {
fprintf(stderr, "%s: DosStartSession failed (%ld)\n", Exename, rc);
exit(-1);
}
RCstring[0] = 0;
targ = 0;
tflags = 0; /* Start thread immediately */
tstack = 4096; /* Stack size for thread */
#ifdef DEBUG_EMX
threadid = _beginthread(read_errpipe, NULL, (unsigned)tstack, &targ);
if (threadid == -1) fprintf(stderr, "_beginthread returned %ld.\n", threadid);
#else
rc = DosCreateThread(&threadid, read_errpipe, targ, tflags, tstack);
if (rc != 0) {
fprintf(stderr, "%s: DosCreateThread failed (%ld).", Exename, rc);
exit(-1);
}
#endif
/* Receive data from the pipes */
rc = DosConnectNPipe(Piphand);
if (rc != 0) {
fprintf(stderr, "%s: DosConnectNPipe failed (%ld)\n", Exename, rc);
exit(-1);
}
transfer_data(&Piphand);
rc = DosWaitThread(&threadid, DCWW_WAIT);
sscanf(RCstring, "%d", &dosreturn);
/* fprintf(stderr, "String: %s, Return: %d", RCstring, dosreturn);*/
/* Wait for the DOS process to finish */
DosWaitChild(DCWA_PROCESS,
DCWW_WAIT,
&wp_results,
&the_pid,
s_pid);
cleanup();
#ifdef DEBUG
fprintf(stderr, "wp_results.codeTerminate=%ld, wp_results.codeResult=%ld\n",
wp_results.codeTerminate, wp_results.codeResult);
#endif
return (USHORT)dosreturn;
} /* end of main */
/*------------------------------------------------------------------------*/
/* end main */
/*------------------------------------------------------------------------*/
/*
* cleanup
* Free memory and disconnect the pipe
*
*/
void cleanup() {
APIRET rc;
int tries = 0;
/* Close the pipes */
DosDisConnectNPipe(Piphand);
DosDisConnectNPipe(Rcdpipe);
/* Erase the batch file */
if (Batname != NULL) { /* Batname may not have been created when */
do { /* usage() is called */
rc = DosDelete(Batname);
tries++;
} while (rc == 32 && tries < 10000);
if (rc != 0) fprintf(stderr, "%s: DosDelete on '%s' failed (%ld).\n",
Exename, Batname, rc);
}
/* Free memory */
freemem(PgmTitle);
freemem(PgmCmdLine);
freemem(PgmSettingsFile);
freemem(Exename);
freemem(PQbasename);
freemem(Pname);
freemem(Batname);
freemem(Command);
freemem(Tempdir);
} /* End of cleanup */
/*
* get_dos_settings
*
* Read the environment from the Dos Settings File
*/
PBYTE get_dos_settings(char *file) {
FILE *setfile;
PBYTE env = (PBYTE)malloc(SETSIZE);
PBYTE p = env;
setfile = fopen(file, "r");
if (setfile == NULL) {
fprintf(stderr, "%s: Error opening DOS Settings file %s.\n", Exename, PgmSettingsFile);
exit(-1);
}
while (fgets(p, 80, setfile)) {
p += strlen(p);
*(p-1)='\0';
if (p > env + 4096) {
fprintf(stderr, "%s: Too many settings.\n", Exename);
fflush(stderr);
exit(-1);
}
}
realloc(env, p-env);
fclose(setfile);
return(env);
} /* end of get_dos_settings */
/*
* process_cmdline
*
* Takes the command line arguments and iterates through them, setting various
* global variables
*/
void process_cmdline(int argc, char *argv[]) {
int cmdcount = 0, argscount = 0, argsize = 0;
char *doscommand;
Opt_visible = 0;
Opt_settings = 0;
Opt_foreground = 0;
if (argc < 2) {
usage();
}
for (cmdcount = 1; cmdcount < argc && argv[cmdcount][0] == '-'; cmdcount++) {
switch (argv[cmdcount][1]) {
case 'v': Opt_visible = 1;
break;
case 's': PgmSettingsFile = strdup(&argv[cmdcount][2]);
if (access(PgmSettingsFile, 0) != 0) {
fprintf(stderr, "%s: Cannot access %s.\n", Exename, PgmSettingsFile);
exit(-1);
}
Opt_settings = 1;
break;
case 'f': Opt_foreground = 1;
break;
case 't': if (argv[cmdcount][strlen(argv[cmdcount]) - 1] != '\\') {
/* For the extra \ at the end of the string */
Tempdir = new_string(strlen(&argv[cmdcount][2]) + 1);
strcpy(Tempdir, &argv[cmdcount][2]);
strcat(Tempdir, "\\");
} else {
Tempdir = new_string(strlen(&argv[cmdcount][2]));
strcpy(Tempdir, &argv[cmdcount][2]);
}
break;
default: break;
}
}
argsize = 0;
for (argscount = cmdcount; argscount < argc; argscount++) {
argsize += strlen(argv[argscount]) + 1;
}
if (argsize == 0) usage();
doscommand = new_string(argsize);
doscommand[0] = 0;
for (argscount = cmdcount; argscount < argc; argscount++) {
strcat(doscommand, argv[argscount]);
strcat(doscommand, " ");
}
if (PgmTitle == NULL) {
PgmTitle = new_string(strlen(&doscommand[1]) > 50 ? 55 : strlen(&doscommand[1]));
PgmTitle[0] = 0;
strncpy(PgmTitle, doscommand, 50);
if (strlen(doscommand) > 50) {
PgmTitle[54] = 0; /* 54, not 55 because PgmTitle is 0 based */
strcat(PgmTitle, "...");
}
Command = strdup(doscommand);
} else {
freemem(doscommand);
usage();
}
if (Command == NULL) {
freemem(doscommand);
usage();
}
freemem(doscommand);
} /* end of process_cmdline */
/*
* usage
*
* Print usage message, cleanup, then terminate with exit code -1
*
*/
void usage() {
fprintf(stderr, "Usage: %s [-v] [-f] [-sdos_settings_filename] [-ttemp_dir_name] dos_command\n", Exename);
cleanup();
exit(-1);
} /* End of usage */
/*
* get_tempdir
*
* Return a string to the temporary directory where the batch file will be
* created. This function is called only if -t"dir_name" is not specified
* on the command line. The environment is scanned for this information, searching
* first for DPIPELN_TEMP, then for TEMP, then for just TMP. If none are
* found then the current directory will be used.
*
*/
char *get_tempdir() {
APIRET rc;
PSZ envptr;
PSZ temp;
if (DosScanEnv("DPIPELN_TEMP", &envptr) != 0)
if (DosScanEnv("TEMP", &envptr) != 0)
if (DosScanEnv("TMP", &envptr) != 0) return NULL;
if (envptr[strlen(envptr) - 1] != '\\') {
temp = new_string(strlen(envptr) + 1);
strcpy(temp, envptr);
strcat(temp, "\\");
} else {
temp = strdup(envptr);
}
return temp;
}
/*
* get_basename
*
* Create a base name for the named pipe and the termination queue out of
* the process id and a character.
*/
char *get_basename() {
char tmpname[256]; /* 255 characters ought to be enough */
/* DosGetInfoBlocks returns the address of the Thread Information Block (TIB)
of the current thread. This function also returns the address
of the Process Information Block (PIB) of the current process. */
PTIB pptib; /* Address of a pointer to a TIB */
PPIB pppib; /* Address of a pointer to a PIB */
APIRET rc; /* Return code */
rc = DosGetInfoBlocks(&pptib, &pppib);
if (rc != 0) fprintf(stderr, "%s: DosGetInfoBlocks failed (%ld)\n", Exename, rc);
OwningPID = pppib->pib_ulpid;
sprintf(tmpname, "n%u", pppib->pib_ulpid);
return(strdup(tmpname));
} /* end of get_basename */
/*
* make_npipename
*
* Add the word \\PIPE\\ onto the base name for the pipe and return
* a new string.
*/
char *make_npipename(char *base) {
char *npipe;
char prefix[] = "\\PIPE\\";
npipe = new_string(strlen(prefix) + strlen(base));
npipe[0] = 0;
strcpy(npipe, prefix);
strcat(npipe, base);
return npipe;
} /* end of make_npipename */
/*
* make_batname
*
* Make the name of the batch file that will be executed by the DOS
* command processor.
*
*/
char *make_batname(char *base, char *tempdir) {
char *bat;
bat = new_string(strlen(tempdir) + strlen(base) + strlen(".bat"));
bat[0] = 0;
strcpy(bat, tempdir);
strcat(bat, base);
strcat(bat, ".bat");
return bat;
} /* end of make_batname */
/*
* make_rcdname
*
* Make the name of the file that will contain the return code from the
* DOS program.
*
*/
char *make_rcdname(char *base) {
char *rcd;
char prefix[] = "\\PIPE\\";
rcd = new_string(strlen(prefix) + strlen(base) + 1);
rcd[0] = 0;
strcpy(rcd, prefix);
strcat(rcd, "e");
strcat(rcd, base);
return rcd;
} /* end of make_rcdname */
/*
* get_exename
* Retrieve the name of the executable. i.e. d:\src\stuff\dpipeln.exe will
* become dpipeln.exe, and return a new string containing such
*/
char *get_exename(char *arg) {
int cmdcount;
for (cmdcount = strlen(arg); cmdcount > 0 && (arg[cmdcount] != '/' &&
arg[cmdcount] != '\\' &&
arg[cmdcount] != ':');
cmdcount-- && (arg[cmdcount] = tolower(arg[cmdcount])));
/* Copy the executable name into Exename */
return (strdup(&(arg[cmdcount == 0 ? cmdcount : ++cmdcount])));
} /* end of get_exename */
/*
* setup_startdata
*
* The startdata structure provides almost all the necessary information
* to DosStartSession about the type of session to be started.
* This function initializes that data.
*
*/
void setup_startdata(STARTDATA *SD) {
SD->Length = sizeof(STARTDATA); /* Length of STARTDATA structure */
SD->Related = SSF_RELATED_INDEPENDENT; /* Independent session */
SD->FgBg = Opt_foreground ? SSF_FGBG_FORE : SSF_FGBG_BACK;
/* Start session in fore/background */
SD->TraceOpt = SSF_TRACEOPT_NONE; /* Don't trace session */
SD->PgmTitle = PgmTitle; /* Session Title string */
/* This session will not need a termination queue. */
SD->TermQ = 0;
SD->InheritOpt = SSF_INHERTOPT_PARENT;
/* Inherit environment and open */
/* file handles from parent */
SD->SessionType = SSF_TYPE_WINDOWEDVDM;
/* Session type is a windowed VDM */
SD->IconFile = 0;
/* Assume no specific icon file */
/* is provided */
SD->PgmHandle = 0;
/* Do not use the installation file */
SD->PgmControl = Opt_visible ? SSF_CONTROL_VISIBLE : SSF_CONTROL_INVISIBLE;
/* Start the program as visible, or invisible */
/* depending on the command line argument */
SD->InitXPos = 30;
SD->InitYPos = 40;
SD->InitXSize = 200; /* Initial window coordinates */
SD->InitYSize = 140; /* and size */
SD->Reserved = 0;
/* Reserved, must be zero */
SD->ObjectBuffer = ObjBuf;
/* Object buffer to hold DosExecPgm */
/* failure causes */
SD->ObjectBuffLen = 100;
/* Size of object buffer */
/* Get the DOS settings or just 0 if no settings file requested */
SD->Environment = Opt_settings ? get_dos_settings(PgmSettingsFile) : 0;
SD->PgmName = 0; /* If this is 0 (or NULL) the Session */
/* will invoke the proper shell--either */
/* the one specified in CONFIG.SYS */
/* or the one in the settings file */
PgmCmdLine = new_string(strlen(Batname) + strlen("/c "));
strcpy(PgmCmdLine, "/c ");
strcat(PgmCmdLine, Batname);
SD->PgmInputs = PgmCmdLine; /* Command line arguments to command processor */
} /* end of setup_startdata */
/*
* transfer_data
* Get passed text from the Dos session and write it to stdout.
*
*/
void transfer_data(HPIPE *piphand) {
char prep_string[BUFSIZE];
APIRET rc = 0;
ULONG BytesRead;
/* "Prep" the string and the return code. */
prep_string[0] = 0;
do {
rc = DosRead(*piphand, /* Read from the pipe */
prep_string, /* Into prep_string */
BUFSIZE - 1, /* A maximum of BUFSIZE - 1 bytes */
&BytesRead); /* Total bytes read goes here */
if (rc !=0) fprintf(stderr, "%s: DosRead failed (%ld)\n", Exename, rc);
/* DosRead is supposed to return 0 in BytesRead when it reaches EOF */
if (BytesRead > 0L) {
prep_string[(int)BytesRead] = 0; /* Stick a null at the end of the string */
fputs(prep_string, stdout); /* Write the string to stdout */
/* Debugging code */
#ifdef DEBUG
for (i = 0; i < strlen(prep_string); i++) {
fprintf(stderr, "%c (%3d)", prep_string[i], prep_string[i]);
if (i % 8 == 0) fprintf(stderr, "\n");
}
#endif
}
} while (rc == 0 && BytesRead > 0L);
} /* end of transfer_data */
/*
* build_batchfile
*
* Build the batch file that the Dos session will execute
*
*/
int build_batchfile(char *fname, char *errpname, char *cmd) {
int count;
FILE *bfile;
if (NULL == (bfile = fopen(fname, "w")))
return -1;
fprintf(bfile, "@echo off\n");
#ifdef USING_4DOS
fprintf(bfile, "%s >& %s\n", cmd, Pname);
fprintf(bfile, "echo %%? > %s\n", errpname);
#else
fprintf(bfile, "%s > %s\n", cmd, Pname);
fprintf(bfile, "echo 0 > %s\n", errpname);
#endif
fclose(bfile);
return 0;
}
/*
* read_errpipe
*
* Read the pipe that should contain the return code from the spawned program.
*
*/
void read_errpipe(ULONG targ) {
APIRET rc = 0;
ULONG bytesread;
int i;
rc = DosConnectNPipe(Rcdpipe);
#ifdef DEBUG_EMX
if (rc != 0) {
fprintf(stderr, "%s: DosConnectNPipe for Rcdpipe failed (%ld)\n", Exename, rc);
exit(-1);
}
#endif
/* "Prep" the string and the return code. */
RCstring[0] = 0;
do {
rc = DosRead(Rcdpipe, /* Read from the pipe */
RCstring, /* Into prep_string */
BUFSIZE - 1, /* A maximum of BUFSIZE - 1 bytes */
&bytesread); /* Total bytes read goes here */
/* if (rc !=0) fprintf(stderr, "%s: Return code pipe DosRead failed (%ld)\n", Exename, rc);*/
/* DosRead is supposed to return 0 in BytesRead when it reaches EOF */
if (bytesread > 0L) {
RCstring[(int)bytesread] = 0; /* Stick a null at the end of the string */
}
/* Debugging code
for (i = 0; i < strlen(RCstring); i++) {
fprintf(stderr, "%c (%3d)", RCstring[i], RCstring[i]);
if (i % 8 == 0) fprintf(stderr, "\n");
}
*/
} while (rc == 0 && bytesread > 0L);
#ifdef DEBUG_EMX
_endthread();
#else
DosExit(0, 0); /* Exit this thread */
#endif
} /* end of read_errpipe */