Prechádzať zdrojové kódy

[Closes #32] display prettyfied and disable ncurses output if stdout is redirected to a file or command

isundil 9 rokov pred
rodič
commit
693db06ceb

+ 3 - 0
CMakeLists.txt

@@ -15,9 +15,12 @@ include_directories(include ${CURSES_INCLUDE_DIRS})
 add_executable(jsonstroll
     src/warning.cpp
     src/params.cpp
+
     src/curseOutput.cpp
     src/curseSimpleOutput.cpp
     src/curseSplitOutput.cpp
+    src/simpleOutput.cpp
+
     src/linearHistory.cpp
     src/outputFlag.cpp
     src/streamConsumer.cpp

+ 7 - 2
doc/jsonstroll.1

@@ -1,7 +1,7 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.47.3.
-.TH JSONSTROLL "1" "September 2016" "jsonstroll (jsonstroller suite) 1.0RC1 generated on Sep 10 2016" "User Commands"
+.TH JSONSTROLL "1" "October 2016" "jsonstroll (jsonstroller suite) 1.0RC1 generated on Oct 30 2016" "User Commands"
 .SH NAME
-jsonstroll \- manual page for jsonstroll (jsonstroller suite) 1.0RC1 generated on Sep 10 2016
+jsonstroll \- manual page for jsonstroll (jsonstroller suite) 1.0RC1 generated on Oct 30 2016
 .SH SYNOPSIS
 .B jsonstroll
 [\fI\,OPTIONS\/\fR]
@@ -18,6 +18,8 @@ jsonstroll \- manual page for jsonstroll (jsonstroller suite) 1.0RC1 generated o
 Read json input and print it using ncurse
 .PP
 if not INPUT nor FILENAME, use standard input
+.PP
+When output is redirected to a file or another command, output will be prettyfied unless \fB\-\-compress\fR
 .TP
 FILENAME
 read input from filename instead of stdin
@@ -37,6 +39,9 @@ do not sort objects by key
 \fB\-\-color\fR[=\fI\,MODE\/\fR]
 colorize output, MODE can be never or always (default when ommited)
 .TP
+\fB\-\-compress\fR
+if output is redirected, strip unnecessaries characters
+.TP
 \fB\-v\fR, \fB\-version\fR
 display version information
 .TP

+ 5 - 3
include/jsonPrimitive.hh

@@ -10,16 +10,18 @@
 
 class Null {}; //Null primitive
 
-class AJSonPrimitive
+class AJSonPrimitive: public JSonElement
 {
     public:
+        AJSonPrimitive(JSonElement *parent);
         virtual ~AJSonPrimitive();
+
         virtual std::string getTypeStr() const =0;
         bool sameType(const AJSonPrimitive *other) const;
 };
 
 template <typename T>
-class JSonPrimitive: public JSonElement, public AJSonPrimitive
+class JSonPrimitive: public AJSonPrimitive
 {
     public:
         JSonPrimitive(JSonContainer *parent, T const &v);
@@ -56,7 +58,7 @@ class JSonPrimitive: public JSonElement, public AJSonPrimitive
 };
 
 template<typename T>
-JSonPrimitive<T>::JSonPrimitive(JSonContainer *parent, T const &v): JSonElement(parent), value(v), stringValue(toString())
+JSonPrimitive<T>::JSonPrimitive(JSonContainer *parent, T const &v): AJSonPrimitive(parent), value(v), stringValue(toString())
 { }
 
 template<typename T>

+ 30 - 0
include/simpleOutput.hh

@@ -0,0 +1,30 @@
+#pragma once
+
+#include <iostream>
+
+class JSonElement;
+class Params;
+
+class SimpleOutput
+{
+    public:
+        static void display(std::ostream &out, const JSonElement *root, const Params &params);
+
+    private:
+        SimpleOutput(std::ostream &output, const Params &p);
+
+        std::string getIndent() const;
+
+        inline void writeObjectEntry(const JSonObjectEntry *);
+        inline void writePrimitive(const AJSonPrimitive *);
+        inline void writeContainer(const JSonContainer *);
+        inline void write(const JSonElement *);
+
+        void indent_inc(int i =1);
+
+    private:
+        std::ostream &out;
+        const Params &params;
+        unsigned short indent;
+};
+

+ 1 - 1
src/curseOutput.cpp

@@ -286,7 +286,7 @@ void CurseOutput::writeBottomLine(const std::wstring &buffer, short color) const
 
 void CurseOutput::init()
 {
-    if (!isatty(fileno(stdin)) || !isatty(fileno(stdout)))
+    if (!isatty(fileno(stdin)) || !isatty(fileno(stdout))) //TODO remove after v1.2 and #29
     {
         screen_fd = fopen("/dev/tty", "r+");
         setbuf(screen_fd, nullptr);

+ 3 - 0
src/jsonPrimitive.cpp

@@ -6,6 +6,9 @@
 
 #include "jsonPrimitive.hh"
 
+AJSonPrimitive::AJSonPrimitive(JSonElement *p): JSonElement(p)
+{}
+
 AJSonPrimitive::~AJSonPrimitive()
 {}
 

+ 66 - 49
src/main.cpp

@@ -5,12 +5,14 @@
 **/
 
 #include <iostream>
+#include <unistd.h>
 #include <locale.h>
-#include "streamConsumer.hh"
-#include "curseSplitOutput.hh"
 #include "curseSimpleOutput.hh"
-#include "params.hh"
+#include "curseSplitOutput.hh"
+#include "streamConsumer.hh"
 #include "jsonException.hh"
+#include "simpleOutput.hh"
+#include "params.hh"
 
 void displayException(const std::string &filename, const Params &params, const std::string &type, const JsonException &e)
 {
@@ -31,6 +33,33 @@ StreamConsumer *readFile(std::pair<std::string, std::basic_istream<char>*> input
     return stream;
 }
 
+StreamConsumer *readOneFile(std::pair<std::string, std::basic_istream<char>*> input, const Params &params, std::deque<Warning> &warns)
+{
+    StreamConsumer *stream;
+
+    try
+    {
+        stream = readFile(input, params);
+    }
+    catch (EofException &e)
+    {
+        std::cerr << params.getProgName() << ": " << input.first << " " << Warning::getType(e) << " ("  << e.what() << ") error while reading" << std::endl;
+        return nullptr;
+    }
+    catch (JsonException &e)
+    {
+        std::cerr << "Error: ";
+        displayException(input.first, params, Warning::getType(e), e);
+        return nullptr;
+    }
+    for (Warning w : stream->getMessages())
+    {
+        w.filename(input.first);
+        warns.push_back(Warning(w));
+    }
+    return stream;
+}
+
 void runDiff(const Params &params)
 {
     const IndexedDeque inputs = params.getInputs();
@@ -45,27 +74,12 @@ void runDiff(const Params &params)
         StreamConsumer *stream;
 
         inputNames.push_back(input.first);
-        try
-        {
-            stream = readFile(input, params);
-        }
-        catch (EofException &e)
+        stream = readOneFile(input, params, warns);
+        if (!stream)
         {
-            std::cerr << params.getProgName() << ": " << input.first << " " << Warning::getType(e) << " ("  << e.what() << ") error while reading" << std::endl;
-            delete stream;
-            stream = nullptr;
-        }
-        catch (JsonException &e)
-        {
-            std::cerr << "Error: ";
-            displayException(input.first, params, Warning::getType(e), e);
-            delete stream;
-            stream = nullptr;
-        }
-        for (Warning w : stream->getMessages())
-        {
-            w.filename(input.first);
-            warns.push_back(Warning(w));
+            for (StreamConsumer *s : streams)
+                delete s;
+            return;
         }
         roots.push_back(stream->getRoot());
         streams.insert(stream);
@@ -84,39 +98,37 @@ void runDiff(const Params &params)
     }
 }
 
+void runStdout(const Params &params)
+{
+    IndexedDeque inputs = params.getInputs();
+    std::deque<Warning> warns;
+
+    for (std::pair<std::string, std::basic_istream<char>*> input : inputs)
+    {
+        StreamConsumer *stream = readOneFile(input, params, warns);
+        if (!stream)
+            break;
+        SimpleOutput::display(std::cout, stream->getRoot(), params);
+        delete stream;
+    }
+    for (Warning w : warns)
+    {
+        std::cerr << "Warning: ";
+        displayException(w.filename(), params, w.getType(), w());
+    }
+}
+
 void run(const Params &params)
 {
     IndexedDeque inputs = params.getInputs();
     CurseSimpleOutput *out = new CurseSimpleOutput(params);
-    std::list<Warning> warns;
+    std::deque<Warning> warns;
 
     for (std::pair<std::string, std::basic_istream<char>*> input : inputs)
     {
-        StreamConsumer *stream;
-        try
-        {
-            stream = readFile(input, params);
-        }
-        catch (EofException &e)
-        {
-            delete out;
-            out = nullptr;
-            std::cerr << params.getProgName() << ": " << input.first << " " << Warning::getType(e) << " ("  << e.what() << ") error while reading" << std::endl;
+        StreamConsumer *stream = readOneFile(input, params, warns);
+        if (!stream)
             break;
-        }
-        catch (JsonException &e)
-        {
-            delete out;
-            out = nullptr;
-            std::cerr << "Error: ";
-            displayException(input.first, params, Warning::getType(e), e);
-            break;;
-        }
-        for (Warning w : stream->getMessages())
-        {
-            w.filename(input.first);
-            warns.push_back(Warning(w));
-        }
         out->run(stream->getRoot(), input.first);
         delete stream;
     }
@@ -153,7 +165,12 @@ int main(int ac, char **av)
         if (params->isDiff())
             runDiff(*params);
         else
-            run(*params);
+        {
+            if (isatty(fileno(stdout)))
+                run(*params);
+            else
+                runStdout(*params);
+        }
     }
     delete params;
     return 0;

+ 2 - 0
src/params.cpp

@@ -137,6 +137,7 @@ void Params::usage() const noexcept
     << "or: " << progName << " --diff [OPTIONS] FILENAME FILENAME [FILENAME]" << std::endl
     << "Read json input and print it using ncurse" << std::endl << std::endl
     << "if not INPUT nor FILENAME, use standard input" << std::endl << std::endl
+    << "When output is redirected to a file or another command, output will be prettyfied unless --compress" << std::endl << std::endl
 
     << "  FILENAME\t\tread input from filename instead of stdin" << std::endl
     << "  INPUT\t\t\tuse this as input instead of stdin" << std::endl
@@ -144,6 +145,7 @@ void Params::usage() const noexcept
     << "  --ascii\t\tignore unicode values" << std::endl
     << "  --keep-order\t\tdo not sort objects by key" << std::endl
     << "  --color[=MODE]\tcolorize output, MODE can be never or always (default when ommited)" << std::endl
+    << "  --compress\tif output is redirected, strip unnecessaries characters" << std::endl
     << "  -v, -version\t\tdisplay version information" << std::endl
     << "  -h, --helph\t\tshow this message and exit" << std::endl << std::endl
 

+ 70 - 0
src/simpleOutput.cpp

@@ -0,0 +1,70 @@
+#include "jsonObjectEntry.hh"
+#include "jsonContainer.hh"
+#include "jsonPrimitive.hh"
+#include "jsonElement.hh"
+
+#include "simpleOutput.hh"
+#include "config.h"
+#include "params.hh"
+
+SimpleOutput::SimpleOutput(std::ostream &output, const Params &p): out(output), params(p), indent(0)
+{ }
+
+void SimpleOutput::writeObjectEntry(const JSonObjectEntry *item)
+{
+    out << getIndent() << item->stringify() << ": ";
+
+    if (dynamic_cast<const JSonContainer *> (**item))
+    {
+        out << std::endl;
+        writeContainer((const JSonContainer*) **item);
+    }
+    else
+        out << (**item)->stringify() << std::endl;
+}
+
+void SimpleOutput::writePrimitive(const AJSonPrimitive *item)
+{
+    if (indent)
+        out << getIndent() << item->stringify() << std::endl;
+    else
+        out << item->stringify() << std::endl;
+}
+
+void SimpleOutput::writeContainer(const JSonContainer *item)
+{
+    std::string _indent = getIndent();
+    const char *brackets = item->stringify().c_str();
+
+    out << _indent << std::string(1, brackets[0]) << std::endl;
+
+    indent_inc();
+    for (JSonElement *i : *item)
+        write(i);
+    indent_inc(-1);
+
+    out << _indent << std::string(1, brackets[2]) << std::endl;
+}
+
+void SimpleOutput::write(const JSonElement *item)
+{
+    if (dynamic_cast<const JSonContainer *>(item))
+        writeContainer((const JSonContainer *) item);
+    else if (dynamic_cast<const JSonObjectEntry*> (item))
+        writeObjectEntry((const JSonObjectEntry *) item);
+    else
+        writePrimitive((const AJSonPrimitive *) item);
+}
+
+void SimpleOutput::indent_inc(int i)
+{ indent += i; }
+
+std::string SimpleOutput::getIndent() const
+{ return std::string(indent * INDENT_LEVEL, ' '); }
+
+void SimpleOutput::display(std::ostream &out, const JSonElement *root, const Params &p)
+{
+    SimpleOutput writer(out, p);
+    writer.write(root);
+}
+