refactor: use wine's parsing function for better parity
This commit is contained in:
parent
d7fc440ce3
commit
59f2aef102
6 changed files with 333 additions and 7 deletions
|
@ -10,10 +10,13 @@ CONFIG += c++17
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
main.cpp \
|
main.cpp \
|
||||||
runapplet.cpp
|
runapplet.cpp \
|
||||||
|
utils.c
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
runapplet.h
|
runapplet.h \
|
||||||
|
utils.h \
|
||||||
|
utils.hpp
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
runapplet.ui
|
runapplet.ui
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
#include "runapplet.h"
|
#include "runapplet.h"
|
||||||
#include "ui_runapplet.h"
|
#include "ui_runapplet.h"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
RunApplet::RunApplet(QWidget *parent)
|
RunApplet::RunApplet(QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
|
@ -25,7 +27,19 @@ RunApplet::RunApplet(QWidget *parent)
|
||||||
qApp->quit();
|
qApp->quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->okBtn, &QPushButton::clicked, this, &RunApplet::runApp);
|
connect(ui->okBtn, &QPushButton::clicked, this, [&]() {
|
||||||
|
runApp(ui->comboBox->currentText());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->browseBtn, &QPushButton::clicked, this, [&]() {
|
||||||
|
QString file = QFileDialog::getOpenFileName(this, tr("Browse"), "", tr("All Files (*)"));
|
||||||
|
if (file == "") return;
|
||||||
|
ui->comboBox->lineEdit()->setText(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->comboBox->lineEdit(), &QLineEdit::textChanged, this, [&]() {
|
||||||
|
ui->okBtn->setDisabled(ui->comboBox->lineEdit()->text() == "");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
RunApplet::~RunApplet()
|
RunApplet::~RunApplet()
|
||||||
|
@ -33,9 +47,17 @@ RunApplet::~RunApplet()
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunApplet::runApp() {
|
void RunApplet::runApp(QString command) {
|
||||||
QString command = ui->comboBox->currentText();
|
QStringList args;
|
||||||
QStringList args = command.split(" ");
|
|
||||||
|
int p_argc;
|
||||||
|
uint32_t p_err;
|
||||||
|
wchar_t** p_args = wine_parse_command_line(command.toStdWString().c_str(), &p_argc, &p_err);
|
||||||
|
|
||||||
|
for (int i = 0; i < p_argc; i++) {
|
||||||
|
args.push_back(QString::fromWCharArray(p_args[i]));
|
||||||
|
}
|
||||||
|
|
||||||
QProcess *process = new QProcess;
|
QProcess *process = new QProcess;
|
||||||
process->setProgram(args[0]);
|
process->setProgram(args[0]);
|
||||||
args.remove(0);
|
args.remove(0);
|
||||||
|
|
|
@ -20,7 +20,7 @@ public:
|
||||||
private:
|
private:
|
||||||
Ui::RunApplet *ui;
|
Ui::RunApplet *ui;
|
||||||
|
|
||||||
void runApp();
|
void runApp(QString command);
|
||||||
|
|
||||||
void saveState();
|
void saveState();
|
||||||
void restoreState();
|
void restoreState();
|
||||||
|
|
284
run-applet/utils.c
Normal file
284
run-applet/utils.c
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2002 Jon Griffiths
|
||||||
|
* Copyright 2016 Sebastian Lackner
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MAX_PATH 1024
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* CommandLineToArgvW [SHCORE.@]
|
||||||
|
*
|
||||||
|
* We must interpret the quotes in the command line to rebuild the argv
|
||||||
|
* array correctly:
|
||||||
|
* - arguments are separated by spaces or tabs
|
||||||
|
* - quotes serve as optional argument delimiters
|
||||||
|
* '"a b"' -> 'a b'
|
||||||
|
* - escaped quotes must be converted back to '"'
|
||||||
|
* '\"' -> '"'
|
||||||
|
* - consecutive backslashes preceding a quote see their number halved with
|
||||||
|
* the remainder escaping the quote:
|
||||||
|
* 2n backslashes + quote -> n backslashes + quote as an argument delimiter
|
||||||
|
* 2n+1 backslashes + quote -> n backslashes + literal quote
|
||||||
|
* - backslashes that are not followed by a quote are copied literally:
|
||||||
|
* 'a\b' -> 'a\b'
|
||||||
|
* 'a\\b' -> 'a\\b'
|
||||||
|
* - in quoted strings, consecutive quotes see their number divided by three
|
||||||
|
* with the remainder modulo 3 deciding whether to close the string or not.
|
||||||
|
* Note that the opening quote must be counted in the consecutive quotes,
|
||||||
|
* that's the (1+) below:
|
||||||
|
* (1+) 3n quotes -> n quotes
|
||||||
|
* (1+) 3n+1 quotes -> n quotes plus closes the quoted string
|
||||||
|
* (1+) 3n+2 quotes -> n+1 quotes plus closes the quoted string
|
||||||
|
* - in unquoted strings, the first quote opens the quoted string and the
|
||||||
|
* remaining consecutive quotes follow the above rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
wchar_t** wine_parse_command_line(const wchar_t *cmdline, int *numargs, uint32_t* error)
|
||||||
|
{
|
||||||
|
int qcount, bcount;
|
||||||
|
const wchar_t *s;
|
||||||
|
wchar_t **argv;
|
||||||
|
uint32_t argc;
|
||||||
|
wchar_t *d;
|
||||||
|
|
||||||
|
*error = 0;
|
||||||
|
if (!numargs)
|
||||||
|
{
|
||||||
|
*error = ERROR_INVALID_PARAMETER;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This functionality is not used and requires passing in an extra argument
|
||||||
|
// if (*cmdline == 0)
|
||||||
|
// {
|
||||||
|
// /* Return the path to the executable */
|
||||||
|
// uint32_t len, deslen = MAX_PATH, size;
|
||||||
|
|
||||||
|
// size = sizeof(wchar_t *) * 2 + deslen * sizeof(wchar_t);
|
||||||
|
// for (;;)
|
||||||
|
// {
|
||||||
|
// if (!(argv = malloc(size))) return NULL;
|
||||||
|
// len = GetModuleFileNameW(0, (wchar_t *)(argv + 2), deslen);
|
||||||
|
// if (!len)
|
||||||
|
// {
|
||||||
|
// free(argv);
|
||||||
|
// return NULL;
|
||||||
|
// }
|
||||||
|
// if (len < deslen) break;
|
||||||
|
// deslen *= 2;
|
||||||
|
// size = sizeof(wchar_t *) * 2 + deslen * sizeof(wchar_t);
|
||||||
|
// free(argv);
|
||||||
|
// }
|
||||||
|
// argv[0] = (wchar_t *)(argv + 2);
|
||||||
|
// argv[1] = NULL;
|
||||||
|
// *numargs = 1;
|
||||||
|
|
||||||
|
// return argv;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/* --- First count the arguments */
|
||||||
|
argc = 1;
|
||||||
|
s = cmdline;
|
||||||
|
/* The first argument, the executable path, follows special rules */
|
||||||
|
if (*s == '"')
|
||||||
|
{
|
||||||
|
/* The executable path ends at the next quote, no matter what */
|
||||||
|
s++;
|
||||||
|
while (*s)
|
||||||
|
if (*s++ == '"')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The executable path ends at the next space, no matter what */
|
||||||
|
while (*s && *s != ' ' && *s != '\t')
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
/* skip to the first argument, if any */
|
||||||
|
while (*s == ' ' || *s == '\t')
|
||||||
|
s++;
|
||||||
|
if (*s)
|
||||||
|
argc++;
|
||||||
|
|
||||||
|
/* Analyze the remaining arguments */
|
||||||
|
qcount = bcount = 0;
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
if ((*s == ' ' || *s == '\t') && qcount == 0)
|
||||||
|
{
|
||||||
|
/* skip to the next argument and count it if any */
|
||||||
|
while (*s == ' ' || *s == '\t')
|
||||||
|
s++;
|
||||||
|
if (*s)
|
||||||
|
argc++;
|
||||||
|
bcount = 0;
|
||||||
|
}
|
||||||
|
else if (*s == '\\')
|
||||||
|
{
|
||||||
|
/* '\', count them */
|
||||||
|
bcount++;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
else if (*s == '"')
|
||||||
|
{
|
||||||
|
/* '"' */
|
||||||
|
if ((bcount & 1) == 0)
|
||||||
|
qcount++; /* unescaped '"' */
|
||||||
|
s++;
|
||||||
|
bcount = 0;
|
||||||
|
/* consecutive quotes, see comment in copying code below */
|
||||||
|
while (*s == '"')
|
||||||
|
{
|
||||||
|
qcount++;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
qcount = qcount % 3;
|
||||||
|
if (qcount == 2)
|
||||||
|
qcount = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* a regular character */
|
||||||
|
bcount = 0;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate in a single lump, the string array, and the strings that go
|
||||||
|
* with it. This way the caller can make a single LocalFree() call to free
|
||||||
|
* both, as per MSDN.
|
||||||
|
*/
|
||||||
|
argv = malloc((argc + 1) * sizeof(wchar_t *) + (wcslen(cmdline) + 1) * sizeof(wchar_t));
|
||||||
|
if (!argv)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* --- Then split and copy the arguments */
|
||||||
|
argv[0] = d = wcscpy((wchar_t *)(argv + argc + 1), cmdline);
|
||||||
|
argc = 1;
|
||||||
|
/* The first argument, the executable path, follows special rules */
|
||||||
|
if (*d == '"')
|
||||||
|
{
|
||||||
|
/* The executable path ends at the next quote, no matter what */
|
||||||
|
s = d + 1;
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
if (*s == '"')
|
||||||
|
{
|
||||||
|
s++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*d++ = *s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The executable path ends at the next space, no matter what */
|
||||||
|
while (*d && *d != ' ' && *d != '\t')
|
||||||
|
d++;
|
||||||
|
s = d;
|
||||||
|
if (*s)
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
/* close the executable path */
|
||||||
|
*d++ = 0;
|
||||||
|
/* skip to the first argument and initialize it if any */
|
||||||
|
while (*s == ' ' || *s == '\t')
|
||||||
|
s++;
|
||||||
|
if (!*s)
|
||||||
|
{
|
||||||
|
/* There are no parameters so we are all done */
|
||||||
|
argv[argc] = NULL;
|
||||||
|
*numargs = argc;
|
||||||
|
return argv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Split and copy the remaining arguments */
|
||||||
|
argv[argc++] = d;
|
||||||
|
qcount = bcount = 0;
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
if ((*s == ' ' || *s == '\t') && qcount == 0)
|
||||||
|
{
|
||||||
|
/* close the argument */
|
||||||
|
*d++ = 0;
|
||||||
|
bcount = 0;
|
||||||
|
|
||||||
|
/* skip to the next one and initialize it if any */
|
||||||
|
do {
|
||||||
|
s++;
|
||||||
|
} while (*s == ' ' || *s == '\t');
|
||||||
|
if (*s)
|
||||||
|
argv[argc++] = d;
|
||||||
|
}
|
||||||
|
else if (*s=='\\')
|
||||||
|
{
|
||||||
|
*d++ = *s++;
|
||||||
|
bcount++;
|
||||||
|
}
|
||||||
|
else if (*s == '"')
|
||||||
|
{
|
||||||
|
if ((bcount & 1) == 0)
|
||||||
|
{
|
||||||
|
/* Preceded by an even number of '\', this is half that
|
||||||
|
* number of '\', plus a quote which we erase.
|
||||||
|
*/
|
||||||
|
d -= bcount / 2;
|
||||||
|
qcount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Preceded by an odd number of '\', this is half that
|
||||||
|
* number of '\' followed by a '"'
|
||||||
|
*/
|
||||||
|
d = d - bcount / 2 - 1;
|
||||||
|
*d++ = '"';
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
bcount = 0;
|
||||||
|
/* Now count the number of consecutive quotes. Note that qcount
|
||||||
|
* already takes into account the opening quote if any, as well as
|
||||||
|
* the quote that lead us here.
|
||||||
|
*/
|
||||||
|
while (*s == '"')
|
||||||
|
{
|
||||||
|
if (++qcount == 3)
|
||||||
|
{
|
||||||
|
*d++ = '"';
|
||||||
|
qcount = 0;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
if (qcount == 2)
|
||||||
|
qcount = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* a regular character */
|
||||||
|
*d++ = *s++;
|
||||||
|
bcount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*d = '\0';
|
||||||
|
argv[argc] = NULL;
|
||||||
|
*numargs = argc;
|
||||||
|
|
||||||
|
return argv;
|
||||||
|
}
|
8
run-applet/utils.h
Normal file
8
run-applet/utils.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define ERROR_INVALID_PARAMETER -1
|
||||||
|
|
||||||
|
wchar_t** wine_parse_command_line(const wchar_t *cmdline, int *numargs, uint32_t* error);
|
9
run-applet/utils.hpp
Normal file
9
run-applet/utils.hpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define ERROR_INVALID_PARAMETER -1
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
wchar_t** wine_parse_command_line(const wchar_t *cmdline, int *numargs, uint32_t* error);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue