1 /*
2     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
3     SPDX-License-Identifier: GPL-2.0-or-later
4 */
5 
6 #include "k3bmkisofshandler.h"
7 
8 #include "k3bexternalbinmanager.h"
9 #include "k3bcore.h"
10 #include "k3bjob.h"
11 #include "k3b_i18n.h"
12 
13 #include <QDebug>
14 
15 #include <cmath>
16 
17 
18 
19 class K3b::MkisofsHandler::Private
20 {
21 public:
22     const K3b::ExternalBin* mkisofsBin;
23     double firstProgressValue;
24     bool readError;
25 };
26 
27 
MkisofsHandler()28 K3b::MkisofsHandler::MkisofsHandler()
29 {
30     d = new Private;
31     d->mkisofsBin = 0;
32 }
33 
34 
~MkisofsHandler()35 K3b::MkisofsHandler::~MkisofsHandler()
36 {
37     delete d;
38 }
39 
40 
mkisofsReadError() const41 bool K3b::MkisofsHandler::mkisofsReadError() const
42 {
43     return d->readError;
44 }
45 
46 
initMkisofs()47 const K3b::ExternalBin* K3b::MkisofsHandler::initMkisofs()
48 {
49     d->mkisofsBin = k3bcore->externalBinManager()->binObject( "mkisofs" );
50 
51     if( d->mkisofsBin ) {
52         if( !d->mkisofsBin->copyright().isEmpty() )
53             handleMkisofsInfoMessage( i18n("Using %1 %2 – Copyright © %3",
54                                            QString("mkisofs"),
55                                            d->mkisofsBin->version(),
56                                            d->mkisofsBin->copyright()),
57                                       K3b::Job::MessageInfo );
58 
59         d->firstProgressValue = -1;
60         d->readError = false;
61     }
62     else {
63         qDebug() << "(K3b::MkisofsHandler) could not find mkisofs executable";
64         handleMkisofsInfoMessage( i18n("Mkisofs executable not found."), K3b::Job::MessageError );
65     }
66 
67     return d->mkisofsBin;
68 }
69 
70 
parseMkisofsOutput(const QString & line)71 void K3b::MkisofsHandler::parseMkisofsOutput( const QString& line )
72 {
73     if( !line.isEmpty() ) {
74         if( line.startsWith( d->mkisofsBin->path() ) ) {
75             // error or warning
76             QString errorLine = line.mid( d->mkisofsBin->path().length() + 2 );
77             if( errorLine.startsWith( "Input/output error. Cannot read from" ) ) {
78                 handleMkisofsInfoMessage( i18n("Read error from file '%1'", errorLine.mid( 38, errorLine.length()-40 ) ),
79                                           K3b::Job::MessageError );
80                 d->readError = true;
81             }
82             else if( errorLine.startsWith( "Value too large for defined data type" ) ) {
83                 handleMkisofsInfoMessage( i18n("Used version of mkisofs does not have large file support."), K3b::Job::MessageError );
84                 handleMkisofsInfoMessage( i18n("Files bigger than 2 GB cannot be handled."), K3b::Job::MessageError );
85                 d->readError = true;
86             }
87             else if( errorLine.startsWith( "No such file or directory. cannot open" ) ) {
88                 handleMkisofsInfoMessage( i18n("No such file or directory '%1'.", errorLine.mid( 40, errorLine.length()-41 ) ),
89                                           K3b::Job::MessageError );
90                 d->readError = true;
91             }
92         }
93         else if( line.contains( "done, estimate" ) ) {
94             int p = parseMkisofsProgress( line );
95             if( p != -1 )
96                 handleMkisofsProgress( p );
97         }
98         else if( line.contains( "extents written" ) ) {
99             handleMkisofsProgress( 100 );
100         }
101         else if( line.startsWith( "Incorrectly encoded string" ) ) {
102             handleMkisofsInfoMessage( i18n("Encountered an incorrectly encoded filename '%1'",
103                                            line.section( QRegExp("[\\(\\)]"), 1, 1 )), K3b::Job::MessageError );
104             handleMkisofsInfoMessage( i18n("This may be caused by a system update which changed the local character set."), K3b::Job::MessageError );
105             handleMkisofsInfoMessage( i18n("You may use convmv (https://j3e.de/linux/convmv/) to fix the filename encoding."), K3b::Job::MessageError );
106             d->readError = true;
107         }
108         else if( line.endsWith( "has not an allowable size." ) ) {
109             handleMkisofsInfoMessage( i18n("The boot image has an invalid size."), K3b::Job::MessageError );
110             d->readError = true;
111         }
112         else if( line.endsWith( "has multiple partitions." ) ) {
113             handleMkisofsInfoMessage( i18n("The boot image contains multiple partitions."), K3b::Job::MessageError );
114             handleMkisofsInfoMessage( i18n("A hard-disk boot image has to contain a single partition."), K3b::Job::MessageError );
115             d->readError = true;
116         }
117         else {
118             qDebug() << "(mkisofs) " << line;
119         }
120     }
121 }
122 
123 
parseMkisofsProgress(const QString & line)124 int K3b::MkisofsHandler::parseMkisofsProgress(const QString& line)
125 {
126     // in multisession mode mkisofs' progress does not start at 0 but at (X+Y)/X
127     // where X is the data already on the cd and Y the data to create
128     // This is not very dramatic but kind or ugly.
129     // We just save the first emitted progress value and to some math ;)
130 
131     QString perStr = line;
132     perStr.truncate(perStr.indexOf('%'));
133     // FIXME: how to support Inuit or Samaritan Aramaic format or cover all
134     // formats? right now it only support, for example: 0.52 and 0,52
135     QRegExp rx("(\\d+.|,+\\d)");
136     QStringList list;
137     int pos = 0;
138     bool ok;
139     while ((pos = rx.indexIn(perStr, pos)) != -1) {
140         list << rx.cap(1);
141         pos += rx.matchedLength();
142     }
143     if (list.size() < 2)
144         return -1;
145     // FIXME: the same story
146     double p = (list[0].replace(',', '.') + list[1]).toDouble(&ok);
147     if (!ok) {
148         qDebug() << "(K3b::MkisofsHandler) Parsing did not work for " << perStr;
149         return -1;
150     } else {
151         if (d->firstProgressValue < 0)
152             d->firstProgressValue = p;
153 
154         return((int)::ceil((p - d->firstProgressValue) * 100.0 /
155                     (100.0 - d->firstProgressValue)));
156     }
157 }
158