winejoystick: Fix a crash on accessing a CFArray past its end due to an off-by-one...
[wine/multimedia.git] / dlls / sane.ds / sane_main.c
blob7484ca5fc44b731f5814225d3d90be06adf0240b
1 /*
2 * SANE.DS functions
4 * Copyright 2000 Shi Quan He <shiquan@cyberdude.com>
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.
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.
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
21 #include "config.h"
22 #include "wine/port.h"
24 #include <stdarg.h>
25 #include <stdio.h>
27 #include "sane_i.h"
28 #include "wine/debug.h"
29 #include "wine/library.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(twain);
33 #ifdef SONAME_LIBSANE
35 HINSTANCE SANE_instance;
37 static void *libsane_handle;
39 static void close_libsane(void *h)
41 if (h)
42 wine_dlclose(h, NULL, 0);
45 static void *open_libsane(void)
47 void *h;
49 h = wine_dlopen(SONAME_LIBSANE, RTLD_GLOBAL | RTLD_NOW, NULL, 0);
50 if (!h)
52 WARN("dlopen(%s) failed\n", SONAME_LIBSANE);
53 return NULL;
56 #define LOAD_FUNCPTR(f) \
57 if((p##f = wine_dlsym(h, #f, NULL, 0)) == NULL) { \
58 close_libsane(h); \
59 ERR("Could not dlsym %s\n", #f); \
60 return NULL; \
63 LOAD_FUNCPTR(sane_init)
64 LOAD_FUNCPTR(sane_exit)
65 LOAD_FUNCPTR(sane_get_devices)
66 LOAD_FUNCPTR(sane_open)
67 LOAD_FUNCPTR(sane_close)
68 LOAD_FUNCPTR(sane_get_option_descriptor)
69 LOAD_FUNCPTR(sane_control_option)
70 LOAD_FUNCPTR(sane_get_parameters)
71 LOAD_FUNCPTR(sane_start)
72 LOAD_FUNCPTR(sane_read)
73 LOAD_FUNCPTR(sane_cancel)
74 LOAD_FUNCPTR(sane_set_io_mode)
75 LOAD_FUNCPTR(sane_get_select_fd)
76 LOAD_FUNCPTR(sane_strstatus)
77 #undef LOAD_FUNCPTR
79 return h;
82 BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
84 TRACE("%p,%x,%p\n", hinstDLL, fdwReason, lpvReserved);
86 switch (fdwReason)
88 case DLL_PROCESS_ATTACH: {
89 SANE_Int version_code;
91 libsane_handle = open_libsane();
92 if (! libsane_handle)
93 return FALSE;
95 psane_init (&version_code, NULL);
96 SANE_instance = hinstDLL;
97 DisableThreadLibraryCalls(hinstDLL);
98 break;
100 case DLL_PROCESS_DETACH:
101 if (lpvReserved) break;
102 TRACE("calling sane_exit()\n");
103 psane_exit ();
104 close_libsane(libsane_handle);
105 break;
108 return TRUE;
111 static TW_UINT16 SANE_GetIdentity( pTW_IDENTITY, pTW_IDENTITY);
112 static TW_UINT16 SANE_OpenDS( pTW_IDENTITY, pTW_IDENTITY);
114 #endif /* SONAME_LIBSANE */
116 static TW_UINT16 SANE_SourceControlHandler (
117 pTW_IDENTITY pOrigin,
118 TW_UINT16 DAT,
119 TW_UINT16 MSG,
120 TW_MEMREF pData)
122 TW_UINT16 twRC = TWRC_SUCCESS;
124 switch (DAT)
126 case DAT_IDENTITY:
127 switch (MSG)
129 case MSG_CLOSEDS:
130 #ifdef SONAME_LIBSANE
131 psane_close (activeDS.deviceHandle);
132 #else
133 twRC = TWRC_FAILURE;
134 activeDS.twCC = TWCC_CAPUNSUPPORTED;
135 #endif
136 break;
137 case MSG_OPENDS:
138 #ifdef SONAME_LIBSANE
139 twRC = SANE_OpenDS( pOrigin, (pTW_IDENTITY)pData);
140 #else
141 twRC = TWRC_FAILURE;
142 activeDS.twCC = TWCC_CAPUNSUPPORTED;
143 #endif
144 break;
145 case MSG_GET:
146 #ifdef SONAME_LIBSANE
147 twRC = SANE_GetIdentity( pOrigin, (pTW_IDENTITY)pData);
148 #else
149 twRC = TWRC_FAILURE;
150 activeDS.twCC = TWCC_CAPUNSUPPORTED;
151 #endif
152 break;
154 break;
155 case DAT_CAPABILITY:
156 switch (MSG)
158 case MSG_GET:
159 twRC = SANE_CapabilityGet (pOrigin, pData);
160 break;
161 case MSG_GETCURRENT:
162 twRC = SANE_CapabilityGetCurrent (pOrigin, pData);
163 break;
164 case MSG_GETDEFAULT:
165 twRC = SANE_CapabilityGetDefault (pOrigin, pData);
166 break;
167 case MSG_QUERYSUPPORT:
168 twRC = SANE_CapabilityQuerySupport (pOrigin, pData);
169 break;
170 case MSG_RESET:
171 twRC = SANE_CapabilityReset (pOrigin, pData);
172 break;
173 case MSG_SET:
174 twRC = SANE_CapabilitySet (pOrigin, pData);
175 break;
176 default:
177 twRC = TWRC_FAILURE;
178 activeDS.twCC = TWCC_CAPBADOPERATION;
179 FIXME("unrecognized opertion triplet\n");
180 break;
182 break;
184 case DAT_EVENT:
185 if (MSG == MSG_PROCESSEVENT)
186 twRC = SANE_ProcessEvent (pOrigin, pData);
187 else
189 activeDS.twCC = TWCC_CAPBADOPERATION;
190 twRC = TWRC_FAILURE;
192 break;
194 case DAT_PENDINGXFERS:
195 switch (MSG)
197 case MSG_ENDXFER:
198 twRC = SANE_PendingXfersEndXfer (pOrigin, pData);
199 break;
200 case MSG_GET:
201 twRC = SANE_PendingXfersGet (pOrigin, pData);
202 break;
203 case MSG_RESET:
204 twRC = SANE_PendingXfersReset (pOrigin, pData);
205 break;
206 default:
207 activeDS.twCC = TWCC_CAPBADOPERATION;
208 twRC = TWRC_FAILURE;
210 break;
212 case DAT_SETUPMEMXFER:
213 if (MSG == MSG_GET)
214 twRC = SANE_SetupMemXferGet (pOrigin, pData);
215 else
217 activeDS.twCC = TWCC_CAPBADOPERATION;
218 twRC = TWRC_FAILURE;
220 break;
222 case DAT_STATUS:
223 if (MSG == MSG_GET)
224 twRC = SANE_GetDSStatus (pOrigin, pData);
225 else
227 activeDS.twCC = TWCC_CAPBADOPERATION;
228 twRC = TWRC_FAILURE;
230 break;
232 case DAT_USERINTERFACE:
233 switch (MSG)
235 case MSG_DISABLEDS:
236 twRC = SANE_DisableDSUserInterface (pOrigin, pData);
237 break;
238 case MSG_ENABLEDS:
239 twRC = SANE_EnableDSUserInterface (pOrigin, pData);
240 break;
241 case MSG_ENABLEDSUIONLY:
242 twRC = SANE_EnableDSUIOnly (pOrigin, pData);
243 break;
244 default:
245 activeDS.twCC = TWCC_CAPBADOPERATION;
246 twRC = TWRC_FAILURE;
247 break;
249 break;
251 case DAT_XFERGROUP:
252 switch (MSG)
254 case MSG_GET:
255 twRC = SANE_XferGroupGet (pOrigin, pData);
256 break;
257 case MSG_SET:
258 twRC = SANE_XferGroupSet (pOrigin, pData);
259 break;
260 default:
261 activeDS.twCC = TWCC_CAPBADOPERATION;
262 twRC = TWRC_FAILURE;
263 break;
265 break;
267 default:
268 WARN("code unsupported: %d\n", DAT);
269 activeDS.twCC = TWCC_CAPUNSUPPORTED;
270 twRC = TWRC_FAILURE;
271 break;
274 return twRC;
278 static TW_UINT16 SANE_ImageGroupHandler (
279 pTW_IDENTITY pOrigin,
280 TW_UINT16 DAT,
281 TW_UINT16 MSG,
282 TW_MEMREF pData)
284 TW_UINT16 twRC = TWRC_SUCCESS;
286 switch (DAT)
288 case DAT_IMAGEINFO:
289 if (MSG == MSG_GET)
290 twRC = SANE_ImageInfoGet (pOrigin, pData);
291 else
293 activeDS.twCC = TWCC_CAPBADOPERATION;
294 twRC = TWRC_FAILURE;
296 break;
298 case DAT_IMAGELAYOUT:
299 switch (MSG)
301 case MSG_GET:
302 twRC = SANE_ImageLayoutGet (pOrigin, pData);
303 break;
304 case MSG_GETDEFAULT:
305 twRC = SANE_ImageLayoutGetDefault (pOrigin, pData);
306 break;
307 case MSG_RESET:
308 twRC = SANE_ImageLayoutReset (pOrigin, pData);
309 break;
310 case MSG_SET:
311 twRC = SANE_ImageLayoutSet (pOrigin, pData);
312 break;
313 default:
314 twRC = TWRC_FAILURE;
315 activeDS.twCC = TWCC_CAPBADOPERATION;
316 ERR("unrecognized operation triplet\n");
317 break;
319 break;
321 case DAT_IMAGEMEMXFER:
322 if (MSG == MSG_GET)
323 twRC = SANE_ImageMemXferGet (pOrigin, pData);
324 else
326 activeDS.twCC = TWCC_CAPBADOPERATION;
327 twRC = TWRC_FAILURE;
329 break;
331 case DAT_IMAGENATIVEXFER:
332 if (MSG == MSG_GET)
333 twRC = SANE_ImageNativeXferGet (pOrigin, pData);
334 else
336 activeDS.twCC = TWCC_CAPBADOPERATION;
337 twRC = TWRC_FAILURE;
339 break;
341 default:
342 twRC = TWRC_FAILURE;
343 activeDS.twCC = TWCC_CAPUNSUPPORTED;
344 WARN("unsupported DG type %d\n", DAT);
345 break;
347 return twRC;
350 /* Main entry point for the TWAIN library */
351 TW_UINT16 WINAPI
352 DS_Entry ( pTW_IDENTITY pOrigin,
353 TW_UINT32 DG,
354 TW_UINT16 DAT,
355 TW_UINT16 MSG,
356 TW_MEMREF pData)
358 TW_UINT16 twRC = TWRC_SUCCESS; /* Return Code */
360 TRACE("(DG=%d DAT=%d MSG=%d)\n", DG, DAT, MSG);
362 switch (DG)
364 case DG_CONTROL:
365 twRC = SANE_SourceControlHandler (pOrigin,DAT,MSG,pData);
366 break;
367 case DG_IMAGE:
368 twRC = SANE_ImageGroupHandler (pOrigin,DAT,MSG,pData);
369 break;
370 case DG_AUDIO:
371 WARN("Audio group of controls not implemented yet.\n");
372 twRC = TWRC_FAILURE;
373 activeDS.twCC = TWCC_CAPUNSUPPORTED;
374 break;
375 default:
376 activeDS.twCC = TWCC_BADPROTOCOL;
377 twRC = TWRC_FAILURE;
380 return twRC;
383 #ifdef SONAME_LIBSANE
384 /* Sane returns device names that are longer than the 32 bytes allowed
385 by TWAIN. However, it colon separates them, and the last bit is
386 the most interesting. So we use the last bit, and add a signature
387 to ensure uniqueness */
388 static void copy_sane_short_name(const char *in, char *out, size_t outsize)
390 const char *p;
391 int signature = 0;
393 if (strlen(in) <= outsize - 1)
395 strcpy(out, in);
396 return;
399 for (p = in; *p; p++)
400 signature += *p;
402 p = strrchr(in, ':');
403 if (!p)
404 p = in;
405 else
406 p++;
408 if (strlen(p) > outsize - 7 - 1)
409 p += strlen(p) - (outsize - 7 - 1);
411 strcpy(out, p);
412 sprintf(out + strlen(out), "(%04X)", signature % 0x10000);
416 static const SANE_Device **sane_devlist;
418 static void
419 detect_sane_devices(void) {
420 if (sane_devlist && sane_devlist[0]) return;
421 TRACE("detecting sane...\n");
422 if (psane_get_devices (&sane_devlist, SANE_FALSE) != SANE_STATUS_GOOD)
423 return;
426 static TW_UINT16
427 SANE_GetIdentity( pTW_IDENTITY pOrigin, pTW_IDENTITY self) {
428 static int cursanedev = 0;
430 detect_sane_devices();
431 if (!sane_devlist[cursanedev])
432 return TWRC_FAILURE;
433 self->ProtocolMajor = TWON_PROTOCOLMAJOR;
434 self->ProtocolMinor = TWON_PROTOCOLMINOR;
435 self->SupportedGroups = DG_CONTROL | DG_IMAGE;
436 copy_sane_short_name(sane_devlist[cursanedev]->name, self->ProductName, sizeof(self->ProductName) - 1);
437 lstrcpynA (self->Manufacturer, sane_devlist[cursanedev]->vendor, sizeof(self->Manufacturer) - 1);
438 lstrcpynA (self->ProductFamily, sane_devlist[cursanedev]->model, sizeof(self->ProductFamily) - 1);
439 cursanedev++;
441 if (!sane_devlist[cursanedev] ||
442 !sane_devlist[cursanedev]->model ||
443 !sane_devlist[cursanedev]->vendor ||
444 !sane_devlist[cursanedev]->name
446 cursanedev = 0; /* wrap to begin */
447 return TWRC_SUCCESS;
450 static TW_UINT16 SANE_OpenDS( pTW_IDENTITY pOrigin, pTW_IDENTITY self) {
451 SANE_Status status;
452 int i;
454 detect_sane_devices();
455 if (!sane_devlist[0]) {
456 ERR("No scanners? We should not get to OpenDS?\n");
457 return TWRC_FAILURE;
460 for (i=0; sane_devlist[i] && sane_devlist[i]->model; i++) {
461 TW_STR32 name;
463 /* To make string as short as above */
464 lstrcpynA(name, sane_devlist[i]->vendor, sizeof(name)-1);
465 if (*self->Manufacturer && strcmp(name, self->Manufacturer))
466 continue;
467 lstrcpynA(name, sane_devlist[i]->model, sizeof(name)-1);
468 if (*self->ProductFamily && strcmp(name, self->ProductFamily))
469 continue;
470 copy_sane_short_name(sane_devlist[i]->name, name, sizeof(name) - 1);
471 if (*self->ProductName && strcmp(name, self->ProductName))
472 continue;
473 break;
475 if (!sane_devlist[i]) {
476 WARN("Scanner not found.\n");
477 return TWRC_FAILURE;
479 status = psane_open(sane_devlist[i]->name,&activeDS.deviceHandle);
480 if (status == SANE_STATUS_GOOD) {
481 activeDS.twCC = SANE_SaneSetDefaults();
482 if (activeDS.twCC == TWCC_SUCCESS) {
483 activeDS.currentState = 4;
484 return TWRC_SUCCESS;
486 else
487 psane_close(activeDS.deviceHandle);
489 else
490 ERR("sane_open(%s): %s\n", sane_devlist[i]->name, psane_strstatus (status));
491 return TWRC_FAILURE;
493 #endif