1 /***************************************************************************
2 speech.cpp - description
5 copyright : (C) 2002 by Gunnar Schmi Dt
6 email : kmouth@schmi-dt.de
7 ***************************************************************************/
9 /***************************************************************************
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
16 ***************************************************************************/
20 #include <qvaluelist.h>
21 #include <qvaluestack.h>
22 #include <qstringlist.h>
24 #include <qtextcodec.h>
28 #include <kdeversion.h>
30 #if KDE_IS_VERSION(3,2,0)
32 #include <kmacroexpander.h>
42 QString
Speech::prepareCommand (QString command
, const QString
&text
,
43 const QString
&filename
, const QString
&language
) {
45 QMap
<QChar
,QString
> map
;
49 return KMacroExpander::expandMacrosShellQuote (command
, map
);
51 QValueStack
<bool> stack
; // saved isdoublequote values during parsing of braces
52 bool issinglequote
=false; // inside '...' ?
53 bool isdoublequote
=false; // inside "..." ?
54 int noreplace
=0; // nested braces when within ${...}
55 QString escText
= KShellProcess::quote(text
);
57 // character sequences that change the state or need to be otherwise processed
58 QRegExp
re_singlequote("('|%%|%t|%f|%l)");
59 QRegExp
re_doublequote("(\"|\\\\|`|\\$\\(|\\$\\{|%%|%t|%f|%l)");
60 QRegExp
re_noquote ("('|\"|\\\\|`|\\$\\(|\\$\\{|\\(|\\{|\\)|\\}|%%|%t|%f|%l)");
63 for (int i
= re_noquote
.search(command
);
65 i
= (issinglequote
?re_singlequote
.search(command
,i
)
66 :isdoublequote
?re_doublequote
.search(command
,i
)
67 :re_noquote
.search(command
,i
))
69 // while there are character sequences that need to be processed
71 if ((command
[i
]=='(') || (command
[i
]=='{')) { // (...) or {...}
72 // assert(isdoublequote == false)
73 stack
.push(isdoublequote
);
75 // count nested braces when within ${...}
79 else if (command
[i
]=='$') { // $(...) or ${...}
80 stack
.push(isdoublequote
);
81 isdoublequote
= false;
82 if ((noreplace
> 0) || (command
[i
+1]=='{'))
83 // count nested braces when within ${...}
87 else if ((command
[i
]==')') || (command
[i
]=='}')) {
88 // $(...) or (...) or ${...} or {...}
90 isdoublequote
= stack
.pop();
92 qWarning("Parse error.");
94 // count nested braces when within ${...}
98 else if (command
[i
]=='\'') {
99 issinglequote
=!issinglequote
;
102 else if (command
[i
]=='"') {
103 isdoublequote
=!isdoublequote
;
106 else if (command
[i
]=='\\')
108 else if (command
[i
]=='`') {
109 // Replace all `...` with safer $(...)
110 command
.replace (i
, 1, "$(");
111 QRegExp
re_backticks("(`|\\\\`|\\\\\\\\|\\\\\\$)");
112 for (int i2
=re_backticks
.search(command
,i
+2);
114 i2
=re_backticks
.search(command
,i2
)
117 if (command
[i2
] == '`') {
118 command
.replace (i2
, 1, ")");
119 i2
=command
.length(); // leave loop
122 // remove backslash and ignore following character
123 command
.remove (i2
, 1);
127 // Leave i unchanged! We need to process "$("
129 else if (noreplace
> 0) { // do not replace macros within ${...}
131 i
+=re_singlequote
.matchedLength();
132 else if (isdoublequote
)
133 i
+=re_doublequote
.matchedLength();
135 i
+=re_noquote
.matchedLength();
137 else { // replace macro
142 match
=re_singlequote
.cap();
143 else if (isdoublequote
)
144 match
=re_doublequote
.cap();
146 match
=re_noquote
.cap();
148 // substitute %variables
151 else if (match
=="%f")
153 else if (match
=="%%")
155 else if (match
=="%l")
158 // %variable inside of a quote?
161 else if (issinglequote
)
164 command
.replace (i
, match
.length(), v
);
172 void Speech::speak(QString command
, bool stdIn
, const QString
&text
, const QString
&language
, int encoding
, QTextCodec
*codec
) {
173 if (text
.length () > 0) {
174 // 1. prepare the text:
175 // 1.a) encode the text
176 QTextStream
ts (encText
, IO_WriteOnly
);
177 if (encoding
== Local
)
178 ts
.setEncoding (QTextStream::Locale
);
179 else if (encoding
== Latin1
)
180 ts
.setEncoding (QTextStream::Latin1
);
181 else if (encoding
== Unicode
)
182 ts
.setEncoding (QTextStream::Unicode
);
187 // 1.b) create a temporary file for the text
188 tempFile
.setAutoDelete(true);
189 QTextStream
* fs
= tempFile
.textStream();
190 if (encoding
== Local
)
191 fs
->setEncoding (QTextStream::Locale
);
192 else if (encoding
== Latin1
)
193 fs
->setEncoding (QTextStream::Latin1
);
194 else if (encoding
== Unicode
)
195 fs
->setEncoding (QTextStream::Unicode
);
197 fs
->setCodec (codec
);
200 QString filename
= tempFile
.file()->name();
203 // 2. prepare the command:
204 command
= prepareCommand (command
, encText
, filename
, language
);
207 // 3. create a new process
209 connect(&process
, SIGNAL(processExited(KProcess
*)), this, SLOT(processExited(KProcess
*)));
210 connect(&process
, SIGNAL(wroteStdin(KProcess
*)), this, SLOT(wroteStdin(KProcess
*)));
211 connect(&process
, SIGNAL(receivedStdout(KProcess
*, char *, int)), this, SLOT(receivedStdout(KProcess
*, char *, int)));
212 connect(&process
, SIGNAL(receivedStderr(KProcess
*, char *, int)), this, SLOT(receivedStderr(KProcess
*, char *, int)));
214 // 4. start the process
216 process
.start(KProcess::NotifyOnExit
, KProcess::All
);
217 if (encText
.size() > 0)
218 process
.writeStdin(encText
, encText
.size());
220 process
.closeStdin();
223 process
.start(KProcess::NotifyOnExit
, KProcess::AllOutput
);
227 void Speech::receivedStdout (KProcess
*, char *buffer
, int buflen
) {
228 kdDebug() << QString::fromLatin1(buffer
, buflen
) + "\n";
230 void Speech::receivedStderr (KProcess
*, char *buffer
, int buflen
) {
231 kdDebug() << QString::fromLatin1(buffer
, buflen
) + "\n";
234 void Speech::wroteStdin(KProcess
*) {
235 process
.closeStdin();
238 void Speech::processExited(KProcess
*) {
242 #include "speech.moc"