/** * Say or Synthesize text. * @param inputText The text that shall be spoken * @param suggestedFilename If not Null, synthesize only to this filename, otherwise * synthesize and audibilize the text. * @param userCmd The program that shall be executed for speaking * @param stdIn True if the program shall recieve its data via standard input * @param codec The QTextCodec if encoding==UseCodec * @param language The language code (used for the %l macro) */ void CommandProc::synth(const QString& inputText, const QString& suggestedFilename, const QString& userCmd, bool stdIn, QTextCodec *codec, QString& language) { if (m_commandProc) { if (m_commandProc->isRunning()) m_commandProc->kill(); delete m_commandProc; m_commandProc = 0; m_synthFilename = QString::null; if (!m_textFilename.isNull()) QFile::remove(m_textFilename); m_textFilename = QString::null; } QString command = userCmd; QString text = inputText.stripWhiteSpace(); if (text.isEmpty()) return; // 1. prepare the text: // 1.a) encode the text text += "\n"; QCString encodedText; if (codec) encodedText = codec->fromUnicode(text); else encodedText = text.latin1(); // Should not happen, but just in case. // 1.b) quote the text as one parameter QString escText = KShellProcess::quote(text); // 1.c) create a temporary file for the text, if %f macro is used. if (command.contains("%f")) { KTempFile tempFile(locateLocal("tmp", "commandplugin-"), ".txt"); QTextStream* fs = tempFile.textStream(); fs->setCodec(codec); *fs << text; *fs << endl; m_textFilename = tempFile.file()->name(); tempFile.close(); } else m_textFilename = QString::null; // 2. replace variables with values QValueStack<bool> stack; bool issinglequote=false; bool isdoublequote=false; int noreplace=0; QRegExp re_noquote("(\"|'|\\\\|`|\\$\\(|\\$\\{|\\(|\\{|\\)|\\}|%%|%t|%f|%l|%w)"); QRegExp re_singlequote("('|%%|%t|%f|%l|%w)"); QRegExp re_doublequote("(\"|\\\\|`|\\$\\(|\\$\\{|%%|%t|%f|%l|%w)"); for ( int i = re_noquote.search(command); i != -1; i = (issinglequote?re_singlequote.search(command,i) :isdoublequote?re_doublequote.search(command,i) :re_noquote.search(command,i)) ) { if ((command[i]=='(') || (command[i]=='{')) // (...) or {...} { // assert(isdoublequote == false) stack.push(isdoublequote); if (noreplace > 0) // count nested braces when within ${...} noreplace++; i++; } else if (command[i]=='$') { stack.push(isdoublequote); isdoublequote = false; if ((noreplace > 0) || (command[i+1]=='{')) // count nested braces when within ${...} noreplace++; i+=2; } else if ((command[i]==')') || (command[i]=='}')) // $(...) or (...) or ${...} or {...} { if (!stack.isEmpty()) isdoublequote = stack.pop(); else qWarning("Parse error."); if (noreplace > 0) // count nested braces when within ${...} noreplace--; i++; } else if (command[i]=='\'') { issinglequote=!issinglequote; i++; } else if (command[i]=='"') { isdoublequote=!isdoublequote; i++; } else if (command[i]=='\\') i+=2; else if (command[i]=='`') { // Replace all `...` with safer $(...) command.replace (i, 1, "$("); QRegExp re_backticks("(`|\\\\`|\\\\\\\\|\\\\\\$)"); for ( int i2=re_backticks.search(command,i+2); i2!=-1; i2=re_backticks.search(command,i2) ) { if (command[i2] == '`') { command.replace (i2, 1, ")"); i2=command.length(); // leave loop } else { // remove backslash and ignore following character command.remove (i2, 1); i2++; } } // Leave i unchanged! We need to process "$(" } else if (noreplace == 0) // do not replace macros within ${...} { QString match, v; // get match if (issinglequote) match=re_singlequote.cap(); else if (isdoublequote) match=re_doublequote.cap(); else match=re_noquote.cap(); // substitue %variables if (match=="%%") v="%"; else if (match=="%t") v=escText; else if (match=="%f") v=m_textFilename; else if (match=="%l") v=language; else if (match=="%w") v = suggestedFilename; // %variable inside of a quote? if (isdoublequote) v='"'+v+'"'; else if (issinglequote) v="'"+v+"'"; command.replace (i, match.length(), v); i+=v.length(); } else { if (issinglequote) i+=re_singlequote.matchedLength(); else if (isdoublequote) i+=re_doublequote.matchedLength(); else i+=re_noquote.matchedLength(); } } // 3. create a new process kdDebug() << "CommandProc::synth: running command: " << command << endl; m_commandProc = new KProcess; m_commandProc->setUseShell(true); m_commandProc->setEnvironment("LANG", language + "." + codec->mimeName()); m_commandProc->setEnvironment("LC_CTYPE", language + "." + codec->mimeName()); *m_commandProc << command; connect(m_commandProc, SIGNAL(processExited(KProcess*)), this, SLOT(slotProcessExited(KProcess*))); connect(m_commandProc, SIGNAL(receivedStdout(KProcess*, char*, int)), this, SLOT(slotReceivedStdout(KProcess*, char*, int))); connect(m_commandProc, SIGNAL(receivedStderr(KProcess*, char*, int)), this, SLOT(slotReceivedStderr(KProcess*, char*, int))); connect(m_commandProc, SIGNAL(wroteStdin(KProcess*)), this, SLOT(slotWroteStdin(KProcess* ))); // 4. start the process if (suggestedFilename.isNull()) m_state = psSaying; else { m_synthFilename = suggestedFilename; m_state = psSynthing; } if (stdIn) { m_commandProc->start(KProcess::NotifyOnExit, KProcess::All); if (encodedText.length() > 0) m_commandProc->writeStdin(encodedText, encodedText.length()); else m_commandProc->closeStdin(); } else m_commandProc->start(KProcess::NotifyOnExit, KProcess::AllOutput); }
QString Speech::prepareCommand(QString command, const QString &text, const QString &filename, const QString &language) { #ifdef macroExpander QHash<QChar, QString> map; map[QLatin1Char('t')] = text; map[QLatin1Char('f')] = filename; map[QLatin1Char('l')] = language; return KMacroExpander::expandMacrosShellQuote(command, map); #else QStack<bool> stack; // saved isdoublequote values during parsing of braces bool issinglequote = false; // inside '...' ? bool isdoublequote = false; // inside "..." ? int noreplace = 0; // nested braces when within ${...} QString escText = K3ShellProcess::quote(text); // character sequences that change the state or need to be otherwise processed QRegExp re_singlequote("('|%%|%t|%f|%l)"); QRegExp re_doublequote("(\"|\\\\|`|\\$\\(|\\$\\{|%%|%t|%f|%l)"); QRegExp re_noquote("('|\"|\\\\|`|\\$\\(|\\$\\{|\\(|\\{|\\)|\\}|%%|%t|%f|%l)"); // parse the command: for (int i = re_noquote.search(command); i != -1; i = (issinglequote ? re_singlequote.search(command, i) : isdoublequote ? re_doublequote.search(command, i) : re_noquote.search(command, i)) ) // while there are character sequences that need to be processed { if ((command[i] == '(') || (command[i] == '{')) { // (...) or {...} // assert(isdoublequote == false) stack.push(isdoublequote); if (noreplace > 0) // count nested braces when within ${...} noreplace++; i++; } else if (command[i] == '$') { // $(...) or ${...} stack.push(isdoublequote); isdoublequote = false; if ((noreplace > 0) || (command[i + 1] == '{')) // count nested braces when within ${...} noreplace++; i += 2; } else if ((command[i] == ')') || (command[i] == '}')) { // $(...) or (...) or ${...} or {...} if (!stack.isEmpty()) isdoublequote = stack.pop(); else qWarning("Parse error."); if (noreplace > 0) // count nested braces when within ${...} noreplace--; i++; } else if (command[i] == '\'') { issinglequote = !issinglequote; i++; } else if (command[i] == '"') { isdoublequote = !isdoublequote; i++; } else if (command[i] == '\\') i += 2; else if (command[i] == '`') { // Replace all `...` with safer $(...) command.replace(i, 1, "$("); QRegExp re_backticks("(`|\\\\`|\\\\\\\\|\\\\\\$)"); for (int i2 = re_backticks.search(command, i + 2); i2 != -1; i2 = re_backticks.search(command, i2) ) { if (command[i2] == '`') { command.replace(i2, 1, ")"); i2 = command.length(); // leave loop } else { // remove backslash and ignore following character command.remove(i2, 1); i2++; } } // Leave i unchanged! We need to process "$(" } else if (noreplace > 0) { // do not replace macros within ${...} if (issinglequote) i += re_singlequote.matchedLength(); else if (isdoublequote) i += re_doublequote.matchedLength(); else i += re_noquote.matchedLength(); } else { // replace macro QString match, v; // get match if (issinglequote) match = re_singlequote.cap(); else if (isdoublequote) match = re_doublequote.cap(); else match = re_noquote.cap(); // substitute %variables if (match == "%t") v = escText; else if (match == "%f") v = filename; else if (match == "%%") v = "%"; else if (match == "%l") v = language; // %variable inside of a quote? if (isdoublequote) v = '"' + v + '"'; else if (issinglequote) v = '\'' + v + '\''; command.replace(i, match.length(), v); i += v.length(); } } return command; #endif }