From 45f929b4949cd7a4a185e3efe6ef3c80f4bc45f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Tue, 19 Jun 2007 12:38:29 +0000 Subject: [PATCH] * added BCommandPipe class (currently private API) kindly provided by Ramshankar * adopted to Haiku coding style git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21463 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/shared/CommandPipe.h | 88 ++++++++ src/kits/shared/CommandPipe.cpp | 324 +++++++++++++++++++++++++++ 2 files changed, 412 insertions(+) create mode 100644 headers/private/shared/CommandPipe.h create mode 100644 src/kits/shared/CommandPipe.cpp diff --git a/headers/private/shared/CommandPipe.h b/headers/private/shared/CommandPipe.h new file mode 100644 index 0000000000..3be0ac8f83 --- /dev/null +++ b/headers/private/shared/CommandPipe.h @@ -0,0 +1,88 @@ +/* + * Copyright 2007 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ramshankar, v.ramshankar@gmail.com + */ +#ifndef _COMMAND_PIPE_H +#define _COMMAND_PIPE_H + + +#include + +#include +#include + + +class BMessage; +class BMessenger; +class BString; + +namespace BPrivate { + +class BCommandPipe { + public: + BCommandPipe(); + virtual ~BCommandPipe(); + + status_t AddArg(const char* argv); + void PrintToStream() const; + + // FlushArgs() deletes the commands while Close() explicity closes all + // pending pipe-ends + // Note: Internally FlushArgs() calls Close() + void FlushArgs(); + void Close(); + + // If you use these, you must explicitly call "close" for the parameters + // (outdes/errdes) when you are done with them! + thread_id Pipe(int* outdes, int* errdes) const; + thread_id Pipe(int* outdes) const; + thread_id PipeAll(int* outAndErrDes) const; + + // If you use these, you need NOT call "fclose" for the parameters + // (out/err) when you are done with them, also you need not do any + // allocation for these FILE* pointers, just use FILE* out = NULL + // and pass &out and so on... + thread_id PipeInto(FILE** _out, FILE** _err); + thread_id PipeInto(FILE** _outAndErr); + + // Run() is a synchronous call, and waits till the command has finished + // executing RunAsync() is an asynchronous call that returns immediately + // after launching the command Neither of these bother about redirecting + // pipes for you to use + void Run(); + void RunAsync(); + + // This function reads line-by-line from "file", cancels its reading + // when "*cancel" is true. It reports each line it has read to "target" + // using the supplied "_message" and string field name. "cancel" can be + // NULL + BString ReadLines(FILE* file, bool* cancel, + BMessenger& target, const BMessage& message, + const BString& stringFieldName); + + // You need NOT free/delete the return array of strings + const char** Argv(int32& _argc) const; + + // Stardard append operators, if you use pointers to a BCommandPipe, + // you must use *pipe << "command"; and not pipe << "command" (as it + // will not compile that way) + BCommandPipe& operator<<(const char *arg); + BCommandPipe& operator<<(const BString& arg); + BCommandPipe& operator<<(const BCommandPipe& arg); + + protected: + BList fArgList; + int fOutDes[2]; + int fErrDes[2]; + bool fOutDesOpen; + bool fErrDesOpen; +}; + +} // namespace BPrivate + +using BPrivate::BCommandPipe; + +#endif // _COMMAND_PIPE_H diff --git a/src/kits/shared/CommandPipe.cpp b/src/kits/shared/CommandPipe.cpp new file mode 100644 index 0000000000..414c39e6d3 --- /dev/null +++ b/src/kits/shared/CommandPipe.cpp @@ -0,0 +1,324 @@ +/* + * Copyright 2007 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ramshankar, v.ramshankar@gmail.com + */ + +//! BCommandPipe class to handle reading shell output +// (stdout/stderr) of other programs into memory. +#include "CommandPipe.h" + +#include +#include + +#include +#include +#include +#include + + +BCommandPipe::BCommandPipe() + : fOutDesOpen(false) + , fErrDesOpen(false) +{ +} + + +BCommandPipe::~BCommandPipe() +{ + FlushArgs(); +} + + +status_t +BCommandPipe::AddArg(const char* arg) +{ + return (fArgList.AddItem(reinterpret_cast(strdup(arg))) == true ? + B_OK : B_ERROR); +} + + +void +BCommandPipe::PrintToStream() const +{ + for (int32 i = 0L; i < fArgList.CountItems(); i++) + printf("%s ", (char*)fArgList.ItemAtFast(i)); + + printf("\n"); +} + + +void +BCommandPipe::FlushArgs() +{ + // Delete all arguments from the list + for(int32 i = 0; i < fArgList.CountItems(); i++) + free(fArgList.RemoveItem(0L)); + + fArgList.MakeEmpty(); + Close(); +} + + +void +BCommandPipe::Close() +{ + if (fErrDesOpen) { + close(fErrDes[0]); + fErrDesOpen = false; + } + + if (fOutDesOpen) { + close(fOutDes[0]); + fOutDesOpen = false; + } +} + + +const char** +BCommandPipe::Argv(int32& _argc) const +{ + // *** Warning *** Freeing is left to caller!! Indicated in Header + int32 argc = fArgList.CountItems(); + const char **argv = (const char**)malloc((argc + 1) * sizeof(char*)); + for (int32 i = 0; i < argc; i++) + argv[i] = (const char*)fArgList.ItemAtFast(i); + + argv[argc] = NULL; + _argc = argc; + return argv; +} + + +// #pragma mark - + + +thread_id +BCommandPipe::PipeAll(int* outAndErrDes) const +{ + // This function pipes both stdout and stderr to the same filedescriptor + // (outdes) + int oldstdout; + int oldstderr; + pipe(outAndErrDes); + oldstdout = dup(STDOUT_FILENO); + oldstderr = dup(STDERR_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + dup2(outAndErrDes[1], STDOUT_FILENO); + dup2(outAndErrDes[1], STDERR_FILENO); + + // Construct the argv vector + int32 argc = fArgList.CountItems(); + const char **argv = (const char**)malloc((argc + 1) * sizeof(char*)); + for (int32 i = 0; i < argc; i++) + argv[i] = (const char*)fArgList.ItemAtFast(i); + + argv[argc] = NULL; + + // Load the app image... and pass the args + thread_id appThread = load_image((int)argc, argv, const_cast< + const char**>(environ)); + + dup2(oldstdout, STDOUT_FILENO); + dup2(oldstderr, STDERR_FILENO); + close(oldstdout); + close(oldstderr); + + delete[] argv; + + return appThread; +} + + +thread_id +BCommandPipe::Pipe(int* outdes, int* errdes) const +{ + int oldstdout; + int oldstderr; + pipe(outdes); + pipe(errdes); + oldstdout = dup(STDOUT_FILENO); + oldstderr = dup(STDERR_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + dup2(outdes[1], STDOUT_FILENO); + dup2(errdes[1], STDERR_FILENO); + + // Construct the argv vector + int32 argc = fArgList.CountItems(); + const char **argv = (const char**)malloc((argc + 1) * sizeof(char*)); + for (int32 i = 0; i < argc; i++) + argv[i] = (const char*)fArgList.ItemAtFast(i); + + argv[argc] = NULL; + + // Load the app image... and pass the args + thread_id appThread = load_image((int)argc, argv, const_cast< + const char**>(environ)); + + dup2(oldstdout, STDOUT_FILENO); + dup2(oldstderr, STDERR_FILENO); + close(oldstdout); + close(oldstderr); + + delete[] argv; + + return appThread; +} + + +thread_id +BCommandPipe::Pipe(int* outdes) const +{ + // Redirects only output (stdout) to caller, stderr is closed + int errdes[2]; + thread_id tid = Pipe(outdes, errdes); + close(errdes[0]); + close(errdes[1]); + return tid; +} + + +thread_id +BCommandPipe::PipeInto(FILE** _out, FILE** _err) +{ + Close(); + thread_id tid = Pipe(fOutDes, fErrDes); + + resume_thread(tid); + + close(fErrDes[1]); + close(fOutDes[1]); + + fOutDesOpen = true; + fErrDesOpen = true; + + *_out = fdopen(fOutDes[0], "r"); + *_err = fdopen(fErrDes[0], "r"); + + return tid; +} + + +thread_id +BCommandPipe::PipeInto(FILE** _outAndErr) +{ + Close(); + thread_id tid = PipeAll(fOutDes); + + if (tid == B_ERROR || tid == B_NO_MEMORY) + return tid; + + resume_thread(tid); + + close(fOutDes[1]); + fOutDesOpen = true; + + *_outAndErr = fdopen(fOutDes[0], "r"); + return tid; +} + + +// #pragma mark - + + +void +BCommandPipe::Run() +{ + // Runs the command without bothering to redirect streams, this is similar + // to system() but uses pipes and wait_for_thread.... Synchronous. + int outdes[2], errdes[2]; + status_t exitCode; + wait_for_thread(Pipe(outdes, errdes), &exitCode); + + close(outdes[0]); + close(errdes[0]); + close(outdes[1]); + close(errdes[1]); +} + + +void +BCommandPipe::RunAsync() +{ + // Runs the command without bothering to redirect streams, this is similar + // to system() but uses pipes.... Asynchronous. + Close(); + FILE* f = NULL; + PipeInto(&f); + fclose(f); +} + + +// #pragma mark - + + +BString +BCommandPipe::ReadLines(FILE* file, bool* cancel, BMessenger& target, + const BMessage& message, const BString& stringFieldName) +{ + // Reads output of file, line by line. The entire output is returned + // and as each line is being read "target" (if any) is informed, + // with "message" i.e. AddString (stringFieldName, ) + + // "cancel" cancels the reading process, when it becomes true (unless its + // waiting on fgetc()) and I don't know how to cancel the waiting fgetc() + // call. + + BString result; + BString line; + BMessage updateMsg(message); + + while (!feof(file)) { + if (cancel != NULL && *cancel == true) + break; + + unsigned char c = fgetc(file); + + if (c != 255) { + line << (char)c; + result << (char)c; + } + + if (c == '\n') { + updateMsg.RemoveName(stringFieldName.String()); + updateMsg.AddString(stringFieldName.String(), line); + target.SendMessage(&updateMsg); + line = ""; + } + } + + return result; +} + + +BCommandPipe& +BCommandPipe::operator<<(const char* _arg) +{ + AddArg(_arg); + return *this; +} + + +BCommandPipe& +BCommandPipe::operator<<(const BString& _arg) +{ + AddArg(_arg.String()); + return *this; +} + + +BCommandPipe& +BCommandPipe::operator<<(const BCommandPipe& _arg) +{ + int32 argc; + const char** argv = _arg.Argv(argc); + for (int32 i = 0; i < argc; i++) + AddArg(argv[i]); + + return *this; +} +