~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/setupapi/stringtable.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  1 /*
  2  * Setupapi string table functions
  3  *
  4  * Copyright 2005 Eric Kohl
  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 19  */
 20 
 21 #include "config.h"
 22 #include "wine/port.h"
 23 
 24 #include <stdarg.h>
 25 
 26 #include "windef.h"
 27 #include "winbase.h"
 28 #include "wingdi.h"
 29 #include "winuser.h"
 30 #include "winreg.h"
 31 #include "setupapi.h"
 32 
 33 #include "wine/debug.h"
 34 
 35 
 36 #define TABLE_DEFAULT_SIZE 256
 37 
 38 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
 39 
 40 DECLARE_HANDLE(HSTRING_TABLE);
 41 
 42 typedef struct _TABLE_SLOT
 43 {
 44     LPWSTR pString;
 45     LPVOID pData;
 46     DWORD dwSize;
 47 } TABLE_SLOT, *PTABLE_SLOT;
 48 
 49 typedef struct _STRING_TABLE
 50 {
 51     PTABLE_SLOT pSlots;
 52     DWORD dwUsedSlots;
 53     DWORD dwMaxSlots;
 54     DWORD dwMaxDataSize;
 55 } STRING_TABLE, *PSTRING_TABLE;
 56 
 57 
 58 /**************************************************************************
 59  * StringTableInitialize [SETUPAPI.@]
 60  *
 61  * Creates a new string table and initializes it.
 62  *
 63  * PARAMS
 64  *     None
 65  *
 66  * RETURNS
 67  *     Success: Handle to the string table
 68  *     Failure: NULL
 69  */
 70 HSTRING_TABLE WINAPI
 71 StringTableInitialize(VOID)
 72 {
 73     PSTRING_TABLE pStringTable;
 74 
 75     TRACE("\n");
 76 
 77     pStringTable = MyMalloc(sizeof(STRING_TABLE));
 78     if (pStringTable == NULL)
 79     {
 80         ERR("Invalid hStringTable!\n");
 81         return NULL;
 82     }
 83 
 84     memset(pStringTable, 0, sizeof(STRING_TABLE));
 85 
 86     pStringTable->pSlots = MyMalloc(sizeof(TABLE_SLOT) * TABLE_DEFAULT_SIZE);
 87     if (pStringTable->pSlots == NULL)
 88     {
 89         MyFree(pStringTable);
 90         return NULL;
 91     }
 92 
 93     memset(pStringTable->pSlots, 0, sizeof(TABLE_SLOT) * TABLE_DEFAULT_SIZE);
 94 
 95     pStringTable->dwUsedSlots = 0;
 96     pStringTable->dwMaxSlots = TABLE_DEFAULT_SIZE;
 97     pStringTable->dwMaxDataSize = 0;
 98 
 99     TRACE("Done\n");
100 
101     return (HSTRING_TABLE)pStringTable;
102 }
103 
104 
105 /**************************************************************************
106  * StringTableInitializeEx [SETUPAPI.@]
107  *
108  * Creates a new string table and initializes it.
109  *
110  * PARAMS
111  *     dwMaxExtraDataSize [I] Maximum extra data size
112  *     dwReserved         [I] Unused
113  *
114  * RETURNS
115  *     Success: Handle to the string table
116  *     Failure: NULL
117  */
118 HSTRING_TABLE WINAPI
119 StringTableInitializeEx(DWORD dwMaxExtraDataSize,
120                         DWORD dwReserved)
121 {
122     PSTRING_TABLE pStringTable;
123 
124     TRACE("\n");
125 
126     pStringTable = MyMalloc(sizeof(STRING_TABLE));
127     if (pStringTable == NULL) return NULL;
128 
129     memset(pStringTable, 0, sizeof(STRING_TABLE));
130 
131     pStringTable->pSlots = MyMalloc(sizeof(TABLE_SLOT) * TABLE_DEFAULT_SIZE);
132     if (pStringTable->pSlots == NULL)
133     {
134         MyFree(pStringTable);
135         return NULL;
136     }
137 
138     memset(pStringTable->pSlots, 0, sizeof(TABLE_SLOT) * TABLE_DEFAULT_SIZE);
139 
140     pStringTable->dwUsedSlots = 0;
141     pStringTable->dwMaxSlots = TABLE_DEFAULT_SIZE;
142     pStringTable->dwMaxDataSize = dwMaxExtraDataSize;
143 
144     TRACE("Done\n");
145 
146     return (HSTRING_TABLE)pStringTable;
147 }
148 
149 
150 /**************************************************************************
151  * StringTableDestroy [SETUPAPI.@]
152  *
153  * Destroys a string table.
154  *
155  * PARAMS
156  *     hStringTable [I] Handle to the string table to be destroyed
157  *
158  * RETURNS
159  *     None
160  */
161 VOID WINAPI
162 StringTableDestroy(HSTRING_TABLE hStringTable)
163 {
164     PSTRING_TABLE pStringTable;
165     DWORD i;
166 
167     TRACE("%p\n", hStringTable);
168 
169     pStringTable = (PSTRING_TABLE)hStringTable;
170     if (pStringTable == NULL)
171         return;
172 
173     if (pStringTable->pSlots != NULL)
174     {
175         for (i = 0; i < pStringTable->dwMaxSlots; i++)
176         {
177             MyFree(pStringTable->pSlots[i].pString);
178             pStringTable->pSlots[i].pString = NULL;
179 
180             MyFree(pStringTable->pSlots[i].pData);
181             pStringTable->pSlots[i].pData = NULL;
182             pStringTable->pSlots[i].dwSize = 0;
183         }
184 
185         MyFree(pStringTable->pSlots);
186     }
187 
188     MyFree(pStringTable);
189 }
190 
191 
192 /**************************************************************************
193  * StringTableAddStringEx [SETUPAPI.@]
194  *
195  * Adds a new string plus extra data to the string table.
196  *
197  * PARAMS
198  *     hStringTable    [I] Handle to the string table
199  *     lpString        [I] String to be added to the string table
200  *     dwFlags         [I] Flags
201  *                           1: case sensitive compare
202  *     lpExtraData     [I] Pointer to the extra data
203  *     dwExtraDataSize [I] Size of the extra data
204  *
205  * RETURNS
206  *     Success: String ID
207  *     Failure: ~0u
208  *
209  * NOTES
210  *     If the given string already exists in the string table it will not
211  *     be added again. The ID of the existing string will be returned in
212  *     this case.
213  */
214 DWORD WINAPI
215 StringTableAddStringEx(HSTRING_TABLE hStringTable, LPWSTR lpString,
216                        DWORD dwFlags, LPVOID lpExtraData, DWORD dwExtraDataSize)
217 {
218     PSTRING_TABLE pStringTable;
219     DWORD i;
220 
221     TRACE("%p %s %x %p, %u\n", hStringTable, debugstr_w(lpString), dwFlags,
222           lpExtraData, dwExtraDataSize);
223 
224     pStringTable = (PSTRING_TABLE)hStringTable;
225     if (!pStringTable)
226     {
227         ERR("Invalid hStringTable!\n");
228         return ~0u;
229     }
230 
231     /* Search for existing string in the string table */
232     for (i = 0; i < pStringTable->dwMaxSlots; i++)
233     {
234         if (pStringTable->pSlots[i].pString)
235         {
236             if (dwFlags & 1)
237             {
238                 if (!lstrcmpW(pStringTable->pSlots[i].pString, lpString))
239                     return i + 1;
240             }
241             else
242             {
243                 if (!lstrcmpiW(pStringTable->pSlots[i].pString, lpString))
244                     return i + 1;
245             }
246         }
247     }
248 
249     /* Check for filled slot table */
250     if (pStringTable->dwUsedSlots == pStringTable->dwMaxSlots)
251     {
252         FIXME("Resize the string table!\n");
253         return ~0u;
254     }
255 
256     /* Search for an empty slot */
257     for (i = 0; i < pStringTable->dwMaxSlots; i++)
258     {
259         if (!pStringTable->pSlots[i].pString)
260         {
261             pStringTable->pSlots[i].pString = MyMalloc((lstrlenW(lpString) + 1) * sizeof(WCHAR));
262             if (!pStringTable->pSlots[i].pString)
263             {
264                 WARN("Couldn't allocate memory for a new string!\n");
265                 return ~0u;
266             }
267             lstrcpyW(pStringTable->pSlots[i].pString, lpString);
268 
269             pStringTable->pSlots[i].pData = MyMalloc(dwExtraDataSize);
270             if (!pStringTable->pSlots[i].pData)
271             {
272                 TRACE("Couldn't allocate memory for data!\n");
273                 return ~0u;
274             }
275             memcpy(pStringTable->pSlots[i].pData, lpExtraData, dwExtraDataSize);
276             pStringTable->pSlots[i].dwSize = dwExtraDataSize;
277             pStringTable->dwUsedSlots++;
278             return i + 1;
279         }
280     }
281     TRACE("Couldn't find an empty slot!\n");
282     return ~0u;
283 }
284 
285 /**************************************************************************
286  * StringTableAddString [SETUPAPI.@]
287  *
288  * Adds a new string to the string table.
289  *
290  * PARAMS
291  *     hStringTable [I] Handle to the string table
292  *     lpString     [I] String to be added to the string table
293  *     dwFlags      [I] Flags
294  *                        1: case sensitive compare
295  *
296  * RETURNS
297  *     Success: String ID
298  *     Failure: ~0u
299  *
300  * NOTES
301  *     If the given string already exists in the string table it will not
302  *     be added again. The ID of the existing string will be returned in
303  *     this case.
304  */
305 DWORD WINAPI
306 StringTableAddString(HSTRING_TABLE hStringTable, LPWSTR lpString, DWORD dwFlags)
307 {
308     return StringTableAddStringEx(hStringTable, lpString, dwFlags, NULL, 0);
309 }
310 
311 
312 /**************************************************************************
313  * StringTableDuplicate [SETUPAPI.@]
314  *
315  * Duplicates a given string table.
316  *
317  * PARAMS
318  *     hStringTable [I] Handle to the string table
319  *
320  * RETURNS
321  *     Success: Handle to the duplicated string table
322  *     Failure: NULL
323  *
324  */
325 HSTRING_TABLE WINAPI
326 StringTableDuplicate(HSTRING_TABLE hStringTable)
327 {
328     PSTRING_TABLE pSourceTable;
329     PSTRING_TABLE pDestinationTable;
330     DWORD i;
331     DWORD length;
332 
333     TRACE("%p\n", hStringTable);
334 
335     pSourceTable = (PSTRING_TABLE)hStringTable;
336     if (pSourceTable == NULL)
337     {
338         ERR("Invalid hStringTable!\n");
339         return NULL;
340     }
341 
342     pDestinationTable = MyMalloc(sizeof(STRING_TABLE));
343     if (pDestinationTable == NULL)
344     {
345         ERR("Could not allocate a new string table!\n");
346         return NULL;
347     }
348 
349     memset(pDestinationTable, 0, sizeof(STRING_TABLE));
350 
351     pDestinationTable->pSlots = MyMalloc(sizeof(TABLE_SLOT) * pSourceTable->dwMaxSlots);
352     if (pDestinationTable->pSlots == NULL)
353     {
354         MyFree(pDestinationTable);
355         return NULL;
356     }
357 
358     memset(pDestinationTable->pSlots, 0, sizeof(TABLE_SLOT) * pSourceTable->dwMaxSlots);
359 
360     pDestinationTable->dwUsedSlots = 0;
361     pDestinationTable->dwMaxSlots = pSourceTable->dwMaxSlots;
362 
363     for (i = 0; i < pSourceTable->dwMaxSlots; i++)
364     {
365         if (pSourceTable->pSlots[i].pString != NULL)
366         {
367             length = (lstrlenW(pSourceTable->pSlots[i].pString) + 1) * sizeof(WCHAR);
368             pDestinationTable->pSlots[i].pString = MyMalloc(length);
369             if (pDestinationTable->pSlots[i].pString != NULL)
370             {
371                 memcpy(pDestinationTable->pSlots[i].pString,
372                        pSourceTable->pSlots[i].pString,
373                        length);
374                 pDestinationTable->dwUsedSlots++;
375             }
376 
377             if (pSourceTable->pSlots[i].pData != NULL)
378             {
379                 length = pSourceTable->pSlots[i].dwSize;
380                 pDestinationTable->pSlots[i].pData = MyMalloc(length);
381                 if (pDestinationTable->pSlots[i].pData)
382                 {
383                     memcpy(pDestinationTable->pSlots[i].pData,
384                            pSourceTable->pSlots[i].pData,
385                            length);
386                     pDestinationTable->pSlots[i].dwSize = length;
387                 }
388             }
389         }
390     }
391 
392     return (HSTRING_TABLE)pDestinationTable;
393 }
394 
395 
396 /**************************************************************************
397  * StringTableGetExtraData [SETUPAPI.@]
398  *
399  * Retrieves extra data from a given string table entry.
400  *
401  * PARAMS
402  *     hStringTable    [I] Handle to the string table
403  *     dwId            [I] String ID
404  *     lpExtraData     [I] Pointer a buffer that receives the extra data
405  *     dwExtraDataSize [I] Size of the buffer
406  *
407  * RETURNS
408  *     Success: TRUE
409  *     Failure: FALSE
410  */
411 BOOL WINAPI
412 StringTableGetExtraData(HSTRING_TABLE hStringTable,
413                         DWORD dwId,
414                         LPVOID lpExtraData,
415                         DWORD dwExtraDataSize)
416 {
417     PSTRING_TABLE pStringTable;
418 
419     TRACE("%p %x %p %u\n",
420           hStringTable, dwId, lpExtraData, dwExtraDataSize);
421 
422     pStringTable = (PSTRING_TABLE)hStringTable;
423     if (pStringTable == NULL)
424     {
425         ERR("Invalid hStringTable!\n");
426         return FALSE;
427     }
428 
429     if (dwId == 0 || dwId > pStringTable->dwMaxSlots)
430     {
431         ERR("Invalid Slot id!\n");
432         return FALSE;
433     }
434 
435     if (pStringTable->pSlots[dwId - 1].dwSize > dwExtraDataSize)
436     {
437         ERR("Data size is too large!\n");
438         return FALSE;
439     }
440 
441     memcpy(lpExtraData,
442            pStringTable->pSlots[dwId - 1].pData,
443            dwExtraDataSize);
444 
445     return TRUE;
446 }
447 
448 
449 /**************************************************************************
450  * StringTableLookUpStringEx [SETUPAPI.@]
451  *
452  * Searches a string table and extra data for a given string.
453  *
454  * PARAMS
455  *     hStringTable [I] Handle to the string table
456  *     lpString     [I] String to be searched for
457  *     dwFlags      [I] Flags
458  *                        1: case sensitive compare
459  *     lpExtraData  [O] Pointer to the buffer that receives the extra data
460  *     dwReserved   [I/O] Unused
461  *
462  * RETURNS
463  *     Success: String ID
464  *     Failure: -1
465  */
466 DWORD WINAPI
467 StringTableLookUpStringEx(HSTRING_TABLE hStringTable,
468                           LPWSTR lpString,
469                           DWORD dwFlags,
470                           LPVOID lpExtraData,
471                           DWORD dwReserved)
472 {
473     PSTRING_TABLE pStringTable;
474     DWORD i;
475 
476     TRACE("%p %s %x %p, %x\n", hStringTable, debugstr_w(lpString), dwFlags,
477           lpExtraData, dwReserved);
478 
479     pStringTable = (PSTRING_TABLE)hStringTable;
480     if (pStringTable == NULL)
481     {
482         ERR("Invalid hStringTable!\n");
483         return ~0u;
484     }
485 
486     /* Search for existing string in the string table */
487     for (i = 0; i < pStringTable->dwMaxSlots; i++)
488     {
489         if (pStringTable->pSlots[i].pString != NULL)
490         {
491             if (dwFlags & 1)
492             {
493                 if (!lstrcmpW(pStringTable->pSlots[i].pString, lpString))
494                 {
495                     if (lpExtraData)
496                         memcpy(lpExtraData, pStringTable->pSlots[i].pData, dwReserved);
497                     return i + 1;
498                 }
499             }
500             else
501             {
502                 if (!lstrcmpiW(pStringTable->pSlots[i].pString, lpString))
503                 {
504                     if (lpExtraData)
505                         memcpy(lpExtraData, pStringTable->pSlots[i].pData, dwReserved);
506                     return i + 1;
507                 }
508             }
509         }
510     }
511     return ~0u;
512 }
513 
514 
515 /**************************************************************************
516  * StringTableLookUpString [SETUPAPI.@]
517  *
518  * Searches a string table for a given string.
519  *
520  * PARAMS
521  *     hStringTable [I] Handle to the string table
522  *     lpString     [I] String to be searched for
523  *     dwFlags      [I] Flags
524  *                        1: case sensitive compare
525  *
526  * RETURNS
527  *     Success: String ID
528  *     Failure: ~0u
529  */
530 DWORD WINAPI
531 StringTableLookUpString(HSTRING_TABLE hStringTable,
532                         LPWSTR lpString,
533                         DWORD dwFlags)
534 {
535     return StringTableLookUpStringEx(hStringTable, lpString, dwFlags, NULL, 0);
536 }
537 
538 
539 /**************************************************************************
540  * StringTableSetExtraData [SETUPAPI.@]
541  *
542  * Sets extra data for a given string table entry.
543  *
544  * PARAMS
545  *     hStringTable    [I] Handle to the string table
546  *     dwId            [I] String ID
547  *     lpExtraData     [I] Pointer to the extra data
548  *     dwExtraDataSize [I] Size of the extra data
549  *
550  * RETURNS
551  *     Success: TRUE
552  *     Failure: FALSE
553  */
554 BOOL WINAPI
555 StringTableSetExtraData(HSTRING_TABLE hStringTable,
556                         DWORD dwId,
557                         LPVOID lpExtraData,
558                         DWORD dwExtraDataSize)
559 {
560     PSTRING_TABLE pStringTable;
561 
562     TRACE("%p %x %p %u\n",
563           hStringTable, dwId, lpExtraData, dwExtraDataSize);
564 
565     pStringTable = (PSTRING_TABLE)hStringTable;
566     if (pStringTable == NULL)
567     {
568         ERR("Invalid hStringTable!\n");
569         return FALSE;
570     }
571 
572     if (dwId == 0 || dwId > pStringTable->dwMaxSlots)
573     {
574         ERR("Invalid Slot id!\n");
575         return FALSE;
576     }
577 
578     if (pStringTable->dwMaxDataSize < dwExtraDataSize)
579     {
580         ERR("Data size is too large!\n");
581         return FALSE;
582     }
583 
584     pStringTable->pSlots[dwId - 1].pData = MyMalloc(dwExtraDataSize);
585     if (pStringTable->pSlots[dwId - 1].pData == NULL)
586     {
587         ERR("\n");
588         return FALSE;
589     }
590 
591     memcpy(pStringTable->pSlots[dwId - 1].pData,
592            lpExtraData,
593            dwExtraDataSize);
594     pStringTable->pSlots[dwId - 1].dwSize = dwExtraDataSize;
595 
596     return TRUE;
597 }
598 
599 
600 /**************************************************************************
601  * StringTableStringFromId [SETUPAPI.@]
602  *
603  * Returns a pointer to a string for the given string ID.
604  *
605  * PARAMS
606  *     hStringTable [I] Handle to the string table.
607  *     dwId         [I] String ID
608  *
609  * RETURNS
610  *     Success: Pointer to the string
611  *     Failure: NULL
612  */
613 LPWSTR WINAPI
614 StringTableStringFromId(HSTRING_TABLE hStringTable,
615                         DWORD dwId)
616 {
617     PSTRING_TABLE pStringTable;
618     static WCHAR empty[] = {0};
619 
620     TRACE("%p %x\n", hStringTable, dwId);
621 
622     pStringTable = (PSTRING_TABLE)hStringTable;
623     if (pStringTable == NULL)
624     {
625         ERR("Invalid hStringTable!\n");
626         return NULL;
627     }
628 
629     if (dwId == 0 || dwId > pStringTable->dwMaxSlots)
630         return empty;
631 
632     return pStringTable->pSlots[dwId - 1].pString;
633 }
634 
635 
636 /**************************************************************************
637  * StringTableStringFromIdEx [SETUPAPI.@]
638  *
639  * Returns a string for the given string ID.
640  *
641  * PARAMS
642  *     hStringTable [I] Handle to the string table
643  *     dwId         [I] String ID
644  *     lpBuffer     [I] Pointer to string buffer
645  *     lpBufferSize [I/O] Pointer to the size of the string buffer
646  *
647  * RETURNS
648  *     Success: TRUE
649  *     Failure: FALSE
650  */
651 BOOL WINAPI
652 StringTableStringFromIdEx(HSTRING_TABLE hStringTable,
653                           DWORD dwId,
654                           LPWSTR lpBuffer,
655                           LPDWORD lpBufferLength)
656 {
657     PSTRING_TABLE pStringTable;
658     DWORD dwLength;
659     BOOL bResult = FALSE;
660 
661     TRACE("%p %x %p %p\n", hStringTable, dwId, lpBuffer, lpBufferLength);
662 
663     pStringTable = (PSTRING_TABLE)hStringTable;
664     if (pStringTable == NULL)
665     {
666         ERR("Invalid hStringTable!\n");
667         *lpBufferLength = 0;
668         return FALSE;
669     }
670 
671     if (dwId == 0 || dwId > pStringTable->dwMaxSlots ||
672         pStringTable->pSlots[dwId - 1].pString == NULL)
673     {
674         WARN("Invalid string ID!\n");
675         *lpBufferLength = 0;
676         return FALSE;
677     }
678 
679     dwLength = (lstrlenW(pStringTable->pSlots[dwId - 1].pString) + 1) * sizeof(WCHAR);
680     if (dwLength <= *lpBufferLength)
681     {
682         lstrcpyW(lpBuffer, pStringTable->pSlots[dwId - 1].pString);
683         bResult = TRUE;
684     }
685 
686     *lpBufferLength = dwLength;
687 
688     return bResult;
689 }
690 
691 
692 /**************************************************************************
693  * StringTableTrim [SETUPAPI.@]
694  *
695  * ...
696  *
697  * PARAMS
698  *     hStringTable [I] Handle to the string table
699  *
700  * RETURNS
701  *     None
702  */
703 VOID WINAPI
704 StringTableTrim(HSTRING_TABLE hStringTable)
705 {
706     FIXME("%p\n", hStringTable);
707 }
708 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.