[2/6] msi: Rewrite command line parsing to handle quoted values correctly.
Hans Leidekker
hans at codeweavers.com
Thu Dec 23 10:07:55 CST 2010
---
dlls/msi/action.c | 196 +++++++++++++++++++++++++++++++++-------------
dlls/msi/tests/install.c | 173 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 316 insertions(+), 53 deletions(-)
diff --git a/dlls/msi/action.c b/dlls/msi/action.c
index 2cccdc9..48e77a3 100644
--- a/dlls/msi/action.c
+++ b/dlls/msi/action.c
@@ -299,79 +299,167 @@ static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
msiobj_release(&row->hdr);
}
+enum parse_state
+{
+ state_whitespace,
+ state_token,
+ state_quote
+};
+
+static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
+{
+ enum parse_state state = state_quote;
+ const WCHAR *p;
+ WCHAR *out = value;
+ int ignore, in_quotes = 0, count = 0, len = 0;
+
+ for (p = str; *p; p++)
+ {
+ ignore = 0;
+ switch (state)
+ {
+ case state_whitespace:
+ switch (*p)
+ {
+ case ' ':
+ if (!count) goto done;
+ in_quotes = 1;
+ ignore = 1;
+ break;
+ case '"':
+ state = state_quote;
+ if (in_quotes) count--;
+ else count++;
+ break;
+ default:
+ state = state_token;
+ if (!count) in_quotes = 0;
+ else in_quotes = 1;
+ len++;
+ break;
+ }
+ break;
+
+ case state_token:
+ switch (*p)
+ {
+ case '"':
+ state = state_quote;
+ if (in_quotes) count--;
+ else count++;
+ break;
+ case ' ':
+ state = state_whitespace;
+ if (!count) goto done;
+ in_quotes = 1;
+ break;
+ default:
+ if (!count) in_quotes = 0;
+ else in_quotes = 1;
+ len++;
+ break;
+ }
+ break;
+
+ case state_quote:
+ switch (*p)
+ {
+ case '"':
+ if (in_quotes) count--;
+ else count++;
+ break;
+ case ' ':
+ state = state_whitespace;
+ if (!count || !len) goto done;
+ in_quotes = 1;
+ break;
+ default:
+ state = state_token;
+ if (!count) in_quotes = 0;
+ else in_quotes = 1;
+ len++;
+ break;
+ }
+ break;
+
+ default: break;
+ }
+ if (!ignore) *out++ = *p;
+ }
+
+done:
+ if (!len) *value = 0;
+ else *out = 0;
+
+ *quotes = count;
+ return p - str;
+}
+
+static void remove_quotes( WCHAR *str )
+{
+ WCHAR *p = str;
+ int len = strlenW( str );
+
+ while ((p = strchrW( p, '"' )))
+ {
+ memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
+ p++;
+ }
+}
+
UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
BOOL preserve_case )
{
- LPCWSTR ptr,ptr2;
- BOOL quote;
+ LPCWSTR ptr, ptr2;
+ int quotes;
DWORD len;
- LPWSTR prop = NULL, val = NULL;
+ WCHAR *prop, *val;
+ UINT r;
if (!szCommandLine)
return ERROR_SUCCESS;
ptr = szCommandLine;
-
while (*ptr)
{
- if (*ptr==' ')
- {
- ptr++;
- continue;
- }
+ while (*ptr == ' ') ptr++;
+ if (!*ptr) break;
- TRACE("Looking at %s\n",debugstr_w(ptr));
-
- ptr2 = strchrW(ptr,'=');
- if (!ptr2)
- {
- ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
- break;
- }
+ ptr2 = strchrW( ptr, '=' );
+ if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
- quote = FALSE;
+ len = ptr2 - ptr;
+ if (!len) return ERROR_INVALID_COMMAND_LINE;
- len = ptr2-ptr;
- prop = msi_alloc((len+1)*sizeof(WCHAR));
- memcpy(prop,ptr,len*sizeof(WCHAR));
- prop[len]=0;
-
- if (!preserve_case)
- struprW(prop);
+ prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
+ memcpy( prop, ptr, len * sizeof(WCHAR) );
+ prop[len] = 0;
+ if (!preserve_case) struprW( prop );
ptr2++;
-
- len = 0;
- ptr = ptr2;
- while (*ptr && (quote || (!quote && *ptr!=' ')))
- {
- if (*ptr == '"')
- quote = !quote;
- ptr++;
- len++;
- }
-
- if (*ptr2=='"')
+ while (*ptr2 == ' ') ptr2++;
+
+ quotes = 0;
+ val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
+ len = parse_prop( ptr2, val, "es );
+ if (quotes % 2)
{
- ptr2++;
- len -= 2;
+ WARN("unbalanced quotes\n");
+ msi_free( val );
+ msi_free( prop );
+ return ERROR_INVALID_COMMAND_LINE;
}
- val = msi_alloc((len+1)*sizeof(WCHAR));
- memcpy(val,ptr2,len*sizeof(WCHAR));
- val[len] = 0;
+ remove_quotes( val );
+ TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
- if (lstrlenW(prop) > 0)
- {
- UINT r = msi_set_property( package->db, prop, val );
+ r = msi_set_property( package->db, prop, val );
+ if (r == ERROR_SUCCESS && !strcmpW( prop, cszSourceDir ))
+ msi_reset_folders( package, TRUE );
- TRACE("Found commandline property (%s) = (%s)\n",
- debugstr_w(prop), debugstr_w(val));
+ msi_free( val );
+ msi_free( prop );
- if (r == ERROR_SUCCESS && !strcmpW( prop, cszSourceDir ))
- msi_reset_folders( package, TRUE );
- }
- msi_free(val);
- msi_free(prop);
+ ptr = ptr2 + len;
}
return ERROR_SUCCESS;
@@ -7356,7 +7444,9 @@ UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
msi_set_sourcedir_props(package, FALSE);
}
- msi_parse_command_line( package, szCommandLine, FALSE );
+ rc = msi_parse_command_line( package, szCommandLine, FALSE );
+ if (rc != ERROR_SUCCESS)
+ return rc;
msi_apply_transforms( package );
msi_apply_patches( package );
diff --git a/dlls/msi/tests/install.c b/dlls/msi/tests/install.c
index 211d6a8..dfcf178 100644
--- a/dlls/msi/tests/install.c
+++ b/dlls/msi/tests/install.c
@@ -1169,6 +1169,25 @@ static const CHAR sd_custom_action_dat[] = "Action\tType\tSource\tTarget\tISComm
"TestSourceDirProp34\t19\t\tTest 34 failed\t\n"
"TestSourceDirProp35\t19\t\tTest 35 failed\t\n";
+static const CHAR cl_custom_action_dat[] = "Action\tType\tSource\tTarget\tISComments\n"
+ "s72\ti2\tS64\tS0\tS255\n"
+ "CustomAction\tAction\n"
+ "TestCommandlineProp\t19\t\tTest1\t\n";
+
+static const CHAR cl_install_exec_seq_dat[] = "Action\tCondition\tSequence\n"
+ "s72\tS255\tI2\n"
+ "InstallExecuteSequence\tAction\n"
+ "LaunchConditions\t\t100\n"
+ "ValidateProductID\t\t700\n"
+ "CostInitialize\t\t800\n"
+ "FileCost\t\t900\n"
+ "CostFinalize\t\t1000\n"
+ "TestCommandlineProp\tP=\"one\"\t1100\n"
+ "InstallInitialize\t\t1500\n"
+ "ProcessComponents\t\t1600\n"
+ "InstallValidate\t\t1400\n"
+ "InstallFinalize\t\t5000\n";
+
typedef struct _msi_table
{
const CHAR *filename;
@@ -1835,6 +1854,19 @@ static const msi_table pv_tables[] =
ADD_TABLE(property)
};
+static const msi_table cl_tables[] =
+{
+ ADD_TABLE(component),
+ ADD_TABLE(directory),
+ ADD_TABLE(feature),
+ ADD_TABLE(feature_comp),
+ ADD_TABLE(file),
+ ADD_TABLE(cl_custom_action),
+ ADD_TABLE(cl_install_exec_seq),
+ ADD_TABLE(media),
+ ADD_TABLE(property)
+};
+
/* cabinet definitions */
/* make the max size large so there is only one cab file */
@@ -6168,6 +6200,146 @@ error:
RemoveDirectory("msitest");
}
+static void test_command_line_parsing(void)
+{
+ UINT r;
+ const char *cmd;
+
+ if (is_process_limited())
+ {
+ skip("process is limited\n");
+ return;
+ }
+
+ create_test_files();
+ create_database(msifile, cl_tables, sizeof(cl_tables)/sizeof(msi_table));
+
+ MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
+
+ cmd = " ";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "=";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "==";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "one";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "=one";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "P=";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = " P=";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "P= ";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "P=\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "P=\"\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "P=\"\"\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "P=\"\"\"\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "P=\" ";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "P= \"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "P= \"\" ";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "P=one";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
+
+ cmd = "P= one";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
+
+ cmd = "P=\"one";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "P=one\"";
+ r = MsiInstallProductA(msifile, cmd);
+ todo_wine ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "P=\"one\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
+
+ cmd = "P= \"one\" ";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
+
+ cmd = "P=\"one\"\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "P=\"\"one\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "P=\"\"one\"\"";
+ r = MsiInstallProductA(msifile, cmd);
+ todo_wine ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
+
+ cmd = "P=\"one two\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "P=\"\"\"one\"\" two\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "P=\"\"\"one\"\" two\" Q=three";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "P=\"\" Q=\"two\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ cmd = "P=\"one\" Q=\"two\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
+
+ cmd = "P=\"one=two\"";
+ r = MsiInstallProductA(msifile, cmd);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ DeleteFile(msifile);
+ RemoveDirectory("msitest");
+}
+
START_TEST(install)
{
DWORD len;
@@ -6257,6 +6429,7 @@ START_TEST(install)
test_icon_table();
test_sourcedir_props();
test_package_validation();
+ test_command_line_parsing();
DeleteFileA(log_file);
--
1.7.1
More information about the wine-patches
mailing list