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 <QtCore/QStack>
21 #include <QtCore/QRegExp>
22 #include <QtCore/QTextCodec>
23 #include <QtCore/QFile>
24 #include <QtCore/QHash>
25 #include <QtCore/QTextStream>
28 #include <kdeversion.h>
30 #include <kmacroexpander.h>
38 QString
Speech::prepareCommand (QString command
, const QString
&text
,
39 const QString
&filename
, const QString
&language
) {
41 QHash
<QChar
,QString
> map
;
45 return KMacroExpander::expandMacrosShellQuote (command
, map
);
47 QStack
<bool> stack
; // saved isdoublequote values during parsing of braces
48 bool issinglequote
=false; // inside '...' ?
49 bool isdoublequote
=false; // inside "..." ?
50 int noreplace
=0; // nested braces when within ${...}
51 QString escText
= K3ShellProcess::quote(text
);
53 // character sequences that change the state or need to be otherwise processed
54 QRegExp
re_singlequote("('|%%|%t|%f|%l)");
55 QRegExp
re_doublequote("(\"|\\\\|`|\\$\\(|\\$\\{|%%|%t|%f|%l)");
56 QRegExp
re_noquote ("('|\"|\\\\|`|\\$\\(|\\$\\{|\\(|\\{|\\)|\\}|%%|%t|%f|%l)");
59 for (int i
= re_noquote
.search(command
);
61 i
= (issinglequote
?re_singlequote
.search(command
,i
)
62 :isdoublequote
?re_doublequote
.search(command
,i
)
63 :re_noquote
.search(command
,i
))
65 // while there are character sequences that need to be processed
67 if ((command
[i
]=='(') || (command
[i
]=='{')) { // (...) or {...}
68 // assert(isdoublequote == false)
69 stack
.push(isdoublequote
);
71 // count nested braces when within ${...}
75 else if (command
[i
]=='$') { // $(...) or ${...}
76 stack
.push(isdoublequote
);
77 isdoublequote
= false;
78 if ((noreplace
> 0) || (command
[i
+1]=='{'))
79 // count nested braces when within ${...}
83 else if ((command
[i
]==')') || (command
[i
]=='}')) {
84 // $(...) or (...) or ${...} or {...}
86 isdoublequote
= stack
.pop();
88 qWarning("Parse error.");
90 // count nested braces when within ${...}
94 else if (command
[i
]=='\'') {
95 issinglequote
=!issinglequote
;
98 else if (command
[i
]=='"') {
99 isdoublequote
=!isdoublequote
;
102 else if (command
[i
]=='\\')
104 else if (command
[i
]=='`') {
105 // Replace all `...` with safer $(...)
106 command
.replace (i
, 1, "$(");
107 QRegExp
re_backticks("(`|\\\\`|\\\\\\\\|\\\\\\$)");
108 for (int i2
=re_backticks
.search(command
,i
+2);
110 i2
=re_backticks
.search(command
,i2
)
113 if (command
[i2
] == '`') {
114 command
.replace (i2
, 1, ")");
115 i2
=command
.length(); // leave loop
118 // remove backslash and ignore following character
119 command
.remove (i2
, 1);
123 // Leave i unchanged! We need to process "$("
125 else if (noreplace
> 0) { // do not replace macros within ${...}
127 i
+=re_singlequote
.matchedLength();
128 else if (isdoublequote
)
129 i
+=re_doublequote
.matchedLength();
131 i
+=re_noquote
.matchedLength();
133 else { // replace macro
138 match
=re_singlequote
.cap();
139 else if (isdoublequote
)
140 match
=re_doublequote
.cap();
142 match
=re_noquote
.cap();
144 // substitute %variables
147 else if (match
=="%f")
149 else if (match
=="%%")
151 else if (match
=="%l")
154 // %variable inside of a quote?
157 else if (issinglequote
)
160 command
.replace (i
, match
.length(), v
);
168 void Speech::speak(QString command
, bool stdIn
, const QString
&text
, const QString
&language
, int encoding
, QTextCodec
*codec
) {
169 if (text
.length () > 0) {
170 // 1. prepare the text:
171 // 1.a) encode the text
172 QTextStream
ts (encText
, QIODevice::WriteOnly
);
173 if (encoding
== Local
)
174 ts
.setEncoding (QTextStream::Locale
);
175 else if (encoding
== Latin1
)
176 ts
.setEncoding (QTextStream::Latin1
);
177 else if (encoding
== Unicode
)
178 ts
.setEncoding (QTextStream::Unicode
);
183 // 1.b) create a temporary file for the text
185 QTextStream
fs ( &tempFile
);
186 if (encoding
== Local
)
187 fs
.setEncoding (QTextStream::Locale
);
188 else if (encoding
== Latin1
)
189 fs
.setEncoding (QTextStream::Latin1
);
190 else if (encoding
== Unicode
)
191 fs
.setEncoding (QTextStream::Unicode
);
196 QString filename
= tempFile
.fileName();
199 // 2. prepare the command:
200 command
= prepareCommand (command
, encText
, filename
, language
);
203 // 3. create a new process
205 connect(&process
, SIGNAL(processExited(K3Process
*)), this, SLOT(processExited(K3Process
*)));
206 connect(&process
, SIGNAL(wroteStdin(K3Process
*)), this, SLOT(wroteStdin(K3Process
*)));
207 connect(&process
, SIGNAL(receivedStdout(K3Process
*, char *, int)), this, SLOT(receivedStdout(K3Process
*, char *, int)));
208 connect(&process
, SIGNAL(receivedStderr(K3Process
*, char *, int)), this, SLOT(receivedStderr(K3Process
*, char *, int)));
210 // 4. start the process
212 process
.start(K3Process::NotifyOnExit
, K3Process::All
);
213 if (encText
.size() > 0)
214 process
.writeStdin(encText
, encText
.size());
216 process
.closeStdin();
219 process
.start(K3Process::NotifyOnExit
, K3Process::AllOutput
);
223 void Speech::receivedStdout (K3Process
*, char *buffer
, int buflen
) {
224 kDebug() << QString::fromLatin1(buffer
, buflen
) + '\n';
226 void Speech::receivedStderr (K3Process
*, char *buffer
, int buflen
) {
227 kDebug() << QString::fromLatin1(buffer
, buflen
) + '\n';
230 void Speech::wroteStdin(K3Process
*) {
231 process
.closeStdin();
234 void Speech::processExited(K3Process
*) {
238 #include "speech.moc"