Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -283,10 +283,12 @@ 4BB25E89139C388A00F574EA /* OFObject+Serialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB25E83139C388A00F574EA /* OFObject+Serialization.m */; }; 4BB25E8A139C388A00F574EA /* OFString+Serialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BB25E84139C388A00F574EA /* OFString+Serialization.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BB25E8B139C388A00F574EA /* OFString+Serialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB25E85139C388A00F574EA /* OFString+Serialization.m */; }; 4BB25E8C139C388A00F574EA /* OFXMLElement+Serialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BB25E86139C388A00F574EA /* OFXMLElement+Serialization.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BB25E8D139C388A00F574EA /* OFXMLElement+Serialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB25E87139C388A00F574EA /* OFXMLElement+Serialization.m */; }; + 4BB524C1143D1E4E0085FBCC /* OFProcess.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BB524BF143D1E4E0085FBCC /* OFProcess.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4BB524C2143D1E4E0085FBCC /* OFProcess.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB524C0143D1E4E0085FBCC /* OFProcess.m */; settings = {ATTRIBUTES = (Public, ); }; }; 4BD653C5143B8489006182F0 /* OFTCPSocket+SOCKS5.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD653C3143B8489006182F0 /* OFTCPSocket+SOCKS5.h */; }; 4BD653C6143B8489006182F0 /* OFTCPSocket+SOCKS5.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD653C4143B8489006182F0 /* OFTCPSocket+SOCKS5.m */; }; 4BD98C03133814220048DD5B /* objfw-defs.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD98C011338140B0048DD5B /* objfw-defs.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BDF37B51338055600F9A81A /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BDF37B41338055600F9A81A /* config.h */; }; 4BE852D213B7671200C00856 /* OFDoubleMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BE852CE13B7671200C00856 /* OFDoubleMatrix.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -635,10 +637,12 @@ 4BB25E85139C388A00F574EA /* OFString+Serialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OFString+Serialization.m"; path = "src/OFString+Serialization.m"; sourceTree = ""; }; 4BB25E86139C388A00F574EA /* OFXMLElement+Serialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFXMLElement+Serialization.h"; path = "src/OFXMLElement+Serialization.h"; sourceTree = ""; }; 4BB25E87139C388A00F574EA /* OFXMLElement+Serialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OFXMLElement+Serialization.m"; path = "src/OFXMLElement+Serialization.m"; sourceTree = ""; }; 4BB50DCF12F863C700C9393F /* of_asprintf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = of_asprintf.h; path = src/of_asprintf.h; sourceTree = SOURCE_ROOT; }; 4BB50DD012F863C700C9393F /* of_asprintf.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = of_asprintf.m; path = src/of_asprintf.m; sourceTree = SOURCE_ROOT; }; + 4BB524BF143D1E4E0085FBCC /* OFProcess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFProcess.h; path = src/OFProcess.h; sourceTree = ""; }; + 4BB524C0143D1E4E0085FBCC /* OFProcess.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFProcess.m; path = src/OFProcess.m; sourceTree = ""; }; 4BBA36C411406AB700CBA3AC /* atomic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = atomic.h; path = src/atomic.h; sourceTree = ""; }; 4BBA36C511406AB700CBA3AC /* macros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macros.h; path = src/macros.h; sourceTree = ""; }; 4BD653C3143B8489006182F0 /* OFTCPSocket+SOCKS5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFTCPSocket+SOCKS5.h"; path = "src/OFTCPSocket+SOCKS5.h"; sourceTree = ""; }; 4BD653C4143B8489006182F0 /* OFTCPSocket+SOCKS5.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OFTCPSocket+SOCKS5.m"; path = "src/OFTCPSocket+SOCKS5.m"; sourceTree = ""; }; 4BD86D801237A6C600ED9912 /* OFBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFBlock.h; path = src/OFBlock.h; sourceTree = SOURCE_ROOT; }; @@ -943,10 +947,12 @@ 4B6799781099E7C50041064A /* OFObject.m */, 4BB25E82139C388A00F574EA /* OFObject+Serialization.h */, 4BB25E83139C388A00F574EA /* OFObject+Serialization.m */, 4B6799791099E7C50041064A /* OFPlugin.h */, 4B67997A1099E7C50041064A /* OFPlugin.m */, + 4BB524BF143D1E4E0085FBCC /* OFProcess.h */, + 4BB524C0143D1E4E0085FBCC /* OFProcess.m */, 4B981CDE116F71DD00294DB7 /* OFSeekableStream.h */, 4B981CDF116F71DD00294DB7 /* OFSeekableStream.m */, 4B989C2E13771A3700109A30 /* OFSerialization.h */, 4B39844013D3A24600E6F825 /* OFSet.h */, 4B39844113D3A24600E6F825 /* OFSet.m */, @@ -1140,10 +1146,11 @@ 4B511B7C139C0A34003764A5 /* OFNull.h in Headers */, 4B3D23D01337FCB000DD29B8 /* OFNumber.h in Headers */, 4B3D23D11337FCB000DD29B8 /* OFObject.h in Headers */, 4BB25E88139C388A00F574EA /* OFObject+Serialization.h in Headers */, 4B3D23D21337FCB000DD29B8 /* OFPlugin.h in Headers */, + 4BB524C1143D1E4E0085FBCC /* OFProcess.h in Headers */, 4B3D23D31337FCB000DD29B8 /* OFSeekableStream.h in Headers */, 4B989C2F13771A3700109A30 /* OFSerialization.h in Headers */, 4B39844213D3A24600E6F825 /* OFSet.h in Headers */, 4B3D23D41337FCB000DD29B8 /* OFSHA1Hash.h in Headers */, 4B3D23D51337FCB000DD29B8 /* OFStream.h in Headers */, @@ -1427,10 +1434,11 @@ 4B511B7D139C0A34003764A5 /* OFNull.m in Sources */, 4B3D239E1337FC0D00DD29B8 /* OFNumber.m in Sources */, 4B3D239F1337FC0D00DD29B8 /* OFObject.m in Sources */, 4BB25E89139C388A00F574EA /* OFObject+Serialization.m in Sources */, 4B3D23A01337FC0D00DD29B8 /* OFPlugin.m in Sources */, + 4BB524C2143D1E4E0085FBCC /* OFProcess.m in Sources */, 4B3D23A11337FC0D00DD29B8 /* OFSeekableStream.m in Sources */, 4B39844313D3A24600E6F825 /* OFSet.m in Sources */, 4BA85BCF140ECCE800E91D51 /* OFSet_hashtable.m in Sources */, 4B3D23A21337FC0D00DD29B8 /* OFSHA1Hash.m in Sources */, 4B3D23A31337FC0D00DD29B8 /* OFStream.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -35,10 +35,11 @@ OFNull.m \ OFNumber.m \ OFObject.m \ OFObject+Serialization.m \ ${OFPLUGIN_M} \ + OFProcess.m \ OFSeekableStream.m \ OFSet.m \ OFSHA1Hash.m \ OFStream.m \ OFStreamObserver.m \ ADDED src/OFProcess.h Index: src/OFProcess.h ================================================================== --- src/OFProcess.h +++ src/OFProcess.h @@ -0,0 +1,41 @@ +#import "OFStream.h" + +/** + * \brief A class for stream-like communication with a newly created process. + */ +@interface OFProcess: OFStream +{ + int readPipe[2], writePipe[2]; + BOOL atEndOfStream; +} + +/** + * \brief Creates a new OFProcess with the specified program, program name and + * arguments and invokes the program. + * + * \param program The program to execute. If it does not start with a slash, the + * search path specified in PATH is used. + * \param programName The program name for the program to invoke (argv[0]). + * Usually, this is equal to program. + * \param arguments The arguments to pass to the program, or nil + * \return A new, autoreleased OFProcess. + */ ++ processWithProgram: (OFString*)program + programName: (OFString*)programName + arguments: (OFArray*)arguments; + +/** + * \brief Initializes an already allocated OFProcess with the specified + * program, program name and arguments and invokes the program. + * + * \param program The program to execute. If it does not start with a slash, the + * search path specified in PATH is used. + * \param programName The program name for the program to invoke (argv[0]). + * Usually, this is equal to program. + * \param arguments The arguments to pass to the program, or nil + * \return An initialized OFProcess. + */ +- initWithProgram: (OFString*)program + programName: (OFString*)programName + arguments: (OFArray*)arguments; +@end ADDED src/OFProcess.m Index: src/OFProcess.m ================================================================== --- src/OFProcess.m +++ src/OFProcess.m @@ -0,0 +1,137 @@ +#include "config.h" + +#include +#include + +#import "OFProcess.h" +#import "OFString.h" +#import "OFArray.h" + +#import "OFInitializationFailedException.h" +#import "OFReadFailedException.h" +#import "OFWriteFailedException.h" + +@implementation OFProcess ++ processWithProgram: (OFString*)program + programName: (OFString*)programName + arguments: (OFArray*)arguments +{ + return [[[self alloc] initWithProgram: program + programName: programName + arguments: arguments] autorelease]; +} + +- initWithProgram: (OFString*)program + programName: (OFString*)programName + arguments: (OFArray*)arguments +{ + self = [super init]; + + @try { + if (pipe(readPipe) != 0 || pipe(writePipe) != 0) + @throw [OFInitializationFailedException + exceptionWithClass: isa]; + + switch (fork()) { + case 0:; + OFString **cArray = [arguments cArray]; + size_t i, count = [arguments count]; + char **argv = alloca((count + 2) * sizeof(char*)); + + argv[0] = (char*)[programName cStringWithEncoding: + OF_STRING_ENCODING_NATIVE]; + + for (i = 0; i < count; i++) + argv[i + 1] = (char*)[cArray[i] + cStringWithEncoding: + OF_STRING_ENCODING_NATIVE]; + + argv[i + 1] = NULL; + + close(readPipe[0]); + close(writePipe[1]); + dup2(writePipe[0], 0); + dup2(readPipe[1], 1); + execvp([program cStringWithEncoding: + OF_STRING_ENCODING_NATIVE], argv); + + @throw [OFInitializationFailedException + exceptionWithClass: isa]; + case -1: + @throw [OFInitializationFailedException + exceptionWithClass: isa]; + default: + close(readPipe[1]); + close(writePipe[0]); + break; + } + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (BOOL)_isAtEndOfStream +{ + if (readPipe[0] == -1) + return YES; + + return atEndOfStream; +} + +- (size_t)_readNBytes: (size_t)length + intoBuffer: (void*)buffer +{ + ssize_t ret; + + if (readPipe[0] == -1 || atEndOfStream || + (ret = read(readPipe[0], buffer, length)) < 0) + @throw [OFReadFailedException exceptionWithClass: isa + stream: self + requestedLength: length]; + + if (ret == 0) + atEndOfStream = YES; + + return ret; +} + +- (void)_writeNBytes: (size_t)length + fromBuffer: (const void*)buffer +{ + if (writePipe[1] == -1 || atEndOfStream || + write(writePipe[1], buffer, length) < length) + @throw [OFWriteFailedException exceptionWithClass: isa + stream: self + requestedLength: length]; +} + +- (void)dealloc +{ + if (readPipe[0] != -1) + close(readPipe[0]); + if (writePipe[1] != -1) + close(writePipe[1]); + + [super dealloc]; +} + +/* + * FIXME: Add -[fileDescriptor]. The problem is that we have two FDs, which is + * not yet supported by OFStreamObserver. This has to be split into one + * FD for reading and one for writing. + */ + +- (void)close +{ + if (readPipe[0] != -1) + close(readPipe[0]); + if (writePipe[1] != -1) + close(writePipe[1]); + + readPipe[0] = -1; + writePipe[1] = -1; +} +@end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -44,10 +44,11 @@ #import "OFStream.h" #import "OFFile.h" #import "OFStreamSocket.h" #import "OFTCPSocket.h" +#import "OFProcess.h" #import "OFStreamObserver.h" #import "OFHTTPRequest.h" #import "OFHash.h"