open-axiom repository from github
/* tm_openaxiom.c1* COPYRIGHT : (C) 2004 Bill Page <[email protected]>2* (Portions) COPYRIGHT : (C) 1999 Andrey Grozin3***********************************************************************4* This software falls under the GNU general public license and comes5* WITHOUT ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE6* for more details. If you don't have this file, write to the Free7* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,8* MA 02111-1307, USA.9***********************************************************************10*11* The program runs OPENAXIOMsys as a separate process under Windows12* using CreateProcess() and asynchronous reader threads. It provides13* an interface between OpenAxiom and TeXmacs for windows.14*15* Before launching, stdin/out/err are each redirected into pipes16* so that OPENAXIOM can be fed commands and its output read from17* those pipes. A separate thread is created to manage reading the18* output of OpenAxiom and sending it (appropriatedly modified) to19* TeXmacs. The main thread reads from TeXmacs and sends it's20* output to OpenAxiom. In this way the input and output is completely21* asynchronous. There may be some advantage to the use of such22* "light weight" threads in Windows.23*24* Build with the command:25* gcc tm_openaxiom.c texbreaker.c -o tm_openaxiom.exe26* It is known to compile with gcc version 3.4.2 (mingw-special)27* under MinGW/MSYS. Other compilers may also work.28*29* Install the tm_openaxiom.exe file in the directory30* C:\Program Files\WinTeXmacs\TeXmacs\bin31*32* Written by Bill Page 2004120333* - based on Maxima test version by Mike Thomas 2003062434* (Thankyou Mike!)35* and on the TeXmacs tm_openaxiom.c program (linux)36* Modified by Bill Page 2004121537* - add call to Robert Sutor line-break routine38* Modified by Bill Page 2004121739* - initialize OPENAXIOM_exe from OPENAXIOM environment variable40* Modified by Bill Page 2004122041* - use args, TM_OPENAXIOM environment variable and/or42* )set output texmacs option,option, ...43* to specify options44* break no -- disables line-break algorithm45* over no -- converts 2-d \over to 1-d /46* cdot no -- converts \cdot to \ (space)47* space no -- convert \ (space) to \,48* big( no -- convert \left( \right to ( )49* width 4.5 -- 4.5 inches * 100050* - process OpenAxiom output with no LaTeX content, e.g.51* )set output tex off52* )set output algebra on53* */5455#include <windows.h>56#include <process.h>57#include <memory.h>58#include <string.h>59#include <stdio.h>60#include <fcntl.h>61#include <io.h>6263/* Allow some debugging output from the IO threads */64/*#define DEBUG_OUT*/6566int option_texbreak = 1, /* default options */67option_use_over = 1,68option_use_cdot = 1,69option_big_space = 0,70option_big_paren = 1;7172#define INP_BUFF_SIZE 307273#define OUT_BUFF_SIZE 819274char szBuffer[OUT_BUFF_SIZE];75#define MATHBUFLEN 8*819276extern int maxLineWidth;77extern char bufout[2*MATHBUFLEN];7879int nBuffer=0;80int nRead;81int IgnorePrompts=0;8283#define READ_HANDLE 084#define WRITE_HANDLE 18586char OPENAXIOM_cmd[256];87char ENV_OPENAXIOM[256];8889int fdStdOutPipe[2], fdStdInPipe[2], fdStdErrPipe[2];9091HANDLE hProcess;92STARTUPINFO si; /* Only need to set si.cb */93PROCESS_INFORMATION pi; /* Post launch child process information. */9495/* consult the environment */9697void parse_options(int nargs, char *args[]) {98char *pOPENAXIOM, *pTM_OPENAXIOM, *pOption;99int i;100float w;101102if (pOPENAXIOM=getenv("OPENAXIOM")) {103strcpy(OPENAXIOM_cmd,pOPENAXIOM); strcat(OPENAXIOM_cmd, "/../../../../bin/open-axiom.exe");104strcat(OPENAXIOM_cmd," --system=\""); strcat(OPENAXIOM_cmd,pOPENAXIOM);105strcat(OPENAXIOM_cmd,"\"");106strcpy(ENV_OPENAXIOM,"OPENAXIOM="); strcat(ENV_OPENAXIOM, pOPENAXIOM);107}108else {109printf("You must set the OPENAXIOM environment variable, e.g.\n");110exit(1);111}112113/* e.g. TM_OPENAXIOM='break off, over yes, ...114* if not found or different options, ignore silently */115for (i=0;i<nargs;i++) {116pTM_OPENAXIOM=args[i]; /* args override environment */117if (i>0 || (pTM_OPENAXIOM=getenv("TM_OPENAXIOM"))) {118if (strstr(pTM_OPENAXIOM,"break off")119|| strstr(pTM_OPENAXIOM,"break n")120|| strstr(pTM_OPENAXIOM,"break 0"))121option_texbreak=0;122if (strstr(pTM_OPENAXIOM,"break on")123|| strstr(pTM_OPENAXIOM,"break y")124|| strstr(pTM_OPENAXIOM,"break 1"))125option_texbreak=1;126if (strstr(pTM_OPENAXIOM,"over off")127|| strstr(pTM_OPENAXIOM,"over n")128|| strstr(pTM_OPENAXIOM,"over 0"))129option_use_over=0;130if (strstr(pTM_OPENAXIOM,"over on")131|| strstr(pTM_OPENAXIOM,"over y")132|| strstr(pTM_OPENAXIOM,"over 1"))133option_use_over=1;134if (strstr(pTM_OPENAXIOM,"cdot off")135|| strstr(pTM_OPENAXIOM,"cdot n")136|| strstr(pTM_OPENAXIOM,"cdot 0"))137option_use_cdot=0;138if (strstr(pTM_OPENAXIOM,"cdot on")139|| strstr(pTM_OPENAXIOM,"cdot y")140|| strstr(pTM_OPENAXIOM,"cdot 1"))141option_use_cdot=1;142if (strstr(pTM_OPENAXIOM,"space off")143|| strstr(pTM_OPENAXIOM,"space n")144|| strstr(pTM_OPENAXIOM,"space 0"))145option_big_space=0;146if (strstr(pTM_OPENAXIOM,"space on")147|| strstr(pTM_OPENAXIOM,"space y")148|| strstr(pTM_OPENAXIOM,"space 1"))149option_big_space=1;150if (strstr(pTM_OPENAXIOM,"big( off")151|| strstr(pTM_OPENAXIOM,"big( n")152|| strstr(pTM_OPENAXIOM,"big( 0"))153option_big_paren=0;154if (strstr(pTM_OPENAXIOM,"big( on")155|| strstr(pTM_OPENAXIOM,"big( y")156|| strstr(pTM_OPENAXIOM,"big( 1"))157option_big_paren=1;158if (pOption=strstr(pTM_OPENAXIOM,"width ")) {159sscanf(pOption+strlen("width "),"%f",&w);160maxLineWidth=trunc(1000.0*w);161};162};163};164}165166/* This is the OpenAxiom Reader thread. It runs asynchronously and167* in parallel with the main thread. It reads output from OpenAxiom168* and after some selection and conversion, it is sent on to169* TeXmacs. */170171unsigned __stdcall StdOutReadThread ( void* pArguments )172{173int nExitCode = STILL_ACTIVE;174175fputs("\2verbatim:",stdout); /* ---> to TeXmacs */176177GetExitCodeProcess ( hProcess, (unsigned long*) &nExitCode );178179while ( nExitCode == STILL_ACTIVE ) {180#ifdef DEBUG_OUT181fprintf ( stderr, " - Stdout read loop\n" );182fflush ( stderr );183#endif /* DEBUG_OUT */184nRead = _read ( fdStdOutPipe[READ_HANDLE], &szBuffer[nBuffer],185OUT_BUFF_SIZE-nBuffer ); /* Read <--- from OpenAxiom */186187szBuffer[nBuffer+nRead]='\0';188#ifdef DEBUG_OUT189fprintf ( stderr, "After _read on stdoutpipe (%d bytes read)\n%s\n",190nRead, &szBuffer[nBuffer] ); fflush ( stderr );191#endif /* DEBUG_OUT */192while (nRead>0) { /* fill buffer until we see OpenAxiom prompt */193if ( strncmp(&szBuffer[nBuffer],"-> ",3)==0 ) { /* done */194HandleOutput(szBuffer, nBuffer); /* send output ---> to TeXmacs */195nRead = nRead-3; /* keep the left overs */196if (nRead>0) strncpy(szBuffer,&szBuffer[nBuffer+3],nRead);197nBuffer = 0; szBuffer[nRead]='\0';198#ifdef DEBUG_OUT199fprintf(stderr, "Leftovers\n%s\n",&szBuffer);200fflush(stderr);201#endif /* DEBUG_OUT */202} else {203nBuffer++;204nRead--;205};206};207GetExitCodeProcess ( hProcess, (unsigned long*) &nExitCode );208};209210fputs("\2latex:\\red The end\\black\5\5",stdout); /* The ends --> TeXmacs */211fflush(stdout);212_endthreadex(0);213}214215/* This routine parses all OpenAxiom output between -> prompts */216217HandleOutput(char Buffer[], int n) /* sends output ---> to TeXmacs */218{219int mmode, i, j;220221#ifdef DEBUG_OUT222fprintf(stderr, "HandleOutput IgnorePrompts:%d\n%s\n",IgnorePrompts,223Buffer);224fflush(stderr);225#endif /* DEBUG_OUT */226if (IgnorePrompts) --IgnorePrompts; /* counting down */227else { /* its a good packet */228while (n>0 && Buffer[n-1]=='\n') n--;229Buffer[n]='\0'; /* mark the real end */230mmode = 0; j=0;231while (Buffer[j]=='\n') j++; /* find the real start */232for (i=j;i<n;i++) {233if (strncmp(&Buffer[i],"$$\n",3)==0) { /* Found a LaTeX marker */234#ifdef DEBUG_OUT235fprintf(stderr, "LaTeX marker at: %d, mode: %d\n",i,mmode);236fflush(stderr);237#endif /* DEBUG_OUT */238Buffer[i]='\0'; /* something ends here */239if (mmode<=0) { /* we were not in mathmode */240fputs(&Buffer[j],stdout); /* send the plain text ---> to TeXmacs */241mmode = i+3; /* start of LaTeX */242fputs("\2latex:$$",stdout);/* tell ---> TeXmacs */243} else { /* we were in mathmode */244HandleLatex(&Buffer[mmode]); /* make some adjustments */245fputs("$$\5",stdout); /* Say "that's all" ---> to TeXmacs */246mmode = -1; j=i+3; /* aftermath */247}248} else { /* this is not a marker */249if (mmode<=0) { /* if not in mathmode */250if (strncmp(&Buffer[i],"Type:",5)==0) { /* look for OpenAxiom Type */251int k;252k=i-1; while (Buffer[k]==' ') k--;253Buffer[k]='\0'; /* mark end of previous */254fputs(&Buffer[j],stdout); /* send the plain text -> to TeXmacs */255fprintf(stdout,"\2latex:\\openaxiomtype{%s}\5",256&Buffer[i+6]);257i=n; j=n; /* and we are done */258}259}260}261};262if (j<n) {263fputs(&Buffer[j],stdout); /* send the plain text ---> to TeXmacs */264};265/* ask for some more work from ---> TeXmacs */266fputs("\2channel:prompt\5\2latex:\\red$\\rightarrow$\\ \5\5",stdout);267fflush(stdout); /* Say "give me a prompt" ---> to TeXmacs */268fputs("\2verbatim:",stdout); /* maybe plain text next ---> to TeXmacs */269}270}271272HandleLatex(char buf[]) { /* fixup Latex coding */273274char *ptr1, *ptr2, *ptr3;275char label[64];276277/* /n -> blank */278while (ptr1=strchr(buf,'\n')) { *ptr1=' '; };279280if (option_texbreak) { /* break long TeX lines */281282/* prepare OpenAxiom output in buf for call to texbreak */283284/* remove the label and save it for later */285label[0]='\0';286while (ptr1=strstr(buf,"\\leqno(")) {287if ((ptr2=strchr(ptr1,')')) && (ptr2-ptr1<64)) {288ptr2++;289strncpy(label,ptr1,ptr2-ptr1); label[ptr2-ptr1]='\0';290strcpy(ptr1,ptr2);291}292};293294texbreak(buf); /* output is in global external called bufout */295strcat(bufout,"\\hfill \\leqno ");296strcat(bufout,&label[6]); /* put label back */297} else {298strcpy(bufout,buf);299};300301/* do some LaTex conversions for TeXmacs */302303/* \root { a } \of { b } ---> \sqrt[ a ] { b } */304/* ptr1^ ptr2^ ^ptr3 ^ ^ ptr1^ ^ */305306while (ptr1=strstr(bufout,"\\root")) {307ptr2=ptr1+5; while (*ptr2==' ') ptr2++;308if ((*ptr2=='{') && (ptr3=strstr(ptr2,"}"))) {309#ifdef DEBUG_OUT310fprintf(stderr, "ptr1: %s\nptr2: %s\nptr3: %s\n",ptr1,ptr2,ptr3);311fflush(stderr);312#endif /* DEBUG_OUT */313strncpy(ptr1,"\\sqrt[",6); ptr1=ptr1+6;314strncpy(ptr1,ptr2+1,ptr3-ptr2-1); ptr1=ptr1+(ptr3-ptr2-1);315strncpy(ptr1,"]",1); ptr1++;316ptr3++; while (*ptr3==' ') ptr3++;317#ifdef DEBUG_OUT318fprintf(stderr, "ptr1: %s\nptr2: %s\nptr3: %s\n",ptr1,ptr2,ptr3);319fflush(stderr);320#endif /* DEBUG_OUT */321if (strncmp(ptr3,"\\of",3)==0) {322ptr3+=3; while (*ptr3==' ') ptr3++;323if (*ptr3=='{') {324strcpy(ptr1,ptr3);325} else {326error("No \\of { \n");327}328} else {329error("No \\of \n");330}331} else {332error("No \\root { } \n");333}334};335336if (!option_big_space) { /* use smaller spaces \ ---> \, */337while (ptr1=strstr(bufout,"\\ ")) {338strncpy(ptr1,"\\,",2); /* use thin spaces */339};340};341342/* other possible conversions */343344if (!option_use_cdot) {345while (ptr1=strstr(bufout,"\\cdot")) {346strncpy(ptr1,"\\ ",2);347strcpy(ptr1+2,ptr1+5);348};349};350351if (!option_big_paren) {352while (ptr1=strstr(bufout,"\\left(")) {353strncpy(ptr1,"(",1);354strcpy(ptr1+1,ptr1+6);355};356while (ptr1=strstr(bufout,"\\right)")) {357strncpy(ptr1,")",1);358strcpy(ptr1+1,ptr1+7);359};360};361if (!option_use_over) {362while (ptr1=strstr(bufout,"\\over")) {363strncpy(ptr1,"/",1);364strcpy(ptr1+1,ptr1+5);365};366};367#ifdef DEBUG_OUT368fprintf(stderr, "HandleLatex:\n%s\n",bufout);369fflush(stderr);370#endif /* DEBUG_OUT */371fputs(bufout,stdout); /* send the LaTeX code ---> to TeXmacs */372}373374unsigned __stdcall StdErrReadThread ( void* pArguments )375{376int nExitCode = STILL_ACTIVE;377int nRead;378char szBuffer[OUT_BUFF_SIZE];379380GetExitCodeProcess ( hProcess, (unsigned long*) &nExitCode );381382while ( nExitCode == STILL_ACTIVE ) {383#ifdef DEBUG_OUT384fprintf ( stderr, " - Stderr read loop\n" ); fflush ( stderr );385#endif /* DEBUG_OUT */386nRead = _read ( fdStdErrPipe[READ_HANDLE], szBuffer, OUT_BUFF_SIZE );387#ifdef DEBUG_OUT388fprintf ( stderr, "After _read on stderrpipe (%d bytes read)\n",389nRead );390fflush ( stderr );391#endif /* DEBUG_OUT */392if ( nRead ) { /* just pass the message on through to TeXmacs */393fwrite(szBuffer, 1, nRead, stderr);394}395GetExitCodeProcess ( hProcess, (unsigned long*) &nExitCode );396}397_endthreadex(0);398}399400void pipe_write(int fdPipe, char Buffer[])401{402_write(fdPipe, Buffer, strlen(Buffer) );403}404405/* process texmacs options */406407process_options(char line[]) {408char *optargs[2];409410optargs[1]=line;411if (*(optargs[1])!='\n') {412parse_options(2,optargs);413} else { /* display current options */414show_options();415};416/* ask for some more work from ---> TeXmacs */417fputs("\2channel:prompt\5\2latex:\\red$\\rightarrow$\\ \5\5",stdout);418fflush(stdout); /* Say "give me a prompt" ---> to TeXmacs */419fputs("\2verbatim:",stdout); /* maybe plain text next ---> to TeXmacs */420421#ifdef DEBUG_OUT422fprintf ( stderr,423"TM_OPENAXIOM=texbreak:%d,use_over:%d,use_cdot:%d,big_space:%d,big_paren:%d,\n",424option_texbreak, option_use_over, option_use_cdot,425option_big_space, option_big_paren );426fprintf ( stderr,427" maxLineWidth:%d\n", maxLineWidth );428fflush ( stderr );429#endif /* DEBUG_OUT */430}431432show_options() {433fprintf(stdout,434"--------------------------- The texmacs Option ----------------------------\n\435\n\436Description: options for display of OPENAXIOM output in TeXmacs\n\437\n\438)set output texmacs is used to control the TeXmacs-OPENAXIOM interface\n\439The default values are controlled by environment variable TM_OPENAXIOM\n\440and may be overriden by command line options.\n\441\n\442Syntax: )set output texmacs <arg>\n\443where arg can be one or more of\n\444break <on>|<off> line-break algorithm\n\445over <on>|<off> convert 2-d \\over to 1-d /\n\446cdot <on>|<off> convert \\cdot to \\ (space)\n\447space <on>|<off> convert \\ (space) to \\,\n\448big( <on>|<off> convert \\left( \\right to ( )\n\449width <9.99> line width in inches\n\450\n\451<on> may be on, yes, 1\n\452<off> may be off, no , 0\n\453\n\454The current settings are:\n\455break %d, over %d, cdot %d, space %d, big( %d, width %2.3f\n",456option_texbreak, option_use_over, option_use_cdot,457option_big_space, option_big_paren, maxLineWidth/1000.0 );458459};460461int main(int nargs, char *args[])462{463HANDLE hStdOutReadThread;464int fdStdOut, fdStdIn;465unsigned threadIDOut;466HANDLE hStdErrReadThread;467int fdStdErr;468unsigned threadIDErr;469int i;470char line[INP_BUFF_SIZE];471472parse_options(nargs,args);473474#ifdef DEBUG_OUT475fprintf ( stderr, "CREATE PROCESS: %s\n", OPENAXIOM_cmd );476fprintf ( stderr, "Setting env var: %s\n", ENV_OPENAXIOM );477fprintf ( stderr,478"TM_OPENAXIOM=texbreak:%d,use_over:%d,use_cdot:%d,big_space:%d,big_paren:%d,\n",479option_texbreak, option_use_over, option_use_cdot,480option_big_space, option_big_paren );481fprintf ( stderr,482" maxLineWidth:%d\n", maxLineWidth );483fflush ( stderr );484#endif /* DEBUG_OUT */485486if ( -1 == _setmode( _fileno( stdin ), _O_BINARY ) ) {487perror ( "machinfo: Cannot set stdin BINARY mode" ); exit(1);488};489if ( -1 == _setmode( _fileno( stdout ), _O_BINARY ) ) {490perror ( "machinfo: Cannot set stdout BINARY mode" ); exit(1);491};492if ( -1 == _setmode( _fileno( stderr ), _O_BINARY ) ) {493perror ( "machinfo: Cannot set stderr BINARY mode" ); exit(1);494};495496/* Make pipes to be passed to the spawned process as stdin/out/err */497if ( _pipe ( fdStdOutPipe, 512, O_BINARY | O_NOINHERIT ) == -1 ) return 1;498if ( _pipe ( fdStdInPipe, 512, O_BINARY | O_NOINHERIT ) == -1 ) return 1;499if ( _pipe ( fdStdErrPipe, 512, O_BINARY | O_NOINHERIT ) == -1 ) return 1;500501/* Duplicate and save original stdin/out/err handles */502fdStdOut = _dup ( _fileno(stdout) );503fdStdIn = _dup ( _fileno(stdin) );504fdStdErr = _dup ( _fileno(stderr) );505506/* Duplicate write end of new pipes to current stdout/err handles,507* read to stdin */508if ( _dup2 ( fdStdOutPipe[WRITE_HANDLE], _fileno(stdout) ) != 0 ) return 2;509if ( _dup2 ( fdStdInPipe[READ_HANDLE], _fileno(stdin) ) != 0 ) return 2;510if ( _dup2 ( fdStdErrPipe[WRITE_HANDLE], _fileno(stderr) ) != 0 ) return 2;511/* Close the duplicated handles to the new pipes */512close ( fdStdOutPipe[WRITE_HANDLE] );513close ( fdStdInPipe[READ_HANDLE] );514close ( fdStdErrPipe[WRITE_HANDLE] );515516putenv ( ENV_OPENAXIOM );517518/* Zero startup and process info structures, take care of Windows519* startup info structure future proofing. */520ZeroMemory( &si, sizeof(si) );521si.cb = sizeof(si);522ZeroMemory( &pi, sizeof(pi) );523524/* Start the child process. */525if ( !CreateProcess(526NULL, /* No module name (use command line). */527OPENAXIOM_cmd, /* Command line. */528NULL, /* Process handle not inheritable. */529NULL, /* Thread handle not inheritable. */530TRUE, /* Allow handle inheritance. */5310, /* No creation flags. */532NULL, /* Use parent's environment block. */533NULL, /* Use parent's starting directory. */534&si, /* Pointer to STARTUPINFO structure.*/535&pi ) /* Pointer to PROCESS_INFORMATION structure. */ ) {536fprintf(stderr, "CreateProcess failed: %s\n", args[1]);537fflush(stderr);538return -1;539}540hProcess = pi.hProcess;541542/* Now that the process is launched,543* replace the original stdin/out/err handles */544if ( _dup2 ( fdStdOut, _fileno ( stdout ) ) != 0 ) return 3;545if ( _dup2 ( fdStdIn, _fileno ( stdin ) ) != 0 ) return 3;546if ( _dup2 ( fdStdErr, _fileno ( stderr ) ) != 0 ) return 3;547548/* Close duplicates */549close(fdStdOut);550close(fdStdIn);551close(fdStdErr);552553/* The child process will become the OpenAxiom read filter and554* we will be the OpenAxiom write filter. */555556/* Create the OpenAxiom stderr listening thread. (Doesn't do much.) */557hStdErrReadThread = (HANDLE)_beginthreadex( NULL, 0, &StdErrReadThread,558NULL, 0, &threadIDErr );559if ( 0 == hStdErrReadThread ) return 5;560561/* * * * * * * * * */562/* start talking! */563/* * * * * * * * * */564565IgnorePrompts = 5; /* Tell the OpenAxiom Reader to ignore first 5 prompts */566/* then create the OpenAxiom stdout Reader thread.*/567hStdOutReadThread = (HANDLE)_beginthreadex( NULL, 0, &StdOutReadThread,568NULL, 0, &threadIDOut );569if ( 0 == hStdOutReadThread ) return 5;570/* start force-feeding ---> to OpenAxiom */571pipe_write(fdStdInPipe[WRITE_HANDLE],")set message prompt plain\n" ); /* 1 */572pipe_write(fdStdInPipe[WRITE_HANDLE],")set messages autoload off\n" );/* 2 */573pipe_write(fdStdInPipe[WRITE_HANDLE],")set quit unprotected\n" ); /* 3 */574pipe_write(fdStdInPipe[WRITE_HANDLE],")set output tex on\n" ); /* 4 */575pipe_write(fdStdInPipe[WRITE_HANDLE],")set output algebra off\n" ); /* 5 */576while (fgets(line,INP_BUFF_SIZE,stdin)!=NULL) { /* wait <--- for TeXmacs */577#ifdef DEBUG_OUT578fprintf ( stderr, "Input:\n%s", line );579#endif /* DEBUG_OUT */580if (strncmp(line,")set output texmacs",581strlen(")set output texmacs"))==0) { /* process texmacs options */582process_options(&line[strlen(")set output texmacs")]);583} else {584pipe_write(fdStdInPipe[WRITE_HANDLE], line ); /* Start ---> OpenAxiom */585}586};587if (nBuffer) HandleOutput(szBuffer,nBuffer); /* anything leftover? */588pipe_write(fdStdInPipe[WRITE_HANDLE], ")quit\n" ); /* stop work */589590WaitForSingleObject ( hStdErrReadThread, INFINITE );591WaitForSingleObject ( hStdOutReadThread, INFINITE );592593/* Wait until child process exits to block the terminal. */594WaitForSingleObject( pi.hProcess, INFINITE );595596/* As we are using gebinthreadex/endthreadex,597* we must close the thread handles. */598CloseHandle ( hStdOutReadThread );599CloseHandle ( hStdErrReadThread );600601return 0;602}603604605