Partial implementation of the Microsoft Installer (msi.dll).
[wine] / dlls / msi / order.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002 Mike McCormack for Codeweavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "windef.h"
22 #include "winbase.h"
23 #include "winerror.h"
24 #include "wine/debug.h"
25 #include "msi.h"
26 #include "msiquery.h"
27 #include "objbase.h"
28 #include "objidl.h"
29 #include "msipriv.h"
30 #include "winnls.h"
31
32 #include "query.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(msi);
35
36
37 /* below is the query interface to a table */
38
39 typedef struct tagMSIORDERVIEW
40 {
41     MSIVIEW        view;
42     MSIDATABASE   *db;
43     MSIVIEW       *table;
44     UINT          *reorder;
45     UINT           num_cols;
46     UINT           cols[1];
47 } MSIORDERVIEW;
48
49 static UINT ORDER_compare( MSIORDERVIEW *ov, UINT a, UINT b, UINT *swap )
50 {
51     UINT r, i, a_val = 0, b_val = 0;
52
53     *swap = 0;
54     for( i=0; i<ov->num_cols; i++ )
55     {
56         r = ov->table->ops->fetch_int( ov->table, a, ov->cols[i], &a_val );
57         if( r != ERROR_SUCCESS )
58             return r;
59
60         r = ov->table->ops->fetch_int( ov->table, b, ov->cols[i], &b_val );
61         if( r != ERROR_SUCCESS )
62             return r;
63
64         if( a_val != b_val )
65         {
66             if( a_val > b_val )
67                 *swap = 1;
68             break;
69         }
70     }
71
72     return ERROR_SUCCESS;
73 }
74
75 static UINT ORDER_mergesort( MSIORDERVIEW *ov, UINT left, UINT right )
76 {
77     UINT r, centre = (left + right)/2, temp, swap = 0, i, j;
78     UINT *array = ov->reorder;
79
80     if( left == right )
81         return ERROR_SUCCESS;
82
83     /* sort the left half */
84     r = ORDER_mergesort( ov, left, centre );
85     if( r != ERROR_SUCCESS )
86         return r;
87
88     /* sort the right half */
89     r = ORDER_mergesort( ov, centre+1, right );
90     if( r != ERROR_SUCCESS )
91         return r;
92
93     for( i=left, j=centre+1; (i<=centre) && (j<=right); i++ )
94     {
95         r = ORDER_compare( ov, array[i], array[j], &swap );
96         if( r != ERROR_SUCCESS )
97             return r;
98         if( swap )
99         { 
100             temp = array[j];
101             memmove( &array[i+1], &array[i], (j-i)*sizeof (UINT) );
102             array[i] = temp;
103             j++;
104             centre++;
105         }
106     }
107
108     return ERROR_SUCCESS;
109 }
110
111 static UINT ORDER_verify( MSIORDERVIEW *ov, UINT num_rows )
112 {
113     UINT i, swap, r;
114
115     for( i=1; i<num_rows; i++ )
116     {
117         r = ORDER_compare( ov, ov->reorder[i-1], ov->reorder[i], &swap );
118         if( r != ERROR_SUCCESS )
119             return r;
120         if( !swap )
121             continue;
122         ERR("Bad order! %d\n", i);
123         return ERROR_FUNCTION_FAILED;
124     }
125
126     return ERROR_SUCCESS;
127 }
128
129 static UINT ORDER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
130 {
131     MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
132
133     TRACE("%p %d %d %p\n", ov, row, col, val );
134
135     if( !ov->table )
136          return ERROR_FUNCTION_FAILED;
137
138     row = ov->reorder[ row ];
139
140     return ov->table->ops->fetch_int( ov->table, row, col, val );
141 }
142
143 static UINT ORDER_execute( struct tagMSIVIEW *view, MSIHANDLE record )
144 {
145     MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
146     UINT r, num_rows = 0, i;
147
148     TRACE("%p %ld\n", ov, record);
149
150     if( !ov->table )
151          return ERROR_FUNCTION_FAILED;
152
153     r = ov->table->ops->execute( ov->table, record );
154     if( r != ERROR_SUCCESS )
155         return r;
156
157     r = ov->table->ops->get_dimensions( ov->table, &num_rows, NULL );
158     if( r != ERROR_SUCCESS )
159         return r;
160
161     ov->reorder = HeapAlloc( GetProcessHeap(), 0, num_rows*sizeof(UINT) );
162     if( !ov->reorder )
163         return ERROR_FUNCTION_FAILED;
164
165     for( i=0; i<num_rows; i++ )
166         ov->reorder[i] = i;
167
168     r = ORDER_mergesort( ov, 0, num_rows - 1 );
169     if( r != ERROR_SUCCESS )
170         return r;
171
172     r = ORDER_verify( ov, num_rows );
173     if( r != ERROR_SUCCESS )
174         return r;
175
176     return ERROR_SUCCESS;
177 }
178
179 static UINT ORDER_close( struct tagMSIVIEW *view )
180 {
181     MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
182
183     TRACE("%p\n", ov );
184
185     if( !ov->table )
186          return ERROR_FUNCTION_FAILED;
187
188     if( ov->reorder )
189         HeapFree( GetProcessHeap(), 0, ov->reorder );
190     ov->reorder = NULL;
191
192     return ov->table->ops->close( ov->table );
193 }
194
195 static UINT ORDER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
196 {
197     MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
198
199     TRACE("%p %p %p\n", ov, rows, cols );
200
201     if( !ov->table )
202          return ERROR_FUNCTION_FAILED;
203
204     return ov->table->ops->get_dimensions( ov->table, rows, cols );
205 }
206
207 static UINT ORDER_get_column_info( struct tagMSIVIEW *view,
208                 UINT n, LPWSTR *name, UINT *type )
209 {
210     MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
211
212     TRACE("%p %d %p %p\n", ov, n, name, type );
213
214     if( !ov->table )
215          return ERROR_FUNCTION_FAILED;
216
217     return ov->table->ops->get_column_info( ov->table, n, name, type );
218 }
219
220 static UINT ORDER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
221 {
222     MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
223
224     TRACE("%p %d %ld\n", ov, eModifyMode, hrec );
225
226     if( !ov->table )
227          return ERROR_FUNCTION_FAILED;
228
229     return ov->table->ops->modify( ov->table, eModifyMode, hrec );
230 }
231
232 static UINT ORDER_delete( struct tagMSIVIEW *view )
233 {
234     MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
235
236     TRACE("%p\n", ov );
237
238     if( ov->table )
239         ov->table->ops->delete( ov->table );
240
241     if( ov->reorder )
242         HeapFree( GetProcessHeap(), 0, ov->reorder );
243     ov->reorder = NULL;
244
245     HeapFree( GetProcessHeap(), 0, ov );
246
247     return ERROR_SUCCESS;
248 }
249
250
251 MSIVIEWOPS order_ops =
252 {
253     ORDER_fetch_int,
254     ORDER_execute,
255     ORDER_close,
256     ORDER_get_dimensions,
257     ORDER_get_column_info,
258     ORDER_modify,
259     ORDER_delete
260 };
261
262 UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
263 {
264     MSIORDERVIEW *ov = NULL;
265     UINT count = 0, r;
266
267     TRACE("%p\n", ov );
268
269     r = table->ops->get_dimensions( table, NULL, &count );
270     if( r != ERROR_SUCCESS )
271     {
272         ERR("can't get table dimensions\n");
273         return r;
274     }
275
276     ov = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
277                     sizeof *ov + sizeof (UINT) * count );
278     if( !ov )
279         return ERROR_FUNCTION_FAILED;
280     
281     /* fill the structure */
282     ov->view.ops = &order_ops;
283     ov->db = db;
284     ov->table = table;
285     ov->reorder = NULL;
286     ov->num_cols = 0;
287     *view = (MSIVIEW*) ov;
288
289     return ERROR_SUCCESS;
290 }
291
292 UINT ORDER_AddColumn( MSIVIEW *view, LPWSTR name )
293 {
294     MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
295     UINT n, count, r;
296     MSIVIEW *table;
297
298     TRACE("%p adding %s\n", ov, debugstr_w( name ) );
299
300     if( ov->view.ops != &order_ops )
301         return ERROR_FUNCTION_FAILED;
302
303     table = ov->table;
304     if( !table )
305         return ERROR_FUNCTION_FAILED;
306     if( !table->ops->get_dimensions )
307         return ERROR_FUNCTION_FAILED;
308     if( !table->ops->get_column_info )
309         return ERROR_FUNCTION_FAILED;
310
311     r = table->ops->get_dimensions( table, NULL, &count );
312     if( r != ERROR_SUCCESS )
313         return r;
314
315     if( ov->num_cols >= count )
316         return ERROR_FUNCTION_FAILED;
317
318     r = VIEW_find_column( table, name, &n );
319     if( r != ERROR_SUCCESS )
320         return r;
321
322     ov->cols[ov->num_cols] = n;
323     TRACE("Ordering by column %s (%d)\n", debugstr_w( name ), n);
324
325     ov->num_cols++;
326
327     return ERROR_SUCCESS;
328 }