92dac0a796daa8efdb74e2809faf4524a304e301
[gpgme.git] / gpgme / w32-io.c
1 /* w32-io.c - W32 API I/O functions
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GPGME 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #ifdef HAVE_DOSISH_SYSTEM
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <signal.h>
32 #include <fcntl.h>
33 #include <windows.h>
34 #include "syshdr.h"
35
36 #include "util.h"
37 #include "sema.h"
38 #include "io.h"
39
40
41 /* 
42  * We assume that a HANDLE can be represented by an int which should be true   
43  * for all i386 systems (HANDLE is defined as void *) and these are the only
44  * systems for which Windows is available.
45  * Further we assume that -1 denotes an invalid handle.
46  */
47
48 #define fd_to_handle(a)  ((HANDLE)(a))
49 #define handle_to_fd(a)  ((int)(a))
50 #define pid_to_handle(a) ((HANDLE)(a))
51 #define handle_to_pid(a) ((int)(a))
52
53 #define READBUF_SIZE 4096
54
55 struct reader_context_s {
56     HANDLE file_hd;
57     HANDLE thread_hd;   
58     DECLARE_LOCK (mutex);
59     
60     int eof;
61     int eof_shortcut;
62     int error;
63     int error_code;
64
65     HANDLE have_data_ev;  /* manually reset */
66     HANDLE have_space_ev; /* auto reset */
67     size_t readpos, writepos;
68     char buffer[READBUF_SIZE];
69 };
70
71
72 #define MAX_READERS 20
73 static struct {
74     volatile int used;
75     int fd;
76     struct reader_context_s *context;
77 } reader_table[MAX_READERS];
78 static int reader_table_size= MAX_READERS;
79 DEFINE_STATIC_LOCK (reader_table_lock);
80
81 static HANDLE
82 set_synchronize (HANDLE h)
83 {
84     HANDLE tmp;
85     
86     /* For NT we have to set the sync flag.  It seems that the only
87      * way to do it is by duplicating the handle.  Tsss.. */
88     if (!DuplicateHandle( GetCurrentProcess(), h,
89                           GetCurrentProcess(), &tmp,
90                           EVENT_MODIFY_STATE|SYNCHRONIZE, FALSE, 0 ) ) {
91         DEBUG1 ("** Set SYNCRONIZE failed: ec=%d\n", (int)GetLastError());
92     }
93     else {
94         CloseHandle (h);
95         h = tmp;
96     }
97     return h;
98 }
99
100
101
102 static DWORD CALLBACK 
103 reader (void *arg)
104 {
105     struct reader_context_s *c = arg;
106     int nbytes;
107     DWORD nread;
108
109     DEBUG2 ("reader thread %p for file %p started", c->thread_hd, c->file_hd );
110     for (;;) {
111         LOCK (c->mutex);
112         /* leave a one byte gap so that we can see wheter it is empty or full*/
113         if ((c->writepos + 1) % READBUF_SIZE == c->readpos) { 
114             /* wait for space */
115             if (!ResetEvent (c->have_space_ev) )
116                 DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
117             UNLOCK (c->mutex);
118             DEBUG1 ("reader thread %p: waiting for space ...", c->thread_hd );
119             WaitForSingleObject (c->have_space_ev, INFINITE);
120             DEBUG1 ("reader thread %p: got space", c->thread_hd );
121             LOCK (c->mutex);
122         }
123         nbytes = (c->readpos + READBUF_SIZE - c->writepos-1) % READBUF_SIZE;
124         if ( nbytes > READBUF_SIZE - c->writepos )
125             nbytes = READBUF_SIZE - c->writepos;
126         UNLOCK (c->mutex);
127
128         DEBUG2 ("reader thread %p: reading %d bytes", c->thread_hd, nbytes );
129         if ( !ReadFile ( c->file_hd,
130                          c->buffer+c->writepos, nbytes, &nread, NULL) ) {
131             c->error_code = (int)GetLastError ();
132             if (c->error_code == ERROR_BROKEN_PIPE ) {
133                 c->eof=1;
134                 DEBUG1 ("reader thread %p: got eof (broken pipe)",
135                         c->thread_hd );
136             }
137             else {
138                 c->error = 1;
139                 DEBUG2 ("reader thread %p: read error: ec=%d",
140                         c->thread_hd, c->error_code );
141             }
142             break;
143         }
144         if ( !nread ) {
145             c->eof = 1;
146             DEBUG1 ("reader thread %p: got eof", c->thread_hd );
147             break;
148         }
149         DEBUG2 ("reader thread %p: got %d bytes", c->thread_hd, (int)nread );
150       
151         LOCK (c->mutex);
152         c->writepos = (c->writepos + nread) % READBUF_SIZE;
153         if ( !SetEvent (c->have_data_ev) )
154             DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
155         UNLOCK (c->mutex);
156     }
157     /* indicate that we have an error or eof */
158     if ( !SetEvent (c->have_data_ev) )
159         DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
160     DEBUG1 ("reader thread %p ended", c->thread_hd );
161
162     return 0;
163 }
164
165
166 static struct reader_context_s *
167 create_reader (HANDLE fd)
168 {
169     struct reader_context_s *c;
170     SECURITY_ATTRIBUTES sec_attr;
171     DWORD tid;
172
173     DEBUG1 ("creating new read thread for file handle %p", fd );
174     memset (&sec_attr, 0, sizeof sec_attr );
175     sec_attr.nLength = sizeof sec_attr;
176     sec_attr.bInheritHandle = FALSE;
177
178     c = xtrycalloc (1, sizeof *c );
179     if (!c)
180         return NULL;
181
182     c->file_hd = fd;
183     c->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
184     c->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
185     if (!c->have_data_ev || !c->have_space_ev) {
186         DEBUG1 ("** CreateEvent failed: ec=%d\n", (int)GetLastError ());
187         if (c->have_data_ev)
188             CloseHandle (c->have_data_ev);
189         if (c->have_space_ev)
190             CloseHandle (c->have_space_ev);
191         xfree (c);
192         return NULL;
193     }
194
195     c->have_data_ev = set_synchronize (c->have_data_ev);
196     INIT_LOCK (c->mutex);
197
198     c->thread_hd = CreateThread (&sec_attr, 0, reader, c, 0, &tid );
199     if (!c->thread_hd) {
200         DEBUG1 ("** failed to create reader thread: ec=%d\n",
201                  (int)GetLastError ());
202         DESTROY_LOCK (c->mutex);
203         if (c->have_data_ev)
204             CloseHandle (c->have_data_ev);
205         if (c->have_space_ev)
206             CloseHandle (c->have_space_ev);
207         xfree (c);
208         return NULL;
209     }    
210
211     return c;
212 }
213
214
215 /* 
216  * Find a reader context or create a new one 
217  * Note that the reader context will last until a io_close.
218  */
219 static struct reader_context_s *
220 find_reader (int fd, int start_it)
221 {
222     int i;
223
224     for (i=0; i < reader_table_size ; i++ ) {
225         if ( reader_table[i].used && reader_table[i].fd == fd )
226             return reader_table[i].context;
227     }
228     if (!start_it)
229         return NULL;
230
231     LOCK (reader_table_lock);
232     for (i=0; i < reader_table_size; i++ ) {
233         if (!reader_table[i].used) {
234             reader_table[i].fd = fd;
235             reader_table[i].context = create_reader (fd_to_handle (fd));
236             reader_table[i].used = 1;
237             UNLOCK (reader_table_lock);
238             return reader_table[i].context;
239         }
240     }
241     UNLOCK (reader_table_lock);
242     return NULL;
243 }
244
245
246
247 int
248 _gpgme_io_read ( int fd, void *buffer, size_t count )
249 {
250     int nread;
251     struct reader_context_s *c = find_reader (fd,1);
252
253     DEBUG2 ("fd %d: about to read %d bytes\n", fd, (int)count );
254     if ( !c ) {
255         DEBUG0 ( "no reader thread\n");
256         return -1;
257     }
258     if (c->eof_shortcut) {
259         DEBUG1 ("fd %d: EOF (again)", fd );
260         return 0;
261     }
262
263     LOCK (c->mutex);
264     if (c->readpos == c->writepos && !c->error) { /*no data avail*/
265         UNLOCK (c->mutex);
266         DEBUG2 ("fd %d: waiting for data from thread %p", fd, c->thread_hd);
267         WaitForSingleObject (c->have_data_ev, INFINITE);
268         DEBUG2 ("fd %d: data from thread %p available", fd, c->thread_hd);
269         LOCK (c->mutex);
270     }
271     
272     if (c->readpos == c->writepos || c->error) {
273         UNLOCK (c->mutex);
274         c->eof_shortcut = 1;
275         if (c->eof) {
276             DEBUG1 ("fd %d: EOF", fd );
277             return 0;
278         }
279         if (!c->error) {
280             DEBUG1 ("fd %d: EOF but eof flag not set", fd );
281             return 0;
282         }
283         DEBUG1 ("fd %d: read error", fd );
284         return -1;
285     }
286       
287     nread = c->readpos < c->writepos? c->writepos - c->readpos
288                                     : READBUF_SIZE - c->readpos;
289     if (nread > count)
290         nread = count;
291     memcpy (buffer, c->buffer+c->readpos, nread);
292     c->readpos = (c->readpos + nread) % READBUF_SIZE;
293     if (c->readpos == c->writepos && !c->eof) {
294         if ( !ResetEvent (c->have_data_ev) )
295             DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
296     }
297     if (!SetEvent (c->have_space_ev))
298         DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
299     UNLOCK (c->mutex);
300
301     DEBUG2 ("fd %d: got %d bytes\n", fd, nread );
302
303     return nread;
304 }
305
306
307 int
308 _gpgme_io_write ( int fd, const void *buffer, size_t count )
309 {
310     DWORD nwritten;
311     HANDLE h = fd_to_handle (fd);
312
313     /* writing blocks for large counts, so we limit it here. */
314     if (count > 1024)
315         count = 1024;
316
317     DEBUG2 ("fd %d: about to write %d bytes\n", fd, (int)count );
318     if ( !WriteFile ( h, buffer, count, &nwritten, NULL) ) {
319         DEBUG1 ("WriteFile failed: ec=%d\n", (int)GetLastError ());
320         return -1;
321     }
322     DEBUG2 ("fd %d:          wrote %d bytes\n",
323                    fd, (int)nwritten );
324
325     return (int)nwritten;
326 }
327
328 int
329 _gpgme_io_pipe ( int filedes[2], int inherit_idx )
330 {
331     HANDLE r, w;
332     SECURITY_ATTRIBUTES sec_attr;
333
334     memset (&sec_attr, 0, sizeof sec_attr );
335     sec_attr.nLength = sizeof sec_attr;
336     sec_attr.bInheritHandle = FALSE;
337     
338     if (!CreatePipe ( &r, &w, &sec_attr, 0))
339         return -1;
340     /* make one end inheritable */
341     if ( inherit_idx == 0 ) {
342         HANDLE h;
343         if (!DuplicateHandle( GetCurrentProcess(), r,
344                               GetCurrentProcess(), &h, 0,
345                               TRUE, DUPLICATE_SAME_ACCESS ) ) {
346             DEBUG1 ("DuplicateHandle failed: ec=%d\n", (int)GetLastError());
347             CloseHandle (r);
348             CloseHandle (w);
349             return -1;
350         }
351         CloseHandle (r);
352         r = h;
353     }
354     else if ( inherit_idx == 1 ) {
355         HANDLE h;
356         if (!DuplicateHandle( GetCurrentProcess(), w,
357                               GetCurrentProcess(), &h, 0,
358                               TRUE, DUPLICATE_SAME_ACCESS ) ) {
359             DEBUG1 ("DuplicateHandle failed: ec=%d\n", (int)GetLastError());
360             CloseHandle (r);
361             CloseHandle (w);
362             return -1;
363         }
364         CloseHandle (w);
365         w = h;
366     }
367
368     filedes[0] = handle_to_fd (r);
369     filedes[1] = handle_to_fd (w);
370     DEBUG5 ("CreatePipe %p %p %d %d inherit=%d\n", r, w,
371                    filedes[0], filedes[1], inherit_idx );
372     return 0;
373 }
374
375 int
376 _gpgme_io_close ( int fd )
377 {
378     if ( fd == -1 )
379         return -1;
380
381     DEBUG1 ("** closing handle for fd %d\n", fd);
382     /* fixme: destroy thread */
383
384     if ( !CloseHandle (fd_to_handle (fd)) ) { 
385         DEBUG2 ("CloseHandle for fd %d failed: ec=%d\n",
386                  fd, (int)GetLastError ());
387         return -1;
388     }
389
390     return 0;
391 }
392
393
394 int
395 _gpgme_io_set_nonblocking ( int fd )
396 {
397     return 0;
398 }
399
400
401 static char *
402 build_commandline ( char **argv )
403 {
404     int i, n = 0;
405     char *buf, *p;
406
407     /* FIXME: we have to quote some things because under Windows the 
408      * program parses the commandline and does some unquoting */
409     for (i=0; argv[i]; i++)
410         n += strlen (argv[i]) + 1;
411     buf = p = xtrymalloc (n);
412     if ( !buf )
413         return NULL;
414     *buf = 0;
415     if ( argv[0] )
416         p = stpcpy (p, argv[0]);
417     for (i = 1; argv[i]; i++)
418         p = stpcpy (stpcpy (p, " "), argv[i]);
419
420     return buf;
421 }
422
423
424 int
425 _gpgme_io_spawn ( const char *path, char **argv,
426                   struct spawn_fd_item_s *fd_child_list,
427                   struct spawn_fd_item_s *fd_parent_list )
428 {
429     SECURITY_ATTRIBUTES sec_attr;
430     PROCESS_INFORMATION pi = {
431         NULL,      /* returns process handle */
432         0,         /* returns primary thread handle */
433         0,         /* returns pid */
434         0         /* returns tid */
435     };
436     STARTUPINFO si;
437     char *envblock = NULL;
438     int cr_flags = CREATE_DEFAULT_ERROR_MODE
439                  | GetPriorityClass (GetCurrentProcess ());
440     int i;
441     char *arg_string;
442     int duped_stdin = 0;
443     int duped_stderr = 0;
444     HANDLE hnul = INVALID_HANDLE_VALUE;
445     int debug_me = !!getenv ("GPGME_DEBUG");
446
447     memset (&sec_attr, 0, sizeof sec_attr );
448     sec_attr.nLength = sizeof sec_attr;
449     sec_attr.bInheritHandle = FALSE;
450
451     arg_string = build_commandline ( argv );
452     if (!arg_string )
453         return -1; 
454
455     memset (&si, 0, sizeof si);
456     si.cb = sizeof (si);
457     si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
458     si.wShowWindow = debug_me? SW_SHOW : SW_MINIMIZE;
459     si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
460     si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
461     si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
462
463     for (i=0; fd_child_list[i].fd != -1; i++ ) {
464         if (fd_child_list[i].dup_to == 0 ) {
465             si.hStdInput = fd_to_handle (fd_child_list[i].fd);
466             DEBUG1 ("using %d for stdin", fd_child_list[i].fd );
467             duped_stdin=1;
468         }
469         else if (fd_child_list[i].dup_to == 1 ) {
470             si.hStdOutput = fd_to_handle (fd_child_list[i].fd);
471             DEBUG1 ("using %d for stdout", fd_child_list[i].fd );
472         }
473         else if (fd_child_list[i].dup_to == 2 ) {
474             si.hStdError = fd_to_handle (fd_child_list[i].fd);
475             DEBUG1 ("using %d for stderr", fd_child_list[i].fd );
476             duped_stderr = 1;
477         }
478     }
479
480     if( !duped_stdin || !duped_stderr ) {
481         SECURITY_ATTRIBUTES sa;
482
483         memset (&sa, 0, sizeof sa );
484         sa.nLength = sizeof sa;
485         sa.bInheritHandle = TRUE;
486         hnul = CreateFile ( "nul",
487                             GENERIC_READ|GENERIC_WRITE,
488                             FILE_SHARE_READ|FILE_SHARE_WRITE,
489                             &sa,
490                             OPEN_EXISTING,
491                             FILE_ATTRIBUTE_NORMAL,
492                             NULL );
493         if ( hnul == INVALID_HANDLE_VALUE ) {
494             DEBUG1 ("can't open `nul': ec=%d\n", (int)GetLastError ());
495             xfree (arg_string);
496             return -1;
497         }
498         /* Make sure that the process has a connected stdin */
499         if ( !duped_stdin ) {
500             si.hStdInput = hnul;
501             DEBUG1 ("using %d for dummy stdin", (int)hnul );
502         }
503         /* We normally don't want all the normal output */
504         if ( !duped_stderr ) {
505             si.hStdError = hnul;
506             DEBUG1 ("using %d for dummy stderr", (int)hnul );
507         }
508     }
509
510     DEBUG2 ("CreateProcess, path=`%s' args=`%s'", path, arg_string);
511     cr_flags |= CREATE_SUSPENDED; 
512     if ( !CreateProcessA (path,
513                           arg_string,
514                           &sec_attr,     /* process security attributes */
515                           &sec_attr,     /* thread security attributes */
516                           TRUE,          /* inherit handles */
517                           cr_flags,      /* creation flags */
518                           envblock,      /* environment */
519                           NULL,          /* use current drive/directory */
520                           &si,           /* startup information */
521                           &pi            /* returns process information */
522         ) ) {
523         DEBUG1 ("CreateProcess failed: ec=%d\n", (int) GetLastError ());
524         xfree (arg_string);
525         return -1;
526     }
527
528     /* close the /dev/nul handle if used */
529     if (hnul != INVALID_HANDLE_VALUE ) {
530         if ( !CloseHandle ( hnul ) )
531             DEBUG1 ("CloseHandle(hnul) failed: ec=%d\n", (int)GetLastError());
532     }
533
534     /* Close the other ends of the pipes */
535     for (i=0; fd_parent_list[i].fd != -1; i++ ) {
536         DEBUG1 ("Closing fd %d\n", fd_parent_list[i].fd );
537         if ( !CloseHandle ( fd_to_handle (fd_parent_list[i].fd) ) )
538             DEBUG1 ("CloseHandle failed: ec=%d", (int)GetLastError());
539     }
540
541     DEBUG4 ("CreateProcess ready\n"
542             "-   hProcess=%p  hThread=%p\n"
543             "-   dwProcessID=%d dwThreadId=%d\n",
544             pi.hProcess, pi.hThread, 
545             (int) pi.dwProcessId, (int) pi.dwThreadId);
546
547     if ( ResumeThread ( pi.hThread ) < 0 ) {
548         DEBUG1 ("ResumeThread failed: ec=%d\n", (int)GetLastError ());
549     }
550
551     if ( !CloseHandle (pi.hThread) ) { 
552         DEBUG1 ("CloseHandle of thread failed: ec=%d\n",
553                  (int)GetLastError ());
554     }
555
556     return handle_to_pid (pi.hProcess);
557 }
558
559
560
561
562 int
563 _gpgme_io_waitpid ( int pid, int hang, int *r_status, int *r_signal )
564 {
565     HANDLE proc = fd_to_handle (pid);
566     int code, exc, ret = 0;
567
568     *r_status = 0;
569     *r_signal = 0;
570     code = WaitForSingleObject ( proc, hang? INFINITE : 0 );
571     switch (code) {
572       case WAIT_FAILED:
573         DEBUG2 ("WFSO pid=%d failed: %d\n", (int)pid, (int)GetLastError () );
574         break;
575
576       case WAIT_OBJECT_0:
577         if (!GetExitCodeProcess (proc, &exc)) {
578             DEBUG2 ("** GECP pid=%d failed: ec=%d\n",
579                     (int)pid, (int)GetLastError () );
580             *r_status = 4; 
581         }
582         else {
583             DEBUG2 ("GECP pid=%d exit code=%d\n", (int)pid,  exc);
584             *r_status = exc;
585         }
586         ret = 1;
587         break;
588
589       case WAIT_TIMEOUT:
590         if (hang)
591             DEBUG1 ("WFSO pid=%d timed out\n", (int)pid);
592         break;
593
594       default:
595         DEBUG2 ("WFSO pid=%d returned %d\n", (int)pid, code );
596         break;
597     }
598     return ret;
599 }
600
601 int
602 _gpgme_io_kill ( int pid, int hard )
603 {
604     HANDLE proc = fd_to_handle (pid);
605
606     #warning I am not sure how to kill a process
607     /* fixme: figure out how this can be done */
608     return 0;
609 }
610
611
612
613 /*
614  * Select on the list of fds.
615  * Returns: -1 = error
616  *           0 = timeout or nothing to select
617  *          >0 = number of signaled fds
618  */
619 int
620 _gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds )
621 {
622 #if 1
623     HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
624     int    waitidx[MAXIMUM_WAIT_OBJECTS];
625     int code, nwait;
626     int i, any, any_write;
627     int count;
628     void *dbg_help;
629
630  restart:
631     DEBUG_BEGIN (dbg_help, "select on [ ");
632     any = any_write = 0;
633     nwait = 0;
634     for ( i=0; i < nfds; i++ ) {
635         if ( fds[i].fd == -1 ) 
636             continue;
637         if ( fds[i].for_read || fds[i].for_write ) {
638             if ( fds[i].for_read ) {
639                 struct reader_context_s *c = find_reader (fds[i].fd,1);
640                 
641                 if (!c) { 
642                     DEBUG1 ("oops: no reader thread for fd %d", fds[i].fd);
643                 }
644                 else {
645                     if ( nwait >= DIM (waitbuf) ) {
646                         DEBUG_END (dbg_help, "oops ]");
647                         DEBUG0 ("Too many objects for WFMO!" );
648                         return -1;
649                     }
650                     waitidx[nwait]   = i;
651                     waitbuf[nwait++] = c->have_data_ev;
652                 }
653             }
654             DEBUG_ADD2 (dbg_help, "%c%d ",
655                         fds[i].for_read? 'r':'w',fds[i].fd );
656             any = 1;
657         }
658         fds[i].signaled = 0;
659     }
660     DEBUG_END (dbg_help, "]");
661     if (!any) 
662         return 0;
663
664     count = 0;
665     /* no way to see whether a handle is ready for writing, signal all */
666     for ( i=0; i < nfds; i++ ) {
667         if ( fds[i].fd == -1 ) 
668             continue;
669         if ( fds[i].for_write ) {
670             fds[i].signaled = 1;
671             any_write =1;
672             count++;
673         }
674     }
675     code = WaitForMultipleObjects ( nwait, waitbuf, 0, any_write? 200:1000);
676     if ( code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait ) {
677         /* This WFMO is a really silly function:  It does return either
678          * the index of the signaled object or if 2 objects have been
679          * signalled at the same time, the index of the object with the
680          * lowest object is returned - so and how do we find out
681          * how many objects have been signaled???.
682          * The only solution I can imagine is to test each object starting
683          * with the returned index individually - how dull.
684          */
685         any = 0;
686         for (i=code - WAIT_OBJECT_0; i < nwait; i++ ) {
687             if (WaitForSingleObject ( waitbuf[i], NULL ) == WAIT_OBJECT_0) {
688                 assert (waitidx[i] >=0 && waitidx[i] < nfds);
689                 fds[waitidx[i]].signaled = 1;
690                 any = 1;
691                 count++;
692             }
693         }
694         if (!any) {
695             DEBUG0 ("Oops: No signaled objects found after WFMO");
696             count = -1;
697         }
698     }
699     else if ( code == WAIT_TIMEOUT ) {
700         DEBUG0 ("WFMO timed out\n" );
701     }  
702     else if (code == WAIT_FAILED ) {
703         int le = (int)GetLastError ();
704         if ( le == ERROR_INVALID_HANDLE ) {
705             int k, j = handle_to_fd (waitbuf[i]);
706                     
707             DEBUG1 ("WFMO invalid handle %d removed\n", j);
708             for (k=0 ; k < nfds; i++ ) {
709                 if ( fds[k].fd == j ) {
710                     fds[k].for_read = fds[k].for_write = 0;
711                     goto restart;
712                 }
713             }
714             DEBUG0 (" oops, or not???\n");
715         }
716         DEBUG1 ("WFMO failed: %d\n", le );
717         count = -1;
718     }
719     else {
720         DEBUG1 ("WFMO returned %d\n", code );
721         count = -1;
722     }
723
724     if ( count ) {
725         DEBUG_BEGIN (dbg_help, " signaled [ ");
726         for ( i=0; i < nfds; i++ ) {
727             if ( fds[i].fd == -1 ) 
728                 continue;
729             if ( (fds[i].for_read || fds[i].for_write) && fds[i].signaled ) {
730                 DEBUG_ADD2 (dbg_help, "%c%d ",
731                             fds[i].for_read? 'r':'w',fds[i].fd );
732             }
733         }
734         DEBUG_END (dbg_help, "]");
735     }
736     
737     return count;
738 #else  /* This is the code we use */
739     int i, any, count;
740     int once_more = 0;
741
742     DEBUG_SELECT ((stderr, "gpgme:fakedselect on [ "));
743     any = 0;
744     for ( i=0; i < nfds; i++ ) {
745         if ( fds[i].fd == -1 ) 
746             continue;
747         if ( fds[i].for_read || fds[i].for_write ) {
748             DEBUG_SELECT ((stderr, "%c%d ",
749                            fds[i].for_read? 'r':'w',fds[i].fd ));
750             any = 1;
751         }
752         fds[i].signaled = 0;
753     }
754     DEBUG_SELECT ((stderr, "]\n" ));
755     if (!any) 
756         return 0;
757
758  restart:
759     count = 0;
760     /* no way to see whether a handle is ready fro writing, signal all */
761     for ( i=0; i < nfds; i++ ) {
762         if ( fds[i].fd == -1 ) 
763             continue;
764         if ( fds[i].for_write ) {
765             fds[i].signaled = 1;
766             count++;
767         }
768     }
769
770     /* now peek on all read handles */
771     for ( i=0; i < nfds; i++ ) {
772         if ( fds[i].fd == -1 ) 
773             continue;
774         if ( fds[i].for_read ) {
775             int navail;
776             
777             if ( !PeekNamedPipe (fd_to_handle (fds[i].fd),
778                                  NULL, 0, NULL, &navail, NULL) ) {
779                 DEBUG1 ("select: PeekFile failed: ec=%d\n",
780                         (int)GetLastError ());
781             }
782             else if ( navail ) {
783                 DEBUG2 ("fd %d has %d bytes to read\n",  fds[i].fd, navail );
784                 fds[i].signaled = 1;
785                 count++;
786             }
787         }
788     }
789     if ( !once_more && !count ) {
790         /* once more but after relinquishing our timeslot */
791         once_more = 1;
792         Sleep (0);
793         goto restart;
794     }
795
796     if ( count ) {
797         DEBUG_SELECT ((stderr, "gpgme:      signaled [ "));
798         for ( i=0; i < nfds; i++ ) {
799             if ( fds[i].fd == -1 ) 
800                 continue;
801             if ( (fds[i].for_read || fds[i].for_write) && fds[i].signaled ) {
802                 DEBUG_SELECT ((stderr, "%c%d ",
803                                fds[i].for_read? 'r':'w',fds[i].fd ));
804             }
805         }
806         DEBUG_SELECT ((stderr, "]\n" ));
807     }
808     
809     return count;
810 #endif
811 }
812
813 #endif /*HAVE_DOSISH_SYSTEM*/
814
815
816
817
818
819
820
821
822