Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to redirect STDOUT to a NSTextView?

Could anybody show me how to redirect the Stdout to a NSTextView?

and whether the info print by NSLog belong to the std?

Thanks

like image 393
iLeoDo Avatar asked May 06 '13 02:05

iLeoDo


2 Answers

The code below uses dup2 to plug stdout onto the write-end of an NSPipe object. The read-end is observed with a GCD dispatch source, that reads data from the pipe and appends it to a textview.

NSPipe* pipe = [NSPipe pipe];
NSFileHandle* pipeReadHandle = [pipe fileHandleForReading];
dup2([[pipe fileHandleForWriting] fileDescriptor], fileno(stdout));
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [pipeReadHandle fileDescriptor], 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
dispatch_source_set_event_handler(source, ^{
    void* data = malloc(4096);
    ssize_t readResult = 0;
    do
    {
        errno = 0;
        readResult = read([pipeReadHandle fileDescriptor], data, 4096);
    } while (readResult == -1 && errno == EINTR);
    if (readResult > 0)
    {
        //AppKit UI should only be updated from the main thread
        dispatch_async(dispatch_get_main_queue(),^{
            NSString* stdOutString = [[NSString alloc] initWithBytesNoCopy:data length:readResult encoding:NSUTF8StringEncoding freeWhenDone:YES];
            NSAttributedString* stdOutAttributedString = [[NSAttributedString alloc] initWithString:stdOutString];
            [self.logView.textStorage appendAttributedString:stdOutAttributedString];
        });
    }
    else{free(data);}
});
dispatch_resume(source);

NSLog(@"...") does not output to stdout though - It prints to stderr. If you want to redirect that into your textview, change

dup2([[pipe fileHandleForWriting] fileDescriptor], fileno(stdout));

to

dup2([[pipe fileHandleForWriting] fileDescriptor], fileno(stderr));
like image 122
Thomas Zoechling Avatar answered Oct 21 '22 16:10

Thomas Zoechling


I know the question was about objective-c but I thought I would post a swift answer incase that helps someone else.

let pipefd = UnsafeMutablePointer<Int32>.allocate(capacity: 8)
pipe(pipefd)

dup2(pipefd[1], fileno(stdout))

// Print something here

let buf = UnsafeMutableRawPointer.allocate(byteCount: 1024, alignment: 0)
read(pipefd[0], buf, 100)

close(pipefd[1])

let output = self.ptrToString(pointer: buf)
if output != "" {
    // Do something with output
}

buf.deallocate()
pipefd.deallocate()

And here is the function I use to convert the pointer to a string:

func ptrToString (pointer buf: UnsafeMutableRawPointer) -> String {
    let filteredArray = Array(UnsafeBufferPointer(start: buf.assumingMemoryBound(to: UInt8.self), count: 1024)).filter { item in
        return item != 0
    }

    return filteredArray
        .map { String(UnicodeScalar(UInt8($0))) }
        .joined()
        .components(separatedBy: "\n")[0]
}

Works on swift 4

like image 42
zoecarver Avatar answered Oct 21 '22 15:10

zoecarver