Browse Source

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

isundil 9 years ago
parent
commit
693db06ceb
9 changed files with 187 additions and 55 deletions
  1. 3 0
      CMakeLists.txt
  2. 7 2
      doc/jsonstroll.1
  3. 5 3
      include/jsonPrimitive.hh
  4. 30 0
      include/simpleOutput.hh
  5. 1 1
      src/curseOutput.cpp
  6. 3 0
      src/jsonPrimitive.cpp
  7. 66 49
      src/main.cpp
  8. 2 0
      src/params.cpp
  9. 70 0
      src/simpleOutput.cpp

+ 3 - 0
CMakeLists.txt

@@ -15,9 +15,12 @@ include_directories(include ${CURSES_INCLUDE_DIRS})
 add_executable(jsonstroll
 add_executable(jsonstroll
     src/warning.cpp
     src/warning.cpp
     src/params.cpp
     src/params.cpp
+
     src/curseOutput.cpp
     src/curseOutput.cpp
     src/curseSimpleOutput.cpp
     src/curseSimpleOutput.cpp
     src/curseSplitOutput.cpp
     src/curseSplitOutput.cpp
+    src/simpleOutput.cpp
+
     src/linearHistory.cpp
     src/linearHistory.cpp
     src/outputFlag.cpp
     src/outputFlag.cpp
     src/streamConsumer.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.
 .\" 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
 .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
 .SH SYNOPSIS
 .B jsonstroll
 .B jsonstroll
 [\fI\,OPTIONS\/\fR]
 [\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
 Read json input and print it using ncurse
 .PP
 .PP
 if not INPUT nor FILENAME, use standard input
 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
 .TP
 FILENAME
 FILENAME
 read input from filename instead of stdin
 read input from filename instead of stdin
@@ -37,6 +39,9 @@ do not sort objects by key
 \fB\-\-color\fR[=\fI\,MODE\/\fR]
 \fB\-\-color\fR[=\fI\,MODE\/\fR]
 colorize output, MODE can be never or always (default when ommited)
 colorize output, MODE can be never or always (default when ommited)
 .TP
 .TP
+\fB\-\-compress\fR
+if output is redirected, strip unnecessaries characters
+.TP
 \fB\-v\fR, \fB\-version\fR
 \fB\-v\fR, \fB\-version\fR
 display version information
 display version information
 .TP
 .TP

+ 5 - 3
include/jsonPrimitive.hh

@@ -10,16 +10,18 @@
 
 
 class Null {}; //Null primitive
 class Null {}; //Null primitive
 
 
-class AJSonPrimitive
+class AJSonPrimitive: public JSonElement
 {
 {
     public:
     public:
+        AJSonPrimitive(JSonElement *parent);
         virtual ~AJSonPrimitive();
         virtual ~AJSonPrimitive();
+
         virtual std::string getTypeStr() const =0;
         virtual std::string getTypeStr() const =0;
         bool sameType(const AJSonPrimitive *other) const;
         bool sameType(const AJSonPrimitive *other) const;
 };
 };
 
 
 template <typename T>
 template <typename T>
-class JSonPrimitive: public JSonElement, public AJSonPrimitive
+class JSonPrimitive: public AJSonPrimitive
 {
 {
     public:
     public:
         JSonPrimitive(JSonContainer *parent, T const &v);
         JSonPrimitive(JSonContainer *parent, T const &v);
@@ -56,7 +58,7 @@ class JSonPrimitive: public JSonElement, public AJSonPrimitive
 };
 };
 
 
 template<typename T>
 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>
 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()
 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+");
         screen_fd = fopen("/dev/tty", "r+");
         setbuf(screen_fd, nullptr);
         setbuf(screen_fd, nullptr);

+ 3 - 0
src/jsonPrimitive.cpp

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

+ 66 - 49
src/main.cpp

@@ -5,12 +5,14 @@
 **/
 **/
 
 
 #include <iostream>
 #include <iostream>
+#include <unistd.h>
 #include <locale.h>
 #include <locale.h>
-#include "streamConsumer.hh"
-#include "curseSplitOutput.hh"
 #include "curseSimpleOutput.hh"
 #include "curseSimpleOutput.hh"
-#include "params.hh"
+#include "curseSplitOutput.hh"
+#include "streamConsumer.hh"
 #include "jsonException.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)
 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;
     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)
 void runDiff(const Params &params)
 {
 {
     const IndexedDeque inputs = params.getInputs();
     const IndexedDeque inputs = params.getInputs();
@@ -45,27 +74,12 @@ void runDiff(const Params &params)
         StreamConsumer *stream;
         StreamConsumer *stream;
 
 
         inputNames.push_back(input.first);
         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());
         roots.push_back(stream->getRoot());
         streams.insert(stream);
         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)
 void run(const Params &params)
 {
 {
     IndexedDeque inputs = params.getInputs();
     IndexedDeque inputs = params.getInputs();
     CurseSimpleOutput *out = new CurseSimpleOutput(params);
     CurseSimpleOutput *out = new CurseSimpleOutput(params);
-    std::list<Warning> warns;
+    std::deque<Warning> warns;
 
 
     for (std::pair<std::string, std::basic_istream<char>*> input : inputs)
     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;
             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);
         out->run(stream->getRoot(), input.first);
         delete stream;
         delete stream;
     }
     }
@@ -153,7 +165,12 @@ int main(int ac, char **av)
         if (params->isDiff())
         if (params->isDiff())
             runDiff(*params);
             runDiff(*params);
         else
         else
-            run(*params);
+        {
+            if (isatty(fileno(stdout)))
+                run(*params);
+            else
+                runStdout(*params);
+        }
     }
     }
     delete params;
     delete params;
     return 0;
     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
     << "or: " << progName << " --diff [OPTIONS] FILENAME FILENAME [FILENAME]" << std::endl
     << "Read json input and print it using ncurse" << std::endl << 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
     << "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
     << "  FILENAME\t\tread input from filename instead of stdin" << std::endl
     << "  INPUT\t\t\tuse this as input 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
     << "  --ascii\t\tignore unicode values" << std::endl
     << "  --keep-order\t\tdo not sort objects by key" << 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
     << "  --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
     << "  -v, -version\t\tdisplay version information" << std::endl
     << "  -h, --helph\t\tshow this message and exit" << std::endl << 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);
+}
+