[6/7] msi: Rewrite the folder resolution code to keep track of the parent-child relationship.

Hans Leidekker hans at codeweavers.com
Mon May 2 09:03:55 CDT 2011


---
 dlls/msi/action.c        |  181 ++++++++++++++++++++++++++++------------------
 dlls/msi/helpers.c       |  130 ---------------------------------
 dlls/msi/install.c       |  113 +++++++++++++----------------
 dlls/msi/msipriv.h       |   12 +++-
 dlls/msi/package.c       |   31 ++++++---
 dlls/msi/tests/package.c |    8 ++
 6 files changed, 199 insertions(+), 276 deletions(-)

diff --git a/dlls/msi/action.c b/dlls/msi/action.c
index 86be546..bcae5e9 100644
--- a/dlls/msi/action.c
+++ b/dlls/msi/action.c
@@ -2056,16 +2056,14 @@ static UINT load_folder( MSIRECORD *row, LPVOID param )
     LPWSTR p, tgt_short, tgt_long, src_short, src_long;
     MSIFOLDER *folder;
 
-    folder = msi_alloc_zero( sizeof (MSIFOLDER) );
-    if (!folder)
-        return ERROR_NOT_ENOUGH_MEMORY;
-
+    if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
+    list_init( &folder->children );
     folder->Directory = msi_dup_record_field( row, 1 );
+    folder->Parent = msi_dup_record_field( row, 2 );
+    p = msi_dup_record_field(row, 3);
 
     TRACE("%s\n", debugstr_w(folder->Directory));
 
-    p = msi_dup_record_field(row, 3);
-
     /* split src and target dir */
     tgt_short = p;
     src_short = folder_split_path( p, ':' );
@@ -2101,17 +2099,36 @@ static UINT load_folder( MSIRECORD *row, LPVOID param )
     TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
     TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
 
-    folder->Parent = msi_dup_record_field( row, 2 );
-
-    folder->Property = msi_dup_property( package->db, folder->Directory );
-
     list_add_tail( &package->folders, &folder->entry );
+    return ERROR_SUCCESS;
+}
 
-    TRACE("returning %p\n", folder);
+static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
+{
+    FolderList *fl;
 
+    if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
+    fl->folder = child;
+    list_add_tail( &parent->children, &fl->entry );
     return ERROR_SUCCESS;
 }
 
+static UINT find_folder_children( MSIRECORD *row, LPVOID param )
+{
+    MSIPACKAGE *package = param;
+    MSIFOLDER *parent, *child;
+
+    if (!(child = get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
+        return ERROR_FUNCTION_FAILED;
+
+    if (!child->Parent) return ERROR_SUCCESS;
+
+    if (!(parent = get_loaded_folder( package, child->Parent )))
+        return ERROR_FUNCTION_FAILED;
+
+    return add_folder_child( parent, child );
+}
+
 static UINT load_all_folders( MSIPACKAGE *package )
 {
     static const WCHAR query[] = {
@@ -2127,24 +2144,17 @@ static UINT load_all_folders( MSIPACKAGE *package )
     if (r != ERROR_SUCCESS)
         return r;
 
-    r = MSI_IterateRecords(view, NULL, load_folder, package);
-    msiobj_release(&view->hdr);
+    r = MSI_IterateRecords( view, NULL, load_folder, package );
+    if (r != ERROR_SUCCESS)
+    {
+        msiobj_release( &view->hdr );
+        return r;
+    }
+    r = MSI_IterateRecords( view, NULL, find_folder_children, package );
+    msiobj_release( &view->hdr );
     return r;
 }
 
-/*
- * I am not doing any of the costing functionality yet.
- * Mostly looking at doing the Component and Feature loading
- *
- * The native MSI does A LOT of modification to tables here. Mostly adding
- * a lot of temporary columns to the Feature and Component tables.
- *
- *    note: Native msi also tracks the short filename. But I am only going to
- *          track the long ones.  Also looking at this directory table
- *          it appears that the directory table does not get the parents
- *          resolved base on property only based on their entries in the
- *          directory table.
- */
 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
 {
     msi_set_property( package->db, szCostingComplete, szZero );
@@ -2554,30 +2564,6 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package)
     return ERROR_SUCCESS;
 }
 
-static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
-{
-    MSIPACKAGE *package = param;
-    LPCWSTR name;
-    LPWSTR path;
-    MSIFOLDER *f;
-
-    name = MSI_RecordGetString(row,1);
-
-    f = get_loaded_folder(package, name);
-    if (!f) return ERROR_SUCCESS;
-
-    /* reset the ResolvedTarget */
-    msi_free(f->ResolvedTarget);
-    f->ResolvedTarget = NULL;
-
-    TRACE("directory %s ...\n", debugstr_w(name));
-    path = resolve_target_folder( package, name, TRUE, TRUE, NULL );
-    TRACE("resolves to %s\n", debugstr_w(path));
-    msi_free(path);
-
-    return ERROR_SUCCESS;
-}
-
 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
 {
     MSIPACKAGE *package = param;
@@ -2782,20 +2768,78 @@ static UINT calculate_file_cost( MSIPACKAGE *package )
     return ERROR_SUCCESS;
 }
 
-/*
- * A lot is done in this function aside from just the costing.
- * The costing needs to be implemented at some point but for now I am going
- * to focus on the directory building
- *
- */
+void msi_clean_path( WCHAR *p )
+{
+    WCHAR *q = p;
+    int n, len = 0;
+
+    while (1)
+    {
+        /* copy until the end of the string or a space */
+        while (*p != ' ' && (*q = *p))
+        {
+            p++, len++;
+            /* reduce many backslashes to one */
+            if (*p != '\\' || *q != '\\')
+                q++;
+        }
+
+        /* quit at the end of the string */
+        if (!*p)
+            break;
+
+        /* count the number of spaces */
+        n = 0;
+        while (p[n] == ' ')
+            n++;
+
+        /* if it's leading or trailing space, skip it */
+        if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
+            p += n;
+        else  /* copy n spaces */
+            while (n && (*q++ = *p++)) n--;
+    }
+}
+
+void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
+{
+    FolderList *fl;
+    MSIFOLDER *folder, *parent, *child;
+    WCHAR *path;
+
+    TRACE("resolving %s\n", debugstr_w(name));
+
+    if (!(folder = get_loaded_folder( package, name ))) return;
+
+    if (!strcmpW( folder->Directory, cszTargetDir )) /* special resolving for target root dir */
+    {
+        if (!load_prop || !(path = msi_dup_property( package->db, cszTargetDir )))
+        {
+            path = msi_dup_property( package->db, cszRootDrive );
+        }
+    }
+    else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
+    {
+        parent = get_loaded_folder( package, folder->Parent );
+        path = build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
+    }
+    msi_clean_path( path );
+    msi_set_property( package->db, folder->Directory, path );
+    msi_free( folder->ResolvedTarget );
+    folder->ResolvedTarget = path;
+
+    LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
+    {
+        child = fl->folder;
+        msi_resolve_target_folder( package, child->Directory, load_prop );
+    }
+    TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
+}
+
 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
 {
-    static const WCHAR ExecSeqQuery[] =
-        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
-         '`','D','i','r','e','c','t','o','r','y','`',0};
-    static const WCHAR ConditionQuery[] =
-        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
-         '`','C','o','n','d','i','t','i','o','n','`',0};
+    static const WCHAR condition_query[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','C','o','n','d','i','t','i','o','n','`',0};
     static const WCHAR szlevel[] =
         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
     static const WCHAR szOutOfDiskSpace[] =
@@ -2805,15 +2849,8 @@ static UINT ACTION_CostFinalize(MSIPACKAGE *package)
     MSIQUERY * view;
     LPWSTR level;
 
-    TRACE("Building Directory properties\n");
-
-    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
-    if (rc == ERROR_SUCCESS)
-    {
-        rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
-                        package);
-        msiobj_release(&view->hdr);
-    }
+    TRACE("Building directory properties\n");
+    msi_resolve_target_folder( package, cszTargetDir, TRUE );
 
     TRACE("Evaluating component conditions\n");
     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
@@ -2835,7 +2872,7 @@ static UINT ACTION_CostFinalize(MSIPACKAGE *package)
     {
         TRACE("Evaluating feature conditions\n");
 
-        rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
+        rc = MSI_DatabaseOpenViewW( package->db, condition_query, &view );
         if (rc == ERROR_SUCCESS)
         {
             rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
diff --git a/dlls/msi/helpers.c b/dlls/msi/helpers.c
index e81736e..aecdc5b 100644
--- a/dlls/msi/helpers.c
+++ b/dlls/msi/helpers.c
@@ -34,7 +34,6 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(msi);
 
-static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
 static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
 
 LPWSTR build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name )
@@ -198,45 +197,6 @@ static LPWSTR get_source_root( MSIPACKAGE *package )
     return path;
 }
 
-/*
- * clean_spaces_from_path()
- *
- * removes spaces from the beginning and end of path segments
- * removes multiple \\ characters
- */
-static void clean_spaces_from_path( LPWSTR p )
-{
-    LPWSTR q = p;
-    int n, len = 0;
-
-    while (1)
-    {
-        /* copy until the end of the string or a space */
-        while (*p != ' ' && (*q = *p))
-        {
-            p++, len++;
-            /* reduce many backslashes to one */
-            if (*p != '\\' || *q != '\\')
-                q++;
-        }
-
-        /* quit at the end of the string */
-        if (!*p)
-            break;
-
-        /* count the number of spaces */
-        n = 0;
-        while (p[n] == ' ')
-            n++;
-
-        /* if it's leading or trailing space, skip it */
-        if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
-            p += n;
-        else  /* copy n spaces */
-            while (n && (*q++ = *p++)) n--;
-    }
-}
-
 LPWSTR resolve_file_source(MSIPACKAGE *package, MSIFILE *file)
 {
     LPWSTR p, path;
@@ -315,96 +275,6 @@ LPWSTR resolve_source_folder( MSIPACKAGE *package, LPCWSTR name, MSIFOLDER **fol
     return path;
 }
 
-const WCHAR *msi_get_target_folder( MSIPACKAGE *package, const WCHAR *name )
-{
-    MSIFOLDER *folder = get_loaded_folder( package, name );
-    if (folder) return folder->ResolvedTarget;
-    return NULL;
-}
-
-LPWSTR resolve_target_folder( MSIPACKAGE *package, LPCWSTR name, BOOL set_prop, BOOL load_prop,
-                              MSIFOLDER **folder )
-{
-    MSIFOLDER *f;
-    LPWSTR p, path = NULL, parent;
-
-    TRACE("working to resolve %s\n", debugstr_w(name));
-
-    f = get_loaded_folder( package, name );
-    if (!f)
-        return NULL;
-
-    /* special resolving for Target and Source root dir */
-    if (!strcmpW( name, cszTargetDir ))
-    {
-        if (!f->ResolvedTarget && !f->Property)
-        {
-            LPWSTR check_path;
-            check_path = msi_dup_property( package->db, cszTargetDir );
-            if (!check_path)
-            {
-                check_path = msi_dup_property( package->db, cszRootDrive );
-                if (set_prop)
-                    msi_set_property( package->db, cszTargetDir, check_path );
-            }
-
-            /* correct misbuilt target dir */
-            path = build_directory_name(2, check_path, NULL);
-            clean_spaces_from_path( path );
-            if (strcmpiW( path, check_path ))
-                msi_set_property( package->db, cszTargetDir, path );
-            msi_free(check_path);
-
-            f->ResolvedTarget = path;
-        }
-    }
-
-    if (folder)
-        *folder = f;
-
-    if (f->ResolvedTarget)
-    {
-        path = strdupW( f->ResolvedTarget );
-        TRACE("   already resolved to %s\n", debugstr_w(path));
-        return path;
-    }
-
-    if (f->Property)
-    {
-        path = build_directory_name( 2, f->Property, NULL );
-        TRACE("   internally set to %s\n", debugstr_w(path));
-        if (set_prop) msi_set_property( package->db, name, path );
-        return path;
-    }
-
-    if (load_prop && (path = msi_dup_property( package->db, name )))
-    {
-        f->ResolvedTarget = strdupW( path );
-        TRACE("   property set to %s\n", debugstr_w(path));
-        return path;
-    }
-
-    if (!f->Parent)
-        return path;
-
-    parent = f->Parent;
-
-    TRACE(" ! parent is %s\n", debugstr_w(parent));
-
-    p = resolve_target_folder( package, parent, set_prop, load_prop, NULL );
-
-    TRACE("   TargetDefault = %s\n", debugstr_w(f->TargetDefault));
-    path = build_directory_name( 3, p, f->TargetDefault, NULL );
-    clean_spaces_from_path( path );
-    f->ResolvedTarget = strdupW( path );
-
-    TRACE("-> %s\n", debugstr_w(path));
-    if (set_prop) msi_set_property( package->db, name, path );
-    msi_free( p );
-
-    return path;
-}
-
 /* wrapper to resist a need for a full rewrite right now */
 DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data )
 {
diff --git a/dlls/msi/install.c b/dlls/msi/install.c
index 5879df4..5993867 100644
--- a/dlls/msi/install.c
+++ b/dlls/msi/install.c
@@ -212,6 +212,13 @@ UINT msi_strcpy_to_awstring( LPCWSTR str, awstring *awbuf, DWORD *sz )
     return r;
 }
 
+const WCHAR *msi_get_target_folder( MSIPACKAGE *package, const WCHAR *name )
+{
+    MSIFOLDER *folder = get_loaded_folder( package, name );
+    if (folder) return folder->ResolvedTarget;
+    return NULL;
+}
+
 /***********************************************************************
  * MsiGetTargetPath   (internal)
  */
@@ -219,7 +226,7 @@ static UINT MSI_GetTargetPath( MSIHANDLE hInstall, LPCWSTR szFolder,
                                awstring *szPathBuf, LPDWORD pcchPathBuf )
 {
     MSIPACKAGE *package;
-    LPWSTR path;
+    const WCHAR *path;
     UINT r = ERROR_FUNCTION_FAILED;
 
     if (!szFolder)
@@ -280,14 +287,13 @@ done:
         return r;
     }
 
-    path = resolve_target_folder( package, szFolder, FALSE, TRUE, NULL );
+    path = msi_get_target_folder( package, szFolder );
     msiobj_release( &package->hdr );
 
     if (!path)
         return ERROR_DIRECTORY;
 
     r = msi_strcpy_to_awstring( path, szPathBuf, pcchPathBuf );
-    msi_free( path );
     return r;
 }
 
@@ -485,80 +491,63 @@ end:
     return rc;
 }
 
-/*
- * Ok my original interpretation of this was wrong. And it looks like msdn has
- * changed a bit also. The given folder path does not have to actually already
- * exist, it just cannot be read only and must be a legal folder path.
- */
-UINT MSI_SetTargetPathW(MSIPACKAGE *package, LPCWSTR szFolder, 
-                             LPCWSTR szFolderPath)
+static void set_target_path( MSIPACKAGE *package, MSIFOLDER *folder, const WCHAR *path )
 {
-    DWORD attrib;
-    LPWSTR path = NULL;
-    LPWSTR path2 = NULL;
+    FolderList *fl;
+    MSIFOLDER *child;
+
+    msi_free( folder->ResolvedTarget );
+    folder->ResolvedTarget = strdupW( path );
+    msi_clean_path( folder->ResolvedTarget );
+    msi_set_property( package->db, folder->Directory, folder->ResolvedTarget );
+
+    LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
+    {
+        child = fl->folder;
+        msi_resolve_target_folder( package, child->Directory, FALSE );
+    }
+}
+
+UINT MSI_SetTargetPathW( MSIPACKAGE *package, LPCWSTR szFolder, LPCWSTR szFolderPath )
+{
+    DWORD attrib, len;
     MSIFOLDER *folder;
     MSIFILE *file;
 
-    TRACE("%p %s %s\n",package, debugstr_w(szFolder),debugstr_w(szFolderPath));
+    TRACE("%p %s %s\n", package, debugstr_w(szFolder), debugstr_w(szFolderPath));
 
     attrib = GetFileAttributesW(szFolderPath);
     /* native MSI tests writeability by making temporary files at each drive */
-    if ( attrib != INVALID_FILE_ATTRIBUTES &&
-          (attrib & FILE_ATTRIBUTE_OFFLINE ||
-           attrib & FILE_ATTRIBUTE_READONLY))
+    if (attrib != INVALID_FILE_ATTRIBUTES &&
+        (attrib & FILE_ATTRIBUTE_OFFLINE || attrib & FILE_ATTRIBUTE_READONLY))
+    {
         return ERROR_FUNCTION_FAILED;
+    }
+    if (!(folder = get_loaded_folder( package, szFolder ))) return ERROR_DIRECTORY;
 
-    path = resolve_target_folder( package, szFolder, FALSE, FALSE, &folder );
-    if (!path)
-        return ERROR_DIRECTORY;
-
-    msi_free(folder->Property);
-    folder->Property = build_directory_name(2, szFolderPath, NULL);
-
-    if (!strcmpiW( path, folder->Property ))
+    len = strlenW( szFolderPath );
+    if (len && szFolderPath[len - 1] != '\\')
     {
-        /*
-         *  Resolved Target has not really changed, so just 
-         *  set this folder and do not recalculate everything.
-         */
-        msi_free(folder->ResolvedTarget);
-        folder->ResolvedTarget = NULL;
-        path2 = resolve_target_folder( package, szFolder, TRUE, FALSE, NULL );
-        msi_free(path2);
+        WCHAR *path = msi_alloc( (len + 2) * sizeof(WCHAR) );
+        memcpy( path, szFolderPath, len * sizeof(WCHAR) );
+        path[len] = '\\';
+        path[len + 1] = 0;
+        set_target_path( package, folder, path );
+        msi_free( path );
     }
-    else
-    {
-        MSIFOLDER *f;
-
-        LIST_FOR_EACH_ENTRY( f, &package->folders, MSIFOLDER, entry )
-        {
-            msi_free(f->ResolvedTarget);
-            f->ResolvedTarget=NULL;
-        }
+    else set_target_path( package, folder, szFolderPath );
 
-        LIST_FOR_EACH_ENTRY( f, &package->folders, MSIFOLDER, entry )
-        {
-            path2 = resolve_target_folder( package, f->Directory, TRUE, FALSE, NULL );
-            msi_free(path2);
-        }
-
-        LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
-        {
-            MSICOMPONENT *comp = file->Component;
-            LPWSTR dir;
-
-            if (!comp->Enabled || (comp->assembly && !comp->assembly->application))
-                continue;
+    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
+    {
+        const WCHAR *dir;
+        MSICOMPONENT *comp = file->Component;
 
-            dir = resolve_target_folder( package, comp->Directory, FALSE, FALSE, NULL );
-            msi_free(file->TargetPath);
+        if (!comp->Enabled || (comp->assembly && !comp->assembly->application)) continue;
 
-            file->TargetPath = build_directory_name(2, dir, file->FileName);
-            msi_free(dir);
-        }
+        dir = msi_get_target_folder( package, comp->Directory );
+        msi_free( file->TargetPath );
+        file->TargetPath = build_directory_name( 2, dir, file->FileName );
     }
-    msi_free(path);
-
     return ERROR_SUCCESS;
 }
 
diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h
index c84dd5d..7758db3 100644
--- a/dlls/msi/msipriv.h
+++ b/dlls/msi/msipriv.h
@@ -489,6 +489,7 @@ typedef struct tagFeatureList
 typedef struct tagMSIFOLDER
 {
     struct list entry;
+    struct list children;
     LPWSTR Directory;
     LPWSTR Parent;
     LPWSTR TargetDefault;
@@ -497,7 +498,6 @@ typedef struct tagMSIFOLDER
 
     LPWSTR ResolvedTarget;
     LPWSTR ResolvedSource;
-    LPWSTR Property;   /* initially set property */
     INT   State;
         /* 0 = uninitialized */
         /* 1 = existing */
@@ -507,6 +507,12 @@ typedef struct tagMSIFOLDER
     INT   Space;
 } MSIFOLDER;
 
+typedef struct tagFolderList
+{
+    struct list entry;
+    MSIFOLDER *folder;
+} FolderList;
+
 typedef enum _msi_file_state {
     msifs_invalid,
     msifs_missing,
@@ -969,7 +975,8 @@ extern UINT msi_set_property( MSIDATABASE *, LPCWSTR, LPCWSTR ) DECLSPEC_HIDDEN;
 extern UINT msi_get_property( MSIDATABASE *, LPCWSTR, LPWSTR, LPDWORD ) DECLSPEC_HIDDEN;
 extern int msi_get_property_int( MSIDATABASE *package, LPCWSTR prop, int def ) DECLSPEC_HIDDEN;
 extern LPWSTR resolve_source_folder(MSIPACKAGE *package, LPCWSTR name, MSIFOLDER **folder) DECLSPEC_HIDDEN;
-extern LPWSTR resolve_target_folder(MSIPACKAGE *package, LPCWSTR name, BOOL set_prop, BOOL load_prop, MSIFOLDER **folder) DECLSPEC_HIDDEN;
+extern void msi_resolve_target_folder(MSIPACKAGE *package, const WCHAR *name, BOOL load_prop) DECLSPEC_HIDDEN;
+extern void msi_clean_path( WCHAR *p ) DECLSPEC_HIDDEN;
 extern LPWSTR resolve_file_source(MSIPACKAGE *package, MSIFILE *file) DECLSPEC_HIDDEN;
 extern const WCHAR *msi_get_target_folder(MSIPACKAGE *package, const WCHAR *name) DECLSPEC_HIDDEN;
 extern void msi_reset_folders( MSIPACKAGE *package, BOOL source ) DECLSPEC_HIDDEN;
@@ -1045,6 +1052,7 @@ extern void ui_actiondata(MSIPACKAGE *, LPCWSTR, MSIRECORD *) DECLSPEC_HIDDEN;
 static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
 static const WCHAR cszSOURCEDIR[] = {'S','O','U','R','C','E','D','I','R',0};
 static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
+static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
 static const WCHAR szLocalSid[] = {'S','-','1','-','5','-','1','8',0};
 static const WCHAR szEmpty[] = {0};
 static const WCHAR szAll[] = {'A','L','L',0};
diff --git a/dlls/msi/package.c b/dlls/msi/package.c
index 7355b3d..9bb5526 100644
--- a/dlls/msi/package.c
+++ b/dlls/msi/package.c
@@ -90,6 +90,26 @@ static void free_feature( MSIFEATURE *feature )
     msi_free( feature );
 }
 
+static void free_folder( MSIFOLDER *folder )
+{
+    struct list *item, *cursor;
+
+    LIST_FOR_EACH_SAFE( item, cursor, &folder->children )
+    {
+        FolderList *fl = LIST_ENTRY( item, FolderList, entry );
+        list_remove( &fl->entry );
+        msi_free( fl );
+    }
+    msi_free( folder->Parent );
+    msi_free( folder->Directory );
+    msi_free( folder->TargetDefault );
+    msi_free( folder->SourceLongPath );
+    msi_free( folder->SourceShortPath );
+    msi_free( folder->ResolvedTarget );
+    msi_free( folder->ResolvedSource );
+    msi_free( folder );
+}
+
 static void free_extension( MSIEXTENSION *ext )
 {
     struct list *item, *cursor;
@@ -140,17 +160,8 @@ static void free_package_structures( MSIPACKAGE *package )
     LIST_FOR_EACH_SAFE( item, cursor, &package->folders )
     {
         MSIFOLDER *folder = LIST_ENTRY( item, MSIFOLDER, entry );
-
         list_remove( &folder->entry );
-        msi_free( folder->Parent );
-        msi_free( folder->Directory );
-        msi_free( folder->TargetDefault );
-        msi_free( folder->SourceLongPath );
-        msi_free( folder->SourceShortPath );
-        msi_free( folder->ResolvedTarget );
-        msi_free( folder->ResolvedSource );
-        msi_free( folder->Property );
-        msi_free( folder );
+        free_folder( folder );
     }
 
     LIST_FOR_EACH_SAFE( item, cursor, &package->components )
diff --git a/dlls/msi/tests/package.c b/dlls/msi/tests/package.c
index 8d70d1c..6060ea4 100644
--- a/dlls/msi/tests/package.c
+++ b/dlls/msi/tests/package.c
@@ -1164,6 +1164,14 @@ static void test_settargetpath(void)
     ok( r == ERROR_SUCCESS, "failed to get target path: %d\n", r);
     ok( !lstrcmpi(buffer, "C:\\one\\two\\three\\"), "Expected C:\\one\\two\\three\\, got %s\n", buffer);
 
+    r = MsiSetTargetPath( hpkg, "TestParent", "C:\\\\one\\\\two  " );
+    ok( r == ERROR_SUCCESS, "MsiSetTargetPath returned %d\n", r );
+
+    sz = sizeof buffer - 1;
+    r = MsiGetTargetPath( hpkg, "TestParent", buffer, &sz );
+    ok( r == ERROR_SUCCESS, "failed to get target path: %d\n", r);
+    ok( !lstrcmpi(buffer, "C:\\one\\two\\"), "Expected \"C:\\one\\two\\\", got %s\n", buffer);
+
     MsiCloseHandle( hpkg );
 }
 
-- 
1.7.4.1







More information about the wine-patches mailing list