1 /***************************************************************************
2 * Copyright (C) 2004-2018 by Thomas Fischer <fischer@unix-ag.uni-kl.de> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 ***************************************************************************/
17
18 #include "bibutils.h"
19
20 #include <QProcess>
21 #include <QBuffer>
22 #include <QByteArray>
23 #include <QStandardPaths>
24
25 #include "logging_io.h"
26
27 class BibUtils::Private
28 {
29 public:
30 BibUtils::Format format;
31
Private(BibUtils * parent)32 Private(BibUtils *parent)
33 : format(BibUtils::MODS)
34 {
35 Q_UNUSED(parent)
36 }
37 };
38
BibUtils()39 BibUtils::BibUtils()
40 : d(new BibUtils::Private(this))
41 {
42 /// nothing
43 }
44
~BibUtils()45 BibUtils::~BibUtils()
46 {
47 delete d;
48 }
49
setFormat(const BibUtils::Format format)50 void BibUtils::setFormat(const BibUtils::Format format) {
51 d->format = format;
52 }
53
format() const54 BibUtils::Format BibUtils::format() const {
55 return d->format;
56 }
57
available()58 bool BibUtils::available() {
59 enum State {untested, avail, unavail};
60 static State state = untested;
61 /// Perform test only once, later rely on statically stored result
62 if (state == untested) {
63 /// Test a number of known BibUtils programs
64 static const QStringList programs {QStringLiteral("bib2xml"), QStringLiteral("isi2xml"), QStringLiteral("ris2xml"), QStringLiteral("end2xml")};
65 state = avail;
66 for (const QString &program : programs) {
67 const QString fullPath = QStandardPaths::findExecutable(program);
68 if (fullPath.isEmpty()) {
69 state = unavail; ///< missing a single program is reason to assume that BibUtils is not correctly installed
70 break;
71 }
72 }
73 if (state == avail)
74 qCDebug(LOG_KBIBTEX_IO) << "BibUtils found, using it to import/export certain types of bibliographies";
75 else if (state == unavail)
76 qCWarning(LOG_KBIBTEX_IO) << "No or only an incomplete installation of BibUtils found";
77 }
78 return state == avail;
79 }
80
convert(QIODevice & source,const BibUtils::Format sourceFormat,QIODevice & destination,const BibUtils::Format destinationFormat) const81 bool BibUtils::convert(QIODevice &source, const BibUtils::Format sourceFormat, QIODevice &destination, const BibUtils::Format destinationFormat) const {
82 /// To proceed, either the source format or the destination format
83 /// has to be MODS, otherwise ...
84 if (sourceFormat != MODS && destinationFormat != MODS) {
85 /// Add indirection: convert source format to MODS,
86 /// then convert MODS data to destination format
87
88 /// Intermediate buffer to hold MODS data
89 QBuffer buffer;
90 bool result = convert(source, sourceFormat, buffer, BibUtils::MODS);
91 if (result)
92 result = convert(buffer, BibUtils::MODS, destination, destinationFormat);
93 return result;
94 }
95
96 QString bibUtilsProgram;
97 QString utf8Argument = QStringLiteral("-un");
98
99 /// Determine part of BibUtils program name that represents source format
100 switch (sourceFormat) {
101 case MODS: bibUtilsProgram = QStringLiteral("xml"); utf8Argument = QStringLiteral("-nb"); break;
102 case BibTeX: bibUtilsProgram = QStringLiteral("bib"); break;
103 case BibLaTeX: bibUtilsProgram = QStringLiteral("biblatex"); break;
104 case ISI: bibUtilsProgram = QStringLiteral("isi"); break;
105 case RIS: bibUtilsProgram = QStringLiteral("ris"); break;
106 case EndNote: bibUtilsProgram = QStringLiteral("end"); break;
107 case EndNoteXML: bibUtilsProgram = QStringLiteral("endx"); break;
108 /// case ADS not supported by BibUtils
109 case WordBib: bibUtilsProgram = QStringLiteral("wordbib"); break;
110 case Copac: bibUtilsProgram = QStringLiteral("copac"); break;
111 case Med: bibUtilsProgram = QStringLiteral("med"); break;
112 default:
113 qCWarning(LOG_KBIBTEX_IO) << "Unsupported BibUtils input format:" << sourceFormat;
114 return false;
115 }
116
117 bibUtilsProgram.append(QStringLiteral("2"));
118
119 /// Determine part of BibUtils program name that represents destination format
120 switch (destinationFormat) {
121 case MODS: bibUtilsProgram.append(QStringLiteral("xml")); break;
122 case BibTeX: bibUtilsProgram.append(QStringLiteral("bib")); break;
123 /// case BibLaTeX not supported by BibUtils
124 case ISI: bibUtilsProgram.append(QStringLiteral("isi")); break;
125 case RIS: bibUtilsProgram.append(QStringLiteral("ris")); break;
126 case EndNote: bibUtilsProgram.append(QStringLiteral("end")); break;
127 /// case EndNoteXML not supported by BibUtils
128 case ADS: bibUtilsProgram.append(QStringLiteral("ads")); break;
129 case WordBib: bibUtilsProgram.append(QStringLiteral("wordbib")); break;
130 /// case Copac not supported by BibUtils
131 /// case Med not supported by BibUtils
132 default:
133 qCWarning(LOG_KBIBTEX_IO) << "Unsupported BibUtils output format:" << destinationFormat;
134 return false;
135 }
136
137 /// Test if required BibUtils program is available
138 bibUtilsProgram = QStandardPaths::findExecutable(bibUtilsProgram);
139 if (bibUtilsProgram.isEmpty())
140 return false;
141
142 /// Test if source device is readable
143 if (!source.isReadable() && !source.open(QIODevice::ReadOnly))
144 return false;
145 /// Test if destination device is writable
146 if (!destination.isWritable() && !destination.open(QIODevice::WriteOnly)) {
147 source.close();
148 return false;
149 }
150
151 QProcess bibUtilsProcess;
152 const QStringList arguments {QStringLiteral("-i"), QStringLiteral("utf8"), utf8Argument};
153 /// Start BibUtils program/process
154 bibUtilsProcess.start(bibUtilsProgram, arguments);
155
156 bool result = bibUtilsProcess.waitForStarted();
157 if (result) {
158 /// Write source data to process's stdin
159 bibUtilsProcess.write(source.readAll());
160 /// Close process's stdin start transformation
161 bibUtilsProcess.closeWriteChannel();
162 result = bibUtilsProcess.waitForFinished();
163
164 /// If process run without problems ...
165 if (result && bibUtilsProcess.exitStatus() == QProcess::NormalExit) {
166 /// Read process's output, i.e. the transformed data
167 const QByteArray stdOut = bibUtilsProcess.readAllStandardOutput();
168 if (!stdOut.isEmpty()) {
169 /// Write transformed data to destination device
170 const int amountWritten = destination.write(stdOut);
171 /// Check that the same amount of bytes is written
172 /// as received from the BibUtils program
173 result = amountWritten == stdOut.size();
174 } else
175 result = false;
176 }
177 else
178 result = false;
179 }
180
181 /// In case it did not terminate earlier
182 bibUtilsProcess.terminate();
183
184 /// Close both source and destination device
185 source.close();
186 destination.close();
187
188 return result;
189 }
190