aas@brolga.cc.uq.oz.au (Alex Sergejew) (07/15/90)
The following are comments and patches to the patches to Larry Wall's patch program (patchlevel 12) recently submitted by ward@sneezy.cs.wisc.edu (Michael Ward) to allow compilation with Turbo C 2.0 and to run under MSDOS. I have addressed a couple of recent bug reports and also re-enabled the use of ed scripts (given a working MSDOS ed program, of which simtel20 has several that are compatible with unix V7 ed). New files are: popen c 10579 7-15-90 3:58p popen h 69 3-08-88 8:08p getswitc c 290 3-08-88 8:08p popentst mak 303 7-15-90 3:43p These provide the popen() functionality and were snarfed from simtel20 in `pd1:<msdos.turbo-c>popen.arc'. The original author's name is not given. Try `make -fpopentst' to check-out the DEMO mode (see popen.c) but remember to delete popen.obj and getswitc.obj from this trial before compiling the merged/patched version of patch. Alex A Sergejew University of Queensland Neuro-Psychiatric Institute aas@cc.uq.oz.au --------------------------- weisen@eniac.seas.upenn.edu (Neil Weisenfeld) reports getting a run-time error of the form: patch: couldn't create D:\/patchpxxxxxx This is due to having the TMP environment variable set to "D:\" (note that a lot of MSDOS software actually wants the trailing "\"). Patch as distributed blindly concatenates /patch?xxxxxx strings and the "\/" juxtaposition causes strife to MSDOS. The problem does not arise using other shells such as ms_sh or ksh. A temporary workaround is to re-set the TMP environment variable without the trailing "\". This has been repaired in the appended patches. --------------------------- mlord@bwdls58.UUCP correctly observes that: >>In file INP.C, there is a procedure called rev_in_string() at the bottom. >>This procedure declares a local variable "s", which is then used without >>first being given a value. This instance of "s" should be replaced with >>the parameter "string" instead on that one line. This has also been repaired in the appended patches. --------------------------- There are still warnings reported by Turbo C in compiling the distribution: In pch.c: 243: Possible use of 'fcntl_line' before definition ... the variable concerned is not in fact used before definition and the warning can be safely ignored. In popen.c there appear 3 warnings of the type: xxx: 'lineno' is assigned a value which is never used in function ... which can also be ignored (the variable is used in DEMO mode). In util.c: 177: possibly incorrect assignment .... the message is misleading (see the Turbo C User Manual). --------------------------- *** makefile Sat Jun 02 16:13:34 1990 --- makefile.new Sun Jul 15 15:59:34 1990 *************** *** 1,8 **** CC = tcc CFLAGS = -N -ml -DTURBOC20 ! c = patch.c pch.c inp.c version.c util.c ! obj = patch.obj pch.obj inp.obj util.obj version.obj .c.obj: $(CC) -c $(CFLAGS) $(LARGE) $*.c --- 1,8 ---- CC = tcc CFLAGS = -N -ml -DTURBOC20 ! c = patch.c pch.c inp.c version.c util.c popen.c getswitc.c ! obj = patch.obj pch.obj inp.obj version.obj util.obj popen.obj getswitc.obj .c.obj: $(CC) -c $(CFLAGS) $(LARGE) $*.c *************** *** 12,18 **** patch.obj: config.h common.h patch.c inp.h pch.h util.h version.h ! pch.obj: config.h common.h pch.c pch.h util.h inp.obj: config.h common.h inp.c inp.h util.h --- 12,18 ---- patch.obj: config.h common.h patch.c inp.h pch.h util.h version.h ! pch.obj: config.h common.h pch.c pch.h util.h popen.h inp.obj: config.h common.h inp.c inp.h util.h *************** *** 19,21 **** --- 19,25 ---- util.obj: config.h common.h util.c util.h version.obj: config.h common.h version.c version.h patchlevel.h util.h + + popen.obj: popen.c popen.h + + getswitc.obj: getswitc.c *** inp.c Sun Jul 15 12:41:14 1990 --- inp.new Sun Jul 15 13:50:30 1990 *************** *** 1,6 **** ! /* $Header: inp.c,v 2.0.1.2 90/05/30 11:30:00 mward $ * * $Log: inp.c,v $ * Revision 2.0.1.2 90/05/30 11:30:00 mward * if TURBOC20 have plan_a check against MAXMEMBLK before mallocs * if TURBOC20 have plan_b use binary mode io for tmp file --- 1,10 ---- ! /* $Header: inp.c,v 2.0.1.3 90/07/15 13:52:00 aas $ * * $Log: inp.c,v $ + * Revision 2.0.1.3 90/07/15 13:52:00 aas + * include bugfix reported by mlord@bwdls58.bnr.ca in rev_in_string() + * regarding parameter string[] typo in first strnEQ() line + * * Revision 2.0.1.2 90/05/30 11:30:00 mward * if TURBOC20 have plan_a check against MAXMEMBLK before mallocs * if TURBOC20 have plan_b use binary mode io for tmp file *************** *** 323,329 **** if (revision == Nullch) return TRUE; patlen = strlen(revision); ! if (strnEQ(string,revision,patlen) && isspace(s[patlen])) return TRUE; for (s = string; *s; s++) { if (isspace(*s) && strnEQ(s+1, revision, patlen) && --- 327,333 ---- if (revision == Nullch) return TRUE; patlen = strlen(revision); ! if (strnEQ(string,revision,patlen) && isspace(string[patlen])) return TRUE; for (s = string; *s; s++) { if (isspace(*s) && strnEQ(s+1, revision, patlen) && *** patch.c Sun Jul 15 12:46:42 1990 --- patch.new Sun Jul 15 14:02:18 1990 *************** *** 1,5 **** char rcsid[] = ! "$Header: patch.c,v 2.0.1.7 90/05/30 11:30:00 mward $"; /* patch - a program to apply diffs to original files * --- 1,5 ---- char rcsid[] = ! "$Header: patch.c,v 2.0.1.8 90/07/15 14:00:00 aas $"; /* patch - a program to apply diffs to original files * *************** *** 9,14 **** --- 9,17 ---- * money off of it, or pretend that you wrote it. * * $Log: patch.c,v $ + * Revision 2.0.1.7 90/07/15 14:00:00 aas + * made init temp file names a little more MS-DOS aware + * * Revision 2.0.1.7 90/05/30 11:30:00 mward * Added usage message vars and routines: use_msg, progname, usage, fname * Check environment for TMP and init temp file names *************** *** 206,212 **** int i; char tmpdir[MAXPATH]; char *tmpenv; ! progname = (*argv[0]?argv[0]:"patch"); setbuf(stderr, serrbuf); --- 209,215 ---- int i; char tmpdir[MAXPATH]; char *tmpenv; ! char c; progname = (*argv[0]?argv[0]:"patch"); setbuf(stderr, serrbuf); *************** *** 216,222 **** /* initialize temp file names */ tmpenv = getenv("TMP"); strcpy(tmpdir,(tmpenv!=NULL?tmpenv:"")); ! if (strlen(tmpdir) && tmpdir[strlen(tmpdir)-1]!='/') strcat(tmpdir,"/"); TMPOUTNAME = strcat(strcpy(malloc(MAXPATH),tmpdir),fname(TMPOUTNAME)); TMPINNAME = strcat(strcpy(malloc(MAXPATH),tmpdir),fname(TMPINNAME)); TMPREJNAME = strcat(strcpy(malloc(MAXPATH),tmpdir),fname(TMPREJNAME)); --- 219,227 ---- /* initialize temp file names */ tmpenv = getenv("TMP"); strcpy(tmpdir,(tmpenv!=NULL?tmpenv:"")); ! if (strlen(tmpdir)) ! if((c=tmpdir[strlen(tmpdir)-1])!='/' && c!='\\') ! strcat(tmpdir,"/"); TMPOUTNAME = strcat(strcpy(malloc(MAXPATH),tmpdir),fname(TMPOUTNAME)); TMPINNAME = strcat(strcpy(malloc(MAXPATH),tmpdir),fname(TMPINNAME)); TMPREJNAME = strcat(strcpy(malloc(MAXPATH),tmpdir),fname(TMPREJNAME)); *** pch.c Sun Jul 15 12:41:50 1990 --- pch.new Sun Jul 15 15:23:00 1990 *************** *** 1,6 **** ! /* $Header: pch.c,v 2.0.1.7 90/05/30 11:30:00 mward $ * * $Log: pch.c,v $ * Revision 2.0.1.8 90/05/30 11:30:00 mward * replace do_ed_script with stub * modify calls to read, write to detect error on return value of -1 --- 1,9 ---- ! /* $Header: pch.c,v 2.0.1.9 90/07/15 15:27:00 aas $ * * $Log: pch.c,v $ + * Revision 2.0.1.9 90/07/15 15:27:00 aas + * put do_ed_script() back the way it was and slot-in MSDOS popen/pclose + * * Revision 2.0.1.8 90/05/30 11:30:00 mward * replace do_ed_script with stub * modify calls to read, write to detect error on return value of -1 *************** *** 38,43 **** --- 41,49 ---- #include "util.h" #include "INTERN.h" #include "pch.h" + #ifdef TURBOC20 + #include "popen.h" + #endif /* Patch (diff listing) abstract type. */ *************** *** 1062,1077 **** return p_hunk_beg; } - #ifdef TURBOC20 - /* Ignore an ed script. */ - - void - do_ed_script() - { - say1("Sorry, can't do ed scripts yet!\n"); - say1("Use ed to do this one yourself!\n"); - } - #else /* Apply an ed script by feeding ed itself. */ void --- 1068,1073 ---- *************** *** 1086,1095 **** --- 1082,1095 ---- if (!skip_rest_of_patch) { Unlink(TMPOUTNAME); copy_file(filearg[0], TMPOUTNAME); + #ifdef TURBOC20 + Sprintf(buf, "ed %s", TMPOUTNAME); + #else if (verbose) Sprintf(buf, "/bin/ed %s", TMPOUTNAME); else Sprintf(buf, "/bin/ed - %s", TMPOUTNAME); + #endif pipefp = popen(buf, "w"); } for (;;) { *************** *** 1135,1138 **** chmod(outname, filemode); set_signals(1); } ! #endif --- 1135,1138 ---- chmod(outname, filemode); set_signals(1); } ! *** /dev/null --- popen.h Tue Mar 08 20:08:36 1988 *************** *** 0 **** --- 1,2 ---- + extern FILE *popen( char *, char * ); + extern int pclose( FILE * ); *** /dev/null --- popen.c Sun Jul 15 15:58:08 1990 *************** *** 0 **** --- 1,339 ---- + /* popen/pclose: + * + * simple MS-DOS piping scheme to imitate UNIX pipes + * + * $Log: popen.c,v $ + * Revision 1.0.1 90/07/15 14:05:00 aas + * fix assumption of trailing "/" in popen() + * + * Revision 1.0.0 88/08/03 20:08:00 ??? + * original distribution: author unknown + * found on simtel20 as pd1:<msdos.turbo-c>popen.arc + * + */ + + #include <stdio.h> + #include <ctype.h> + #include <alloc.h> + #include <string.h> + #include <errno.h> + #include <setjmp.h> + #include <process.h> + + #include "popen.h" + + extern char *getenv( char * ); + + #ifndef _NFILE + # define _NFILE OPEN_MAX /* Number of open files */ + #endif _NFILE + + #define READIT 1 /* Read pipe */ + #define WRITEIT 2 /* Write pipe */ + + static char *prgname[ _NFILE ]; /* program name if write pipe */ + static int pipetype[ _NFILE ]; /* 1=read 2=write */ + static char *pipename[ _NFILE ]; /* pipe file name */ + + /* + *------------------------------------------------------------------------ + * stoupper: Convert string to uppercase (in place) + *------------------------------------------------------------------------ + */ + + static void + stoupper( s ) + char *s; + { + int c; + for( ; (c = *s) != '\0'; ++s ) { + if( islower( c ) ) *s = _toupper( c ); + } + } + + /* + *------------------------------------------------------------------------ + * strsave: Copy string into malloc'ed memory and return address + *------------------------------------------------------------------------ + */ + + static char * + strsave( s ) + char *s; + { + char *sp = malloc( strlen( s ) + 1 ); + if( sp != (char *) NULL ) (void) strcpy( sp, s ); + return( sp ); + } + + /* + *------------------------------------------------------------------------ + * strfree: Returm strsave'd string memory + *------------------------------------------------------------------------ + */ + + static void + strfree( s ) + char *s; + { + if( s != (char *) NULL ) free( s ); + } + + /* + *------------------------------------------------------------------------ + * run: Execute command via SHELL or COMSPEC + *------------------------------------------------------------------------ + */ + + static int + run( command ) + char *command; + { + jmp_buf panic; /* How to recover from errors */ + int lineno; /* Line number where panic happened */ + char *shell; /* Command processor */ + char *s = (char *) NULL; /* Holds the command */ + int s_is_malloced = 0; /* True if need to free 's' */ + static char *command_com = "COMMAND.COM"; + int status; /* Return codes */ + char *shellpath; /* Full command processor path */ + char *bp; /* Generic string pointer */ + static char dash_c[ 3 ] = { '?', 'c', '\0' }; + if( (lineno = setjmp( panic )) != 0 ) { + int E = errno; + #ifdef DEMO + fprintf( stderr, "RUN panic on line %d: %d\n", lineno, E ); + #endif DEMO + if( s_is_malloced && (s != (char *) NULL) ) strfree( s ); + errno = E; + return( -1 ); + } + if( (s = strsave( command )) == (char *) NULL ) longjmp( panic, __LINE__ ); + /* Determine the command processor */ + if( ((shell = getenv( "SHELL" )) == (char *) NULL) && + ((shell = getenv( "COMSPEC" )) == (char *) NULL) ) shell = command_com; + stoupper( shell ); + shellpath = shell; + /* Strip off any leading backslash directories */ + shell = strrchr( shellpath, '\\' ); + if( shell != (char *) NULL ) ++shell; + else shell = shellpath; + /* Strip off any leading slash directories */ + bp = strrchr( shell, '/' ); + if( bp != (char *) NULL ) shell = ++bp; + if( strcmp( shell, command_com ) != 0 ) { + /* MKS Shell needs quoted argument */ + char *bp; + if( (bp = s = malloc( strlen( command ) + 3 )) == (char *) NULL ) + longjmp( panic, __LINE__ ); + *bp++ = '\''; + while( (*bp++ = *command++) != '\0' ); + *(bp - 1) = '\''; + *bp = '\0'; + s_is_malloced = 1; + } else s = command; + dash_c[ 0 ] = getswitch(); + /* Run the program */ + #ifdef DEMO + fprintf( stderr, "Running: (%s) %s %s %s\n", shellpath, shell, dash_c, s ); + #endif DEMO + status = spawnl( P_WAIT, shellpath, shell, dash_c, s, (char *) NULL ); + if( s_is_malloced ) free( s ); + return( status ); + } + + /* + *------------------------------------------------------------------------ + * uniquepipe: returns a unique file name + *------------------------------------------------------------------------ + */ + + static char * + uniquepipe() + { + static char name[ 14 ]; + static short int num = 0; + (void) sprintf( name, "pipe%05d.tmp", num++ ); + return( name ); + } + + /* + *------------------------------------------------------------------------ + * resetpipe: Private routine to cancel a pipe + *------------------------------------------------------------------------ + */ + + static void + resetpipe( fd ) + int fd; + { + char *bp; + if( (fd >= 0) && (fd < _NFILE) ) { + pipetype[ fd ] = 0; + if( (bp = pipename[ fd ]) != (char *) NULL ) { + (void) unlink( bp ); + strfree( bp ); + pipename[ fd ] = (char *) NULL; + } + if( (bp = prgname[ fd ]) != (char *) NULL ) { + strfree( bp ); + prgname[ fd ] = (char *) NULL; + } + } + } + + /* + *------------------------------------------------------------------------ + * popen: open a pipe + *------------------------------------------------------------------------ + */ + + FILE *popen( prg, type ) + char *prg; /* The command to be run */ + char *type; /* "w" or "r" */ + { + FILE *p = (FILE *) NULL; /* Where we open the pipe */ + int ostdin; /* Where our stdin is now */ + int pipefd = -1; /* fileno( p ) -- for convenience */ + char tmpfile[ BUFSIZ ]; /* Holds name of pipe file */ + char *tmpdir; /* Points to directory prefix of pipe */ + jmp_buf panic; /* Where to go if there's an error */ + int lineno; /* Line number where panic happened */ + char c; /* aas */ + /* Find out where we should put temporary files */ + if( (tmpdir = getenv( "TMPDIR" )) == (char *) NULL ) + tmpdir = getenv( "TMP" ); + if( tmpdir != (char *) NULL ) { + /* Use temporary directory if available */ + (void) strcpy( tmpfile, tmpdir ); + if((c=tmpfile[strlen(tmpfile)-1])!='/' && c!='\\') /* aas */ + (void) strcat( tmpfile, "/" ); + } else *tmpfile = '\0'; + /* Get a unique pipe file name */ + (void) strcat( tmpfile, uniquepipe() ); + if( (lineno = setjmp( panic )) != 0 ) { + /* An error has occurred, so clean up */ + int E = errno; + #ifdef DEMO + fprintf( stderr, "POPEN panic on line %d: %d\n", lineno, E ); + #endif DEMO + if( p != (FILE *) NULL ) (void) fclose( p ); + resetpipe( pipefd ); + errno = E; + return( (FILE *) NULL ); + } + if( strcmp( type, "w" ) == 0 ) { + /* for write style pipe, pclose handles program execution */ + if( (p = fopen( tmpfile, "w" )) != (FILE *) NULL ) { + pipefd = fileno( p ); + pipetype[ pipefd ] = WRITEIT; + pipename[ pipefd ] = strsave( tmpfile ); + prgname[ pipefd ] = strsave( prg ); + if( !pipename[ pipefd ] || !prgname[ pipefd ] ) longjmp( panic, __LINE__ ); + } + } else if( strcmp( type, "r" ) == 0 ) { + /* read pipe must create tmp file, set up stdout to point to the temp + * file, and run the program. note that if the pipe file cannot be + * opened, it'll return a condition indicating pipe failure, which is + * fine. + */ + if( (p = fopen( tmpfile, "w" )) != (FILE *) NULL ) { + int ostdout; + pipefd = fileno( p ); + pipetype[ pipefd ]= READIT; + if( (pipename[ pipefd ] = strsave( tmpfile )) == (char *) NULL ) + longjmp( panic, __LINE__ ); + /* Redirect stdin for the new command */ + ostdout = dup( fileno( stdout ) ); + if( dup2( fileno( stdout ), pipefd ) < 0 ) { + int E = errno; + (void) dup2( fileno( stdout ), ostdout ); + errno = E; + longjmp( panic, __LINE__ ); + } + if( run( prg ) != 0 ) longjmp( panic, __LINE__ ); + if( dup2( fileno( stdout ), ostdout ) < 0 ) longjmp( panic, __LINE__ ); + if( fclose( p ) < 0 ) longjmp( panic, __LINE__ ); + if( (p = fopen( tmpfile, "r" )) == (FILE *) NULL ) longjmp( panic, __LINE__ ); + } + } else { + /* screwy call or unsupported type */ + errno = EINVFNC; + longjmp( panic, __LINE__ ); + } + return( p ); + } + + /* close a pipe */ + + int + pclose( p ) + FILE *p; + { + int pipefd = -1; /* Fildes where pipe is opened */ + int ostdout; /* Where our stdout points now */ + int ostdin; /* Where our stdin points now */ + jmp_buf panic; /* Context to return to if error */ + int lineno; /* Line number where panic happened */ + if( (lineno = setjmp( panic )) != 0 ) { + /* An error has occurred, so clean up and return */ + int E = errno; + #ifdef DEMO + fprintf( stderr, "POPEN panic on line %d: %d\n", lineno, E ); + #endif DEMO + if( p != (FILE *) NULL ) (void) fclose( p ); + resetpipe( pipefd ); + errno = E; + return( -1 ); + } + pipefd = fileno( p ); + if( fclose( p ) < 0 ) longjmp( panic, __LINE__ ); + switch( pipetype[ pipefd ] ) { + case WRITEIT: + /* open the temp file again as read, redirect stdin from that + * file, run the program, then clean up. + */ + if( (p = fopen( pipename[ pipefd ],"r" )) == (FILE *) NULL ) + longjmp( panic, __LINE__ ); + ostdin = dup( fileno( stdin )); + if( dup2( fileno( stdin ), fileno( p ) ) < 0 ) longjmp( panic, __LINE__ ); + if( run( prgname[ pipefd ] ) != 0 ) longjmp( panic, __LINE__ ); + if( dup2( fileno( stdin ), ostdin ) < 0 ) longjmp( panic, __LINE__ ); + if( fclose( p ) < 0 ) longjmp( panic, __LINE__ ); + resetpipe( pipefd ); + break; + case READIT: + /* close the temp file and remove it */ + resetpipe( pipefd ); + break; + default: + errno = EINVFNC; + longjmp( panic, __LINE__ ); + /*NOTREACHED*/ + } + return( 0 ); + } + + #ifdef DEMO + int + main( argc, argv ) + int argc; + char **argv; + { + FILE *pipe; + char buf[ BUFSIZ ]; + int n; + *buf = '\0'; + for( n = 1; n < argc; ++n ) { + (void) strcat( buf, argv[ n ] ); + (void) strcat( buf, " " ); + } + if( (pipe = popen( buf, "r" )) != (FILE *) NULL ) { + while( fgets( buf, sizeof( buf ), pipe ) != (char *) NULL ) + (void) fputs( buf, stdout ); + if( pclose( pipe ) != 0 ) fprintf( stderr, "error closing pipe!\n" ); + } else fprintf( stderr, "it didn't work!\n" ); + exit( 0 ); + /*NOTREACHED*/ + } + #endif DEMO *** /dev/null --- getswitc.c Tue Mar 08 20:08:04 1988 *************** *** 0 **** --- 1,16 ---- + #include <stdio.h> + #include <dos.h> + + static char SW = 0; /* DOS switch character, either '-' or '/' */ + + int + getswitch() + { + if (SW == 0) { + /* get SW using dos call 0x37 */ + _AX = 0x3700; + geninterrupt(0x21); + SW = _DL; + } + return( SW & 0xFF ); + } *** /dev/null --- popentst.mak Sun Jul 15 15:43:08 1990 *************** *** 0 **** --- 1,18 ---- + popen.exe: popen.obj getswitch.obj + tcc -epopen popen.obj getswitch.obj + + popen.obj: popen.c popen.h + tcc -c -DDEMO popen.c + + getswitch.obj: getswitch.c + tcc -c getswitch.c + + clean: + rm -f *.obj core *.map + + clobber: clean + rm -f *.exe install + + install: + tcc -c popen.c + @echo What next?