Warning, file /multimedia/kid3/src/app/cli/standardiohandler.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /**
0002  * \file standardiohandler.cpp
0003  * CLI I/O Handler for standard I/O.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 10 Aug 2013
0008  *
0009  * Copyright (C) 2013-2018  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #include "standardiohandler.h"
0028 #include "cliconfig.h"
0029 #include <QThread>
0030 #include <QIODevice>
0031 #ifdef Q_OS_WIN32
0032 #include <windows.h>
0033 #else
0034 #include <unistd.h>
0035 #endif
0036 #ifdef HAVE_READLINE
0037 #include <cstdio>
0038 #include <readline/readline.h>
0039 #include <readline/history.h>
0040 #if RL_READLINE_VERSION < 0x0600
0041 #include <cstdlib>
0042 #endif
0043 #else
0044 #include <QTextStream>
0045 #endif
0046 
0047 /**
0048  * Constructor.
0049  * @param prompt command line prompt
0050  */
0051 StandardIOHandler::StandardIOHandler(const char* prompt)
0052   : m_prompt(prompt), m_conInThread(nullptr),
0053     m_cout(stdout, QIODevice::WriteOnly), m_cerr(stderr, QIODevice::WriteOnly)
0054 {
0055 #ifdef Q_OS_WIN32
0056   DWORD mode;
0057   m_consoleMode = GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &mode);
0058 #else
0059   m_consoleMode = isatty(fileno(stdout));
0060 #endif
0061 }
0062 
0063 /**
0064  * Restore terminal on cleanup.
0065  */
0066 void StandardIOHandler::cleanup()
0067 {
0068 #ifdef HAVE_READLINE
0069   ::rl_cleanup_after_signal();
0070 #endif
0071 }
0072 
0073 /**
0074  * Start processing.
0075  */
0076 void StandardIOHandler::start()
0077 {
0078   m_conInThread = new QThread;
0079   m_conInThread->setObjectName(QLatin1String("conInThread"));
0080 
0081   connect(m_conInThread, &QThread::started, this, &StandardIOHandler::blockingReadLine);
0082   connect(m_conInThread, &QThread::finished, this, &QObject::deleteLater);
0083   connect(m_conInThread, &QThread::finished, m_conInThread, &QObject::deleteLater);
0084   moveToThread(m_conInThread);
0085 
0086   m_conInThread->start();
0087 }
0088 
0089 /**
0090  * Stop processing.
0091  */
0092 void StandardIOHandler::stop()
0093 {
0094   QMetaObject::invokeMethod(m_conInThread, "quit",
0095                              Qt::QueuedConnection);
0096 }
0097 
0098 /**
0099  * Read the next line.
0100  * This method will asynchronously invoke reading of a line from standard
0101  * input in the read thread.
0102  * When the line is ready, lineReady() is emitted.
0103  */
0104 void StandardIOHandler::readLine()
0105 {
0106   QMetaObject::invokeMethod(this, "blockingReadLine",
0107                              Qt::QueuedConnection);
0108 }
0109 
0110 /**
0111  * Read the next line.
0112  */
0113 void StandardIOHandler::blockingReadLine()
0114 {
0115   if (!m_consoleMode) {
0116     QTextStream stdIn(stdin, QIODevice::ReadOnly);
0117     QString line = stdIn.readLine();
0118     emit lineReady(line);
0119     return;
0120   }
0121 #ifdef HAVE_READLINE
0122   char* lineRead = ::readline(m_prompt);
0123   if (!lineRead) {
0124     // EOF
0125     emit lineReady(QString());
0126     return;
0127   }
0128   if (*lineRead) {
0129     ::add_history(lineRead);
0130   }
0131   QString line = QString::fromLocal8Bit(lineRead);
0132 #if RL_READLINE_VERSION >= 0x0600
0133   ::rl_free(lineRead);
0134 #else
0135   ::free(lineRead);
0136 #endif
0137 #elif defined Q_OS_WIN32
0138   WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE),
0139                 m_prompt, qstrlen(m_prompt), 0, 0);
0140   const int numCharsInBuf = 512;
0141   wchar_t buf[numCharsInBuf];
0142   DWORD numCharsRead;
0143   QString line;
0144   do {
0145     ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE),
0146         buf, numCharsInBuf, &numCharsRead, 0);
0147     line += QString::fromWCharArray(buf, numCharsRead);
0148   } while (numCharsRead > 0 && line[line.length() - 1] != QLatin1Char('\n'));
0149   while (line.length() > 0 &&
0150          (line[line.length() - 1] == QLatin1Char('\r') ||
0151           line[line.length() - 1] == QLatin1Char('\n'))) {
0152     line.truncate(line.length() - 1);
0153   }
0154 #else
0155   QTextStream stdOut(stdout, QIODevice::WriteOnly);
0156   stdOut << m_prompt;
0157   stdOut.flush();
0158   QTextStream stdIn(stdin, QIODevice::ReadOnly);
0159   QString line = stdIn.readLine();
0160 #endif
0161   emit lineReady(line);
0162 }
0163 
0164 /**
0165  * Write a line to standard output.
0166  * @param line line to write
0167  */
0168 void StandardIOHandler::writeLine(const QString& line)
0169 {
0170 #ifdef Q_OS_WIN32
0171   if (m_consoleMode) {
0172     QString str = line + QLatin1Char('\n');
0173     WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
0174         str.utf16(), str.size(), 0, 0);
0175     return;
0176   }
0177 #endif
0178   m_cout << line;
0179   m_cout << QLatin1Char('\n');
0180   m_cout.flush();
0181 }
0182 
0183 /**
0184  * Write a line to standard error.
0185  * @param line line to write
0186  */
0187 void StandardIOHandler::writeErrorLine(const QString& line)
0188 {
0189 #ifdef Q_OS_WIN32
0190   if (m_consoleMode) {
0191     QString str = line + QLatin1Char('\n');
0192     WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE),
0193         str.utf16(), str.size(), 0, 0);
0194     return;
0195   }
0196 #endif
0197   m_cerr << line;
0198   m_cerr << QLatin1Char('\n');
0199   m_cerr.flush();
0200 }
0201 
0202 /**
0203  * Flush the standard output.
0204  */
0205 void StandardIOHandler::flushStandardOutput()
0206 {
0207 #ifdef Q_OS_WIN32
0208   if (m_consoleMode) {
0209     return;
0210   }
0211 #endif
0212   m_cout.flush();
0213 }