PipeStr.st
changeset 1648 faa891d2c1b0
parent 1295 83f594f05c52
child 1662 ce26ca3d837c
equal deleted inserted replaced
1647:6ccae4646a4a 1648:faa891d2c1b0
     9  other person.  No title to or ownership of the software is
     9  other person.  No title to or ownership of the software is
    10  hereby transferred.
    10  hereby transferred.
    11 "
    11 "
    12 
    12 
    13 NonPositionableExternalStream subclass:#PipeStream
    13 NonPositionableExternalStream subclass:#PipeStream
    14 	instanceVariableNames:'commandString'
    14 	instanceVariableNames:'commandString pid exitStatus exitSema'
    15 	classVariableNames:'BrokenPipeSignal'
    15 	classVariableNames:'BrokenPipeSignal'
    16 	poolDictionaries:''
    16 	poolDictionaries:''
    17 	category:'Streams-External'
    17 	category:'Streams-External'
    18 !
    18 !
    19 
    19 
   128 #endif
   128 #endif
   129 
   129 
   130 %}
   130 %}
   131 ! !
   131 ! !
   132 
   132 
   133 !PipeStream class methodsFor:'documentation'!
   133 !PipeStream  class methodsFor:'documentation'!
   134 
   134 
   135 copyright
   135 copyright
   136 "
   136 "
   137  COPYRIGHT (c) 1989 by Claus Gittinger
   137  COPYRIGHT (c) 1989 by Claus Gittinger
   138 	      All Rights Reserved
   138 	      All Rights Reserved
   182     [author:]
   182     [author:]
   183         Claus Gittinger
   183         Claus Gittinger
   184 "
   184 "
   185 ! !
   185 ! !
   186 
   186 
   187 !PipeStream class methodsFor:'initialization'!
   187 !PipeStream  class methodsFor:'initialization'!
   188 
   188 
   189 initialize
   189 initialize
   190     "setup the signal"
   190     "setup the signal"
   191 
   191 
   192     BrokenPipeSignal isNil ifTrue:[
   192     BrokenPipeSignal isNil ifTrue:[
   194 	BrokenPipeSignal nameClass:self message:#brokenPipeSignal.
   194 	BrokenPipeSignal nameClass:self message:#brokenPipeSignal.
   195 	BrokenPipeSignal notifierString:'write on a pipe with no one to read'.
   195 	BrokenPipeSignal notifierString:'write on a pipe with no one to read'.
   196     ]
   196     ]
   197 ! !
   197 ! !
   198 
   198 
   199 !PipeStream class methodsFor:'instance creation'!
   199 !PipeStream  class methodsFor:'instance creation'!
   200 
   200 
   201 readingFrom:commandString
   201 readingFrom:commandString
   202     "create and return a new pipeStream which can read from the unix command
   202     "create and return a new pipeStream which can read from the unix command
   203      given by command."
   203      given by command."
   204 
   204 
   205     ^ (self basicNew) readingFrom:commandString
   205     ^ (self basicNew) readingFrom:commandString
   206 
   206 
   207     "PipeStream readingFrom:'ls -l'"
   207     "
       
   208         PipeStream readingFrom:'ls -l'
       
   209 
       
   210         |s|
       
   211         s := PipeStream readingFrom:'sh -c sleep\ 600'.
       
   212         (Delay forSeconds:2) wait.
       
   213         s shutDown
       
   214     "
       
   215 
       
   216     "Modified: 24.4.1996 / 09:09:25 / stefan"
   208 !
   217 !
   209 
   218 
   210 writingTo:commandString
   219 writingTo:commandString
   211     "create and return a new pipeStream which can write to the unix command
   220     "create and return a new pipeStream which can write to the unix command
   212      given by command."
   221      given by command."
   214     ^ (self basicNew) writingTo:commandString
   223     ^ (self basicNew) writingTo:commandString
   215 
   224 
   216     "PipeStream writingTo:'sort'"
   225     "PipeStream writingTo:'sort'"
   217 ! !
   226 ! !
   218 
   227 
   219 !PipeStream class methodsFor:'Signal constants'!
   228 !PipeStream  class methodsFor:'Signal constants'!
   220 
   229 
   221 brokenPipeSignal
   230 brokenPipeSignal
   222     "return the signal used to handle SIGPIPE unix-signals"
   231     "return the signal used to handle SIGPIPE unix-signals"
   223 
   232 
   224     ^ BrokenPipeSignal
   233     ^ BrokenPipeSignal
   228 
   237 
   229 commandString
   238 commandString
   230     "return the command string"
   239     "return the command string"
   231 
   240 
   232     ^ commandString
   241     ^ commandString
       
   242 !
       
   243 
       
   244 exitStatus
       
   245     "return exitStatus"
       
   246 
       
   247     ^ exitStatus
       
   248 
       
   249     "Created: 28.12.1995 / 14:54:41 / stefan"
       
   250 !
       
   251 
       
   252 pid
       
   253     "return pid"
       
   254 
       
   255     ^ pid
       
   256 
       
   257     "Created: 28.12.1995 / 14:54:30 / stefan"
   233 ! !
   258 ! !
   234 
   259 
   235 !PipeStream methodsFor:'instance release'!
   260 !PipeStream methodsFor:'instance release'!
   236 
   261 
   237 closeFile
   262 closeFile
   238     "low level close - redefined since we close a pipe here.
   263     "low level close
   239      This waits for the command to finish. 
   264      This waits for the command to finish. 
   240      Use shutDown for a fast (nonBlocking) close."
   265      Use shutDown for a fast (nonBlocking) close."
   241 
   266 
   242 %{  /* UNLIMITEDSTACK */
   267     filePointer notNil ifTrue:[
   243 #if !defined(transputer) && !defined(MSDOS_LIKE)
   268         super closeFile.
   244     OBJ fp;
   269         filePointer := nil.
   245 
   270         pid notNil ifTrue:[
   246     if ((fp = __INST(filePointer)) != nil) {
   271             exitSema wait.
   247 	__INST(filePointer) = nil;
   272         ].
   248 	/*
   273     ].
   249 	 * allow interrupt even when blocking here ...
       
   250 	 */
       
   251 	__BEGIN_INTERRUPTABLE__
       
   252 	pclose(__FILEVal(fp));
       
   253 	__END_INTERRUPTABLE__
       
   254     }
       
   255 #endif /* not transputer && not MSDOS_LIKE */
       
   256 %}
       
   257 !
   274 !
   258 
   275 
   259 closeFileDescriptor
   276 closeFileDescriptor
   260     "alternative very low level close 
   277     "alternative very low level close 
   261      This closes the underlying OS-fileDescriptor 
   278      This closes the underlying OS-fileDescriptor 
   268     OBJ fp;
   285     OBJ fp;
   269     FILE *f;
   286     FILE *f;
   270 
   287 
   271     if ((fp = __INST(filePointer)) != nil) {
   288     if ((fp = __INST(filePointer)) != nil) {
   272 	__INST(filePointer) = nil;
   289 	__INST(filePointer) = nil;
       
   290 	f = __FILEVal(fp);
   273 	__BEGIN_INTERRUPTABLE__
   291 	__BEGIN_INTERRUPTABLE__
   274 	f = __FILEVal(fp);
       
   275 	close(fileno(f));
   292 	close(fileno(f));
   276 	__END_INTERRUPTABLE__
   293 	__END_INTERRUPTABLE__
   277     }
   294     }
   278 #endif /* not transputer && not MSDOS_LIKE */
   295 #endif /* not transputer && not MSDOS_LIKE */
   279 %}
   296 %}
   284 
   301 
   285     self shutDown
   302     self shutDown
   286 !
   303 !
   287 
   304 
   288 shutDown
   305 shutDown
   289     "close the Stream, ignoring any broken-pipe errors"
   306     "close the Stream, ignoring any broken-pipe errors.
       
   307      Terminate the command"
   290 
   308 
   291     BrokenPipeSignal catch:[
   309     BrokenPipeSignal catch:[
   292 	Lobby unregister:self.
   310         |tpid|
   293 	self closeFileDescriptor
   311 
       
   312         Lobby unregister:self.
       
   313         self closeFileDescriptor.
       
   314         tpid := pid.                    "copy pid to avoid race"
       
   315         tpid notNil ifTrue:[
       
   316             "/
       
   317             "/ Terminate both the process and group, just in case the
       
   318             "/ operating system does not support process groups.
       
   319             "/
       
   320             OperatingSystem terminateProcess:tpid.
       
   321             OperatingSystem terminateProcessGroup:tpid.
       
   322             pid := nil.
       
   323         ].
   294     ]
   324     ]
       
   325 
       
   326     "Modified: 23.5.1996 / 09:15:41 / stefan"
   295 ! !
   327 ! !
   296 
   328 
   297 !PipeStream methodsFor:'private'!
   329 !PipeStream methodsFor:'private'!
   298 
   330 
   299 atEnd
   331 atEnd
   321 .
   353 .
   322     ^ super atEnd
   354     ^ super atEnd
   323 !
   355 !
   324 
   356 
   325 openPipeFor:aCommandString withMode:mode
   357 openPipeFor:aCommandString withMode:mode
   326     "open a pipe to the unix command in aCcommandString; 
   358     "open a pipe to the unix command in commandString; 
   327      mode may be 'r' or 'w'"
   359      mode may be 'r' or 'w'"
   328 
   360 
   329     |retVal|
   361     |blocked pipeFdArray execFdArray execFd myFd|
   330 
   362 
   331     filePointer notNil ifTrue:[
   363     filePointer notNil ifTrue:[
   332 	"the pipe was already open ...
   364         "the pipe was already open ...
   333 	 this should (can) not happen."
   365          this should (can) not happen."
   334 	^ self errorOpen
   366         ^ self errorOpen
   335     ].
   367     ].
   336 
   368     lastErrorNumber := nil.
   337 %{  /* STACK: 32000 */
   369     exitStatus := nil.
   338 #if !defined(transputer) && !defined(MSDOS_LIKE)
   370     exitSema := Semaphore new.
   339     FILE *f;
   371 
   340     OBJ fp;
   372     pipeFdArray := OperatingSystem makePipe.
   341 
   373     pipeFdArray isNil ifTrue:[
   342     __INST(lastErrorNumber) = nil;
   374         lastErrorNumber := OperatingSystem currentErrorNumber.
   343 
   375         ^ self openError
   344     if (__isString(aCommandString) && __isString(mode)) {
   376     ].
   345 	__BEGIN_INTERRUPTABLE__
   377 
   346 	do {
   378     mode = 'r' ifTrue:[
   347 # ifdef LINUX
   379         execFd := pipeFdArray at:2.
   348 	    /* LINUX returns a non-NULL f even when interrupted */
   380         execFdArray := Array with:0 with:execFd with:2.
   349 	    errno = 0;
   381         myFd := pipeFdArray at:1.
   350 	    f = (FILE *)popen((char *) __stringVal(aCommandString),
   382     ] ifFalse:[
   351 			      (char *) __stringVal(mode));
   383         execFd := pipeFdArray at:1.
   352 	    if (errno == EINTR)
   384         execFdArray := Array with:execFd with:1 with:2.
   353 		f = NULL;
   385         myFd := pipeFdArray at:2.
   354 # else
   386     ].
   355 	    f = (FILE *)popen((char *) __stringVal(aCommandString),
   387 
   356 			      (char *) __stringVal(mode));
   388     blocked := OperatingSystem blockInterrupts.
   357 # endif /* LINUX */
   389     pid := OperatingSystem exec:'/bin/sh'
   358 	} while ((f == NULL) && (errno == EINTR));
   390                            withArguments:(Array with:'sh' with:'-c' with:aCommandString)
   359 	__END_INTERRUPTABLE__
   391                            fileDescriptors:execFdArray
   360 
   392                            closeDescriptors:(Array with:myFd)
   361 	if (f == NULL) {
   393                            fork:true
   362 	    __INST(lastErrorNumber) = __MKSMALLINT(errno);
   394                            newPgrp:true.
   363 	} else {
   395 
   364 	    clearerr(f);
   396     OperatingSystem closeFd:execFd.
   365 	    __INST(filePointer) = fp = __MKOBJ(f); __STORE(self, fp);
   397     pid > 0 ifTrue:[
   366 	    retVal = self;
   398         self fileDescriptor:myFd withMode:mode.
   367 	}
   399         Processor monitorPid:pid action:[ :stat |
   368     }
   400             exitStatus := stat.
   369 #endif /* not transputer && not MSDOS_LIKE */
   401             pid := nil.
   370 %}.
   402             exitSema signal.
   371     retVal notNil ifTrue:[
   403         ].
   372 	commandString := aCommandString.
   404     ] ifFalse:[
   373 	buffered := true.
   405         pid := nil.
   374 	hitEOF := false.
   406         lastErrorNumber := OperatingSystem currentErrorNumber.
   375 	binary := false.
   407         OperatingSystem closeFd:myFd.
   376 	Lobby register:self
   408     ].
   377     ].
   409     blocked ifFalse:[
   378     lastErrorNumber notNil ifTrue:[
   410         OperatingSystem unblockInterrupts
   379 	"
   411     ].
   380 	 the pipe open failed for some reason ...
   412 
   381 	 ... this may be either due to an invalid command string,
   413     lastErrorNumber isNil ifTrue:[
   382 	 or due to the system running out of memory (when forking
   414         commandString := aCommandString.
   383 	 the unix process)
   415         buffered := true.
   384 	"
   416         hitEOF := false.
   385 	^ self openError
   417         binary := false.
   386     ].
   418         Lobby register:self
   387     ^ retVal
   419     ] ifFalse:[
       
   420         "
       
   421          the pipe open failed for some reason ...
       
   422          ... this may be either due to an invalid command string,
       
   423          or due to the system running out of memory (when forking
       
   424          the unix process)
       
   425         "
       
   426         ^ self openError
       
   427     ].
       
   428     ^ self
       
   429 
       
   430     "Modified: 23.4.1996 / 17:05:59 / stefan"
   388 !
   431 !
   389 
   432 
   390 readingFrom:command
   433 readingFrom:command
   391     "setup the receiver to read from command"
   434     "setup the receiver to read from command"
   392 
   435 
   399 
   442 
   400     mode := #writeonly. didWrite := true.
   443     mode := #writeonly. didWrite := true.
   401     ^ self openPipeFor:command withMode:'w'
   444     ^ self openPipeFor:command withMode:'w'
   402 ! !
   445 ! !
   403 
   446 
   404 !PipeStream class methodsFor:'documentation'!
   447 !PipeStream  class methodsFor:'documentation'!
   405 
   448 
   406 version
   449 version
   407     ^ '$Header: /cvs/stx/stx/libbasic/Attic/PipeStr.st,v 1.43 1996-04-25 17:01:50 cg Exp $'
   450     ^ '$Header: /cvs/stx/stx/libbasic/Attic/PipeStr.st,v 1.44 1996-09-09 10:30:27 stefan Exp $'
   408 ! !
   451 ! !
   409 PipeStream initialize!
   452 PipeStream initialize!