lists: fix crash in ListInsertElements
[jimtcl.git] / jim-win32api.c
blob28dd087dcce20409ca3d104e28cf2db070113a48
1 /*-
2 * Copyright (c) 2005 Pat Thoyts <patthoyts@users.sourceforge.net>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
26 * $Id$
28 * WIN32 API extension
31 #define STRICT
32 #define WIN32_LEAN_AND_MEAN
33 #include <windows.h>
34 #include <shellapi.h>
35 #include <lmcons.h>
37 #include <stdio.h>
39 #define JIM_EXTENSION
40 #include "jim.h"
42 #if _MSC_VER >= 1000
43 #pragma comment(lib, "shell32")
44 #pragma comment(lib, "user32")
45 #pragma comment(lib, "advapi32")
46 #endif /* _MSC_VER >= 1000 */
48 static const char win32api_type_hash[] = "win32api:typemap";
50 __declspec(dllexport) int Jim_OnLoad(Jim_Interp *interp);
52 /* ----------------------------------------------------------------------
53 * Convert Win32 error codes into an error message string object.
56 static Jim_Obj *
57 Win32ErrorObj(Jim_Interp *interp, const char * szPrefix, DWORD dwError)
59 Jim_Obj *msgObj = NULL;
60 char * lpBuffer = NULL;
61 DWORD dwLen = 0;
63 dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
64 | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, LANG_NEUTRAL,
65 (char *)&lpBuffer, 0, NULL);
66 if (dwLen < 1) {
67 dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
68 | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
69 "code 0x%1!08X!%n", 0, LANG_NEUTRAL,
70 (char *)&lpBuffer, 0, (va_list *)&dwError);
73 msgObj = Jim_NewStringObj(interp, szPrefix, -1);
74 if (dwLen > 0) {
75 char *p = lpBuffer + dwLen - 1; /* remove cr-lf at end */
76 for ( ; p && *p && isspace(*p); p--)
78 *++p = 0;
79 Jim_AppendString(interp, msgObj, ": ", 2);
80 Jim_AppendString(interp, msgObj, lpBuffer, -1);
82 LocalFree((HLOCAL)lpBuffer);
83 return msgObj;
86 /* ----------------------------------------------------------------------
87 * Information recorded for API calls that we can call.
90 typedef struct Win32ApiDeclaration {
91 HMODULE module; /* Reference counted handle to the library */
92 LPSTR symbol; /* The function name as used in the library */
93 FARPROC lpfn; /* Generic function pointer for the API */
94 LPSTR rtype; /* The return type */
95 Jim_Obj *typeList; /* List of parameters as type name type ... */
96 } Win32ApiDeclaration;
99 /* ----------------------------------------------------------------------
100 * Type hash table
102 * This is generic string:thing* hash.
103 * Keys are a typename. Values are pointers Win32ApiTypeInfo structs
106 typedef struct Win32ApiTypeInfo {
107 size_t type_size; /* The size of the type in bytes (not used yet) */
108 char type_spec[]; /* packing details for the type */
109 } Win32ApiTypeInfo;
111 static unsigned int Win32ApiTypeInfoHashTableHash(const void *key)
113 /*return Jim_DjbHashFunction(key, strlen(key));*/
114 unsigned int h = 5381;
115 size_t len = strlen(key);
116 while(len--)
117 h = (h + (h << 5)) ^ *((const char *)key)++;
118 return h;
121 static const void *Win32ApiTypeInfoHashTableCopyKey(void *privdata, const void *key)
123 JIM_NOTUSED(privdata);
124 return Jim_StrDup(key);
127 static int Win32ApiTypeInfoHashTableCompare(void *privdata, const void *key1, const void *key2)
129 JIM_NOTUSED(privdata);
130 return strcmp(key1, key2) == 0;
133 static void Win32ApiTypeInfoHashTableDestroyKey(void *privdata, const void *key)
135 JIM_NOTUSED(privdata);
136 Jim_Free((void*)key);
139 static void Win32ApiTypeInfoHashTableDestroyValue(void *interp, void *val)
141 Win32ApiTypeInfo *entryPtr = (Win32ApiTypeInfo *)val;
142 JIM_NOTUSED(interp);
143 Jim_Free((void*)entryPtr);
146 static Jim_HashTableType Win32ApiTypeHashTableType = {
147 Win32ApiTypeInfoHashTableHash, /* hash function */
148 Win32ApiTypeInfoHashTableCopyKey, /* key dup */
149 NULL, /* val dup */
150 Win32ApiTypeInfoHashTableCompare, /* key compare */
151 Win32ApiTypeInfoHashTableDestroyKey, /* key destructor */
152 Win32ApiTypeInfoHashTableDestroyValue /* val destructor */
155 /* ----------------------------------------------------------------------
156 * The typedef object type is a caching internal rep to avoid repeat
157 * lookups into the hash table.
160 Jim_ObjType typedefObjType = {
161 "win32.typedef",
162 NULL,
163 NULL,
164 NULL,
165 JIM_TYPE_REFERENCES,
169 TypedefSetFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
171 if (objPtr->typePtr != &typedefObjType) {
172 const char *tname = Jim_GetString(objPtr, NULL);
173 Jim_HashTable *hashPtr = Jim_GetAssocData(interp, win32api_type_hash);
174 Jim_HashEntry *entryPtr = Jim_FindHashEntry(hashPtr, tname);
175 if (entryPtr == NULL) {
176 Jim_Obj *errObj = Jim_NewEmptyStringObj(interp);
177 Jim_AppendStrings(interp, errObj, "type \"", tname, "\" is not defined", NULL);
178 Jim_SetResult(interp, errObj);
179 return JIM_ERR;
182 Jim_FreeIntRep(interp, objPtr);
183 objPtr->internalRep.ptr = entryPtr;
184 objPtr->typePtr = &typedefObjType;
186 return JIM_OK;
189 /* ---------------------------------------------------------------------- */
191 /* Clean up the package's interp associated data */
192 static void
193 Win32_PackageDeleteProc(Jim_Interp *interp, void *clientData)
195 Jim_HashTable *hashPtr = (Jim_HashTable *)clientData;
196 Jim_FreeHashTable(hashPtr);
197 Jim_Free(clientData);
200 /* Update and introspect the type table
202 * Perls pack supports...
203 a char binary data (null padded) u uuencoded string
204 A cha binary data (space padded) U unicode char
205 Z asciiz
206 c char s int16 i int l int32 n be u_int16 v le uint16 q int64 f float p pointer (null term)
207 C uchar S u_int16 I u_int L uint_32 N be u_int32 V le uint32 Q u_int64 d double P block pointer
209 static int
210 Win32_Typedef(Jim_Interp *interp, int objc, Jim_Obj *const objv[])
212 enum {OPT_NULL, OPT_TYPE_NAMES, OPT_TYPE_GET, OPT_TYPE_SET};
213 Jim_HashTable *hashPtr = (Jim_HashTable *)Jim_CmdPrivData(interp);
214 Win32ApiTypeInfo *typePtr;
215 const char *name = NULL, *spec = NULL;
216 int name_len, spec_len, n, r = JIM_OK;
217 struct def { char c; unsigned char n; } defs[] = {
218 'c', 1, 'C', 1, 's', 2, 'S', 2, 'i', 4, 'I', 4,
219 'l', 4, 'L', 4, 'n', 4, 'N', 4, 'v', 4, 'V', 4,
220 'q', 8, 'Q', 8, 'f', 4, 'd', 8, 'p', 4, 'P', 4,
221 'a', 1, 'A', 1, 'Z', 0, 'U', 2, 0, 0};
222 struct def *sp;
223 const char *p;
224 size_t size, m = 0;
226 if (objc > 3) {
227 Jim_WrongNumArgs(interp, 1, objv, "typename ?pack_spec?");
228 return JIM_ERR;
231 switch (objc) {
232 /* Assign a new type. We validate the pack format and calculate the type size at the same time */
233 case OPT_TYPE_SET: {
234 name = Jim_GetString(objv[1], &name_len);
235 spec = Jim_GetString(objv[2], &spec_len);
237 size = 0;
238 p = spec;
239 while (p && *p) {
240 for (sp = defs; sp->c != 0; sp++) {
241 if (sp->c == *p)
242 break;
244 if (sp->c == 0) {
245 Jim_Obj *errObj = Jim_NewStringObj(interp, "invalid pack character \"0\"", -1);
246 errObj->bytes[24] = *p; /* NOTE: if you change the text in the above string this must be fixed */
247 Jim_AppendStrings(interp, errObj, " while defining type \"", name, "\"", NULL);
248 Jim_SetResult(interp, errObj);
249 return JIM_ERR;
251 if (isdigit(*(p+1))) {
252 char *pe = NULL;
253 m = strtol(p+1, &pe, 0);
254 p = pe;
255 } else {
256 m = 1;
257 p++;
259 size += (sp->n * m);
262 n = name_len + spec_len + 1;
263 typePtr = (Win32ApiTypeInfo *)Jim_Alloc(n);
264 typePtr->type_size = size;
265 strcpy(typePtr->type_spec, spec);
266 Jim_AddHashEntry(hashPtr, name, typePtr);
267 Jim_SetResultString(interp, name, name_len);
268 break;
270 case OPT_TYPE_GET: {
271 Jim_HashEntry *entryPtr;
272 name = Jim_GetString(objv[1], &name_len);
273 if ((entryPtr = Jim_FindHashEntry(hashPtr, name)) == NULL) {
274 Jim_Obj *resObj = Jim_NewEmptyStringObj(interp);
275 Jim_AppendStrings(interp, resObj, "type \"", name, "\" not found", NULL);
276 Jim_SetResult(interp, resObj);
277 r = JIM_ERR;
278 } else {
279 typePtr = entryPtr->val;
280 Jim_SetResultString(interp, typePtr->type_spec, -1);
282 break;
284 case OPT_TYPE_NAMES: {
285 Jim_Obj *listPtr;
286 Jim_HashEntry *entryPtr;
287 Jim_HashTableIterator *it = Jim_GetHashTableIterator(hashPtr);
288 listPtr = Jim_NewListObj(interp, NULL, 0);
289 while ((entryPtr = Jim_NextHashEntry(it)) != NULL) {
290 Jim_ListAppendElement(interp, listPtr, Jim_NewStringObj(interp, entryPtr->key, -1));
292 Jim_SetResult(interp, listPtr);
293 break;
296 return r;
299 /* ---------------------------------------------------------------------- */
302 static void
303 Win32_ApiCleanup(Jim_Interp *interp, void *clientData)
305 Win32ApiDeclaration *declPtr = (Win32ApiDeclaration *)clientData;
306 FreeLibrary(declPtr->module);
307 Jim_Free((void *)declPtr->symbol);
308 Jim_Free((void *)declPtr->rtype);
309 Jim_DecrRefCount(interp, declPtr->typeList);
312 static int
313 Win32_ApiHandler(Jim_Interp *interp, int objc, Jim_Obj *const objv[])
315 Win32ApiDeclaration *declPtr = (Win32ApiDeclaration *)Jim_CmdPrivData(interp);
316 Jim_HashTable *hashPtr;
317 int nargs = 0, n, np, r;
318 long lval;
319 double dblval;
320 float fltval;
321 struct {
322 unsigned long params[16];
323 } param;
325 Jim_ListLength(interp, declPtr->typeList, &nargs);
326 if (objc-1 != nargs/2) {
327 int tlen = 0;
328 const char *types = Jim_GetString(declPtr->typeList, &tlen);
329 char *sz = (char *)_alloca(tlen + 3);
330 sprintf(sz, "(%s)", types);
331 Jim_WrongNumArgs(interp, 1, objv, sz);
332 return JIM_ERR;
335 ZeroMemory(&param, sizeof(param));
336 hashPtr = Jim_GetAssocData(interp, win32api_type_hash);
338 for (n = 1, np = 0; n < objc; n++) {
339 Jim_HashEntry *entryPtr;
340 Jim_Obj *tnameObj, *pnameObj;
341 const char *tname, *pname;
343 r = Jim_ListIndex(interp, declPtr->typeList, (np*2), &tnameObj, JIM_ERRMSG);
344 tname = Jim_GetString(tnameObj, NULL);
345 r = Jim_ListIndex(interp, declPtr->typeList, (np*2)+1, &pnameObj, JIM_ERRMSG);
346 pname = Jim_GetString(pnameObj, NULL);
348 entryPtr = Jim_FindHashEntry(hashPtr, tname);
349 switch (((Win32ApiTypeInfo *)entryPtr->val)->type_spec[0]) {
350 case 'q': case 'Q':
351 Jim_GetWide(interp, objv[n], (jim_wide *)&param.params[np]);
352 np += 2;
353 break;
354 case 's': case 'v':
355 Jim_GetLong(interp, objv[n], &lval);
356 param.params[np] = (unsigned short)lval;
357 np++;
358 break;
359 case 'd':
360 Jim_GetDouble(interp, objv[n], &dblval);
361 memcpy(&param.params[np], &dblval, 8);
362 np+=2;
363 break;
364 case 'f':
365 Jim_GetDouble(interp, objv[n], &dblval);
366 fltval = (float)dblval;
367 memcpy(&param.params[np], &fltval, 4);
368 np+=2;
369 break;
370 case 'l': case 'L': case 'i': case 'I': case 'V':
371 default:
372 Jim_GetLong(interp, objv[n], &param.params[np]);
373 np++;
374 break;
378 if (nargs == 0)
379 r = declPtr->lpfn();
380 else
381 r = declPtr->lpfn(param);
383 Jim_SetResult(interp, Jim_NewIntObj(interp, r));
384 return JIM_OK;
387 static int
388 Win32_Declare(Jim_Interp *interp, int objc, Jim_Obj *const objv[])
390 Jim_HashTable *hashPtr = (Jim_HashTable *)Jim_CmdPrivData(interp); /* type hash map */
391 Win32ApiDeclaration *declPtr;
392 HMODULE hLib = NULL;
393 FARPROC lpfn = NULL;
394 const char *lib, *rtype, *name;
395 Jim_Obj *cmdObj = NULL;
397 if (objc != 5) {
398 Jim_WrongNumArgs(interp, 1, objv, "lib return_type name typelist");
399 return JIM_ERR;
402 lib = Jim_GetString(objv[1], NULL);
403 rtype = Jim_GetString(objv[2], NULL);
404 name = Jim_GetString(objv[3], NULL);
405 hLib = LoadLibraryA(lib);
406 if (hLib == NULL) {
407 Jim_SetResultString(interp, "failed to load library", -1);
408 return JIM_ERR;
411 if ((lpfn = GetProcAddress(hLib, name)) == NULL) {
412 Jim_Obj *errObj = Jim_NewEmptyStringObj(interp);
413 FreeLibrary(hLib);
414 Jim_AppendStrings(interp, errObj, "could not load \"", name, "\" from \"", lib, "\"", NULL);
415 Jim_SetResult(interp, errObj);
416 return JIM_ERR;
419 declPtr = (Win32ApiDeclaration *)Jim_Alloc(sizeof(Win32ApiDeclaration));
420 declPtr->module = hLib;
421 declPtr->lpfn = lpfn;
422 declPtr->symbol = Jim_StrDup(name);
423 declPtr->rtype = Jim_StrDup(rtype);
424 declPtr->typeList = Jim_DuplicateObj(interp, objv[4]);
425 Jim_IncrRefCount(declPtr->typeList);
427 cmdObj = Jim_NewStringObj(interp, "", strlen(name) + 9);
428 sprintf(cmdObj->bytes, "win32api.%s", name);
429 Jim_CreateCommand(interp, cmdObj->bytes, Win32_ApiHandler, declPtr, Win32_ApiCleanup);
430 Jim_SetResult(interp, cmdObj);
431 return JIM_OK;
434 /* ---------------------------------------------------------------------- */
437 Jim_OnLoad(Jim_Interp *interp)
439 Jim_HashTable *hashPtr;
440 Jim_InitExtension(interp);
441 if (Jim_PackageProvide(interp, "win32api", "1.0", JIM_ERRMSG) != JIM_OK)
442 return JIM_ERR;
444 hashPtr = (Jim_HashTable *)Jim_Alloc(sizeof(Jim_HashTable));
445 Jim_InitHashTable(hashPtr, &Win32ApiTypeHashTableType, NULL);
446 Jim_SetAssocData(interp, win32api_type_hash, Win32_PackageDeleteProc, hashPtr);
448 Jim_CreateCommand(interp, "win32.declare", Win32_Declare, hashPtr, NULL);
449 Jim_CreateCommand(interp, "win32.typedef", Win32_Typedef, hashPtr, NULL);
451 return JIM_OK;
454 /* ----------------------------------------------------------------------
455 * Local variables:
456 * indent-tabs-mode: nil
457 * End: