| /* | |
| * 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, | |
| MSIRECORD *rec ) | |
| { | |
| MSIORDERVIEW *ov = (MSIORDERVIEW*)view; | |
| TRACE("%p %d %p\n", ov, eModifyMode, rec ); | |
| if( !ov->table ) | |
| return ERROR_FUNCTION_FAILED; | |
| return ov->table->ops->modify( ov->table, eModifyMode, rec ); | |
| } | |
| 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; | |
| } |