334 lines
7.8 KiB
C
334 lines
7.8 KiB
C
/*
|
|
* Implementation of the Microsoft Installer (msi.dll)
|
|
*
|
|
* Copyright 2002 Mike McCormack for CodeWeavers
|
|
*
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winerror.h"
|
|
#include "wine/debug.h"
|
|
#include "msi.h"
|
|
#include "msiquery.h"
|
|
#include "objbase.h"
|
|
#include "objidl.h"
|
|
#include "msipriv.h"
|
|
#include "winnls.h"
|
|
|
|
#include "query.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(msi);
|
|
|
|
|
|
/* below is the query interface to a table */
|
|
|
|
typedef struct tagMSIORDERVIEW
|
|
{
|
|
MSIVIEW view;
|
|
MSIDATABASE *db;
|
|
MSIVIEW *table;
|
|
UINT *reorder;
|
|
UINT num_cols;
|
|
UINT cols[1];
|
|
} MSIORDERVIEW;
|
|
|
|
static UINT ORDER_compare( MSIORDERVIEW *ov, UINT a, UINT b, UINT *swap )
|
|
{
|
|
UINT r, i, a_val = 0, b_val = 0;
|
|
|
|
*swap = 0;
|
|
for( i=0; i<ov->num_cols; i++ )
|
|
{
|
|
r = ov->table->ops->fetch_int( ov->table, a, ov->cols[i], &a_val );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
r = ov->table->ops->fetch_int( ov->table, b, ov->cols[i], &b_val );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
if( a_val != b_val )
|
|
{
|
|
if( a_val > b_val )
|
|
*swap = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static UINT ORDER_mergesort( MSIORDERVIEW *ov, UINT left, UINT right )
|
|
{
|
|
UINT r, centre = (left + right)/2, temp, swap = 0, i, j;
|
|
UINT *array = ov->reorder;
|
|
|
|
if( left == right )
|
|
return ERROR_SUCCESS;
|
|
|
|
/* sort the left half */
|
|
r = ORDER_mergesort( ov, left, centre );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
/* sort the right half */
|
|
r = ORDER_mergesort( ov, centre+1, right );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
for( i=left, j=centre+1; (i<=centre) && (j<=right); i++ )
|
|
{
|
|
r = ORDER_compare( ov, array[i], array[j], &swap );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
if( swap )
|
|
{
|
|
temp = array[j];
|
|
memmove( &array[i+1], &array[i], (j-i)*sizeof (UINT) );
|
|
array[i] = temp;
|
|
j++;
|
|
centre++;
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static UINT ORDER_verify( MSIORDERVIEW *ov, UINT num_rows )
|
|
{
|
|
UINT i, swap, r;
|
|
|
|
for( i=1; i<num_rows; i++ )
|
|
{
|
|
r = ORDER_compare( ov, ov->reorder[i-1], ov->reorder[i], &swap );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
if( !swap )
|
|
continue;
|
|
ERR("Bad order! %d\n", i);
|
|
return ERROR_FUNCTION_FAILED;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static UINT ORDER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
|
|
{
|
|
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
|
|
|
|
TRACE("%p %d %d %p\n", ov, row, col, val );
|
|
|
|
if( !ov->table )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
row = ov->reorder[ row ];
|
|
|
|
return ov->table->ops->fetch_int( ov->table, row, col, val );
|
|
}
|
|
|
|
static UINT ORDER_execute( struct tagMSIVIEW *view, MSIRECORD *record )
|
|
{
|
|
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
|
|
UINT r, num_rows = 0, i;
|
|
|
|
TRACE("%p %p\n", ov, record);
|
|
|
|
if( !ov->table )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
r = ov->table->ops->execute( ov->table, record );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
r = ov->table->ops->get_dimensions( ov->table, &num_rows, NULL );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
ov->reorder = HeapAlloc( GetProcessHeap(), 0, num_rows*sizeof(UINT) );
|
|
if( !ov->reorder )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
for( i=0; i<num_rows; i++ )
|
|
ov->reorder[i] = i;
|
|
|
|
r = ORDER_mergesort( ov, 0, num_rows - 1 );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
r = ORDER_verify( ov, num_rows );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static UINT ORDER_close( struct tagMSIVIEW *view )
|
|
{
|
|
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
|
|
|
|
TRACE("%p\n", ov );
|
|
|
|
if( !ov->table )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
HeapFree( GetProcessHeap(), 0, ov->reorder );
|
|
ov->reorder = NULL;
|
|
|
|
return ov->table->ops->close( ov->table );
|
|
}
|
|
|
|
static UINT ORDER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
|
|
{
|
|
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
|
|
|
|
TRACE("%p %p %p\n", ov, rows, cols );
|
|
|
|
if( !ov->table )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
return ov->table->ops->get_dimensions( ov->table, rows, cols );
|
|
}
|
|
|
|
static UINT ORDER_get_column_info( struct tagMSIVIEW *view,
|
|
UINT n, LPWSTR *name, UINT *type )
|
|
{
|
|
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
|
|
|
|
TRACE("%p %d %p %p\n", ov, n, name, type );
|
|
|
|
if( !ov->table )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
return ov->table->ops->get_column_info( ov->table, n, name, type );
|
|
}
|
|
|
|
static UINT ORDER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
|
|
{
|
|
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
|
|
|
|
TRACE("%p %d %ld\n", ov, eModifyMode, hrec );
|
|
|
|
if( !ov->table )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
return ov->table->ops->modify( ov->table, eModifyMode, hrec );
|
|
}
|
|
|
|
static UINT ORDER_delete( struct tagMSIVIEW *view )
|
|
{
|
|
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
|
|
|
|
TRACE("%p\n", ov );
|
|
|
|
if( ov->table )
|
|
ov->table->ops->delete( ov->table );
|
|
|
|
HeapFree( GetProcessHeap(), 0, ov->reorder );
|
|
ov->reorder = NULL;
|
|
|
|
msiobj_release( &ov->db->hdr );
|
|
HeapFree( GetProcessHeap(), 0, ov );
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
MSIVIEWOPS order_ops =
|
|
{
|
|
ORDER_fetch_int,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
ORDER_execute,
|
|
ORDER_close,
|
|
ORDER_get_dimensions,
|
|
ORDER_get_column_info,
|
|
ORDER_modify,
|
|
ORDER_delete
|
|
};
|
|
|
|
UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
|
|
{
|
|
MSIORDERVIEW *ov = NULL;
|
|
UINT count = 0, r;
|
|
|
|
TRACE("%p\n", ov );
|
|
|
|
r = table->ops->get_dimensions( table, NULL, &count );
|
|
if( r != ERROR_SUCCESS )
|
|
{
|
|
ERR("can't get table dimensions\n");
|
|
return r;
|
|
}
|
|
|
|
ov = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
|
|
sizeof *ov + sizeof (UINT) * count );
|
|
if( !ov )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
/* fill the structure */
|
|
ov->view.ops = &order_ops;
|
|
msiobj_addref( &db->hdr );
|
|
ov->db = db;
|
|
ov->table = table;
|
|
ov->reorder = NULL;
|
|
ov->num_cols = 0;
|
|
*view = (MSIVIEW*) ov;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
UINT ORDER_AddColumn( MSIVIEW *view, LPWSTR name )
|
|
{
|
|
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
|
|
UINT n, count, r;
|
|
MSIVIEW *table;
|
|
|
|
TRACE("%p adding %s\n", ov, debugstr_w( name ) );
|
|
|
|
if( ov->view.ops != &order_ops )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
table = ov->table;
|
|
if( !table )
|
|
return ERROR_FUNCTION_FAILED;
|
|
if( !table->ops->get_dimensions )
|
|
return ERROR_FUNCTION_FAILED;
|
|
if( !table->ops->get_column_info )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
r = table->ops->get_dimensions( table, NULL, &count );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
if( ov->num_cols >= count )
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
r = VIEW_find_column( table, name, &n );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
ov->cols[ov->num_cols] = n;
|
|
TRACE("Ordering by column %s (%d)\n", debugstr_w( name ), n);
|
|
|
|
ov->num_cols++;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|