Initial import.
[profile/ivi/apr.git] / file_io / win32 / open.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "apr_private.h"
18 #include "apr_arch_file_io.h"
19 #include "apr_file_io.h"
20 #include "apr_general.h"
21 #include "apr_strings.h"
22 #include "apr_portable.h"
23 #include "apr_thread_mutex.h"
24 #if APR_HAVE_ERRNO_H
25 #include <errno.h>
26 #endif
27 #include <winbase.h>
28 #include <string.h>
29 #if APR_HAVE_SYS_STAT_H
30 #include <sys/stat.h>
31 #endif
32 #include "apr_arch_misc.h"
33 #include "apr_arch_inherit.h"
34 #include <io.h>
35 #include <winioctl.h>
36
37 #if APR_HAS_UNICODE_FS
38 apr_status_t utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen, 
39                                   const char* srcstr)
40 {
41     /* TODO: The computations could preconvert the string to determine
42      * the true size of the retstr, but that's a memory over speed
43      * tradeoff that isn't appropriate this early in development.
44      *
45      * Allocate the maximum string length based on leading 4 
46      * characters of \\?\ (allowing nearly unlimited path lengths) 
47      * plus the trailing null, then transform /'s into \\'s since
48      * the \\?\ form doesn't allow '/' path seperators.
49      *
50      * Note that the \\?\ form only works for local drive paths, and
51      * \\?\UNC\ is needed UNC paths.
52      */
53     apr_size_t srcremains = strlen(srcstr) + 1;
54     apr_wchar_t *t = retstr;
55     apr_status_t rv;
56
57     /* This is correct, we don't twist the filename if it is will
58      * definitely be shorter than 248 characters.  It merits some 
59      * performance testing to see if this has any effect, but there
60      * seem to be applications that get confused by the resulting
61      * Unicode \\?\ style file names, especially if they use argv[0]
62      * or call the Win32 API functions such as GetModuleName, etc.
63      * Not every application is prepared to handle such names.
64      * 
65      * Note also this is shorter than MAX_PATH, as directory paths 
66      * are actually limited to 248 characters. 
67      *
68      * Note that a utf-8 name can never result in more wide chars
69      * than the original number of utf-8 narrow chars.
70      */
71     if (srcremains > 248) {
72         if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
73             wcscpy (retstr, L"\\\\?\\");
74             retlen -= 4;
75             t += 4;
76         }
77         else if ((srcstr[0] == '/' || srcstr[0] == '\\')
78               && (srcstr[1] == '/' || srcstr[1] == '\\')
79               && (srcstr[2] != '?')) {
80             /* Skip the slashes */
81             srcstr += 2;
82             srcremains -= 2;
83             wcscpy (retstr, L"\\\\?\\UNC\\");
84             retlen -= 8;
85             t += 8;
86         }
87     }
88
89     if ((rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen))) {
90         return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
91     }
92     if (srcremains) {
93         return APR_ENAMETOOLONG;
94     }
95     for (; *t; ++t)
96         if (*t == L'/')
97             *t = L'\\';
98     return APR_SUCCESS;
99 }
100
101 apr_status_t unicode_to_utf8_path(char* retstr, apr_size_t retlen,
102                                   const apr_wchar_t* srcstr)
103 {
104     /* Skip the leading 4 characters if the path begins \\?\, or substitute
105      * // for the \\?\UNC\ path prefix, allocating the maximum string
106      * length based on the remaining string, plus the trailing null.
107      * then transform \\'s back into /'s since the \\?\ form never
108      * allows '/' path seperators, and APR always uses '/'s.
109      */
110     apr_size_t srcremains = wcslen(srcstr) + 1;
111     apr_status_t rv;
112     char *t = retstr;
113     if (srcstr[0] == L'\\' && srcstr[1] == L'\\' && 
114         srcstr[2] == L'?'  && srcstr[3] == L'\\') {
115         if (srcstr[4] == L'U' && srcstr[5] == L'N' && 
116             srcstr[6] == L'C' && srcstr[7] == L'\\') {
117             srcremains -= 8;
118             srcstr += 8;
119             retstr[0] = '\\';
120             retstr[1] = '\\';
121             retlen -= 2;
122             t += 2;
123         }
124         else {
125             srcremains -= 4;
126             srcstr += 4;
127         }
128     }
129         
130     if ((rv = apr_conv_ucs2_to_utf8(srcstr, &srcremains, t, &retlen))) {
131         return rv;
132     }
133     if (srcremains) {
134         return APR_ENAMETOOLONG;
135     }
136     return APR_SUCCESS;
137 }
138 #endif
139
140 void *res_name_from_filename(const char *file, int global, apr_pool_t *pool)
141 {
142 #if APR_HAS_UNICODE_FS
143     IF_WIN_OS_IS_UNICODE
144     {
145         apr_wchar_t *wpre, *wfile, *ch;
146         apr_size_t n = strlen(file) + 1;
147         apr_size_t r, d;
148
149         if (apr_os_level >= APR_WIN_2000) {
150             if (global)
151                 wpre = L"Global\\";
152             else
153                 wpre = L"Local\\";
154         }
155         else
156             wpre = L"";
157         r = wcslen(wpre);
158
159         if (n > 256 - r) {
160             file += n - 256 - r;
161             n = 256;
162             /* skip utf8 continuation bytes */
163             while ((*file & 0xC0) == 0x80) {
164                 ++file;
165                 --n;
166             }
167         }
168         wfile = apr_palloc(pool, (r + n) * sizeof(apr_wchar_t));
169         wcscpy(wfile, wpre);
170         d = n;
171         if (apr_conv_utf8_to_ucs2(file, &n, wfile + r, &d)) {
172             return NULL;
173         }
174         for (ch = wfile + r; *ch; ++ch) {
175             if (*ch == ':' || *ch == '/' || *ch == '\\')
176                 *ch = '_';
177         }
178         return wfile;
179     }
180 #endif
181 #if APR_HAS_ANSI_FS
182     ELSE_WIN_OS_IS_ANSI
183     {
184         char *nfile, *ch;
185         apr_size_t n = strlen(file) + 1;
186
187 #if !APR_HAS_UNICODE_FS
188         apr_size_t r, d;
189         char *pre;
190
191         if (apr_os_level >= APR_WIN_2000) {
192             if (global)
193                 pre = "Global\\";
194             else
195                 pre = "Local\\";
196         }
197         else
198             pre = "";
199         r = strlen(pre);
200
201         if (n > 256 - r) {
202             file += n - 256 - r;
203             n = 256;
204         }
205         nfile = apr_palloc(pool, (r + n) * sizeof(apr_wchar_t));
206         memcpy(nfile, pre, r);
207         memcpy(nfile + r, file, n);
208 #else
209         const apr_size_t r = 0;
210         if (n > 256) {
211             file += n - 256;
212             n = 256;
213         }
214         nfile = apr_pmemdup(pool, file, n);
215 #endif
216         for (ch = nfile + r; *ch; ++ch) {
217             if (*ch == ':' || *ch == '/' || *ch == '\\')
218                 *ch = '_';
219         }
220         return nfile;
221     }
222 #endif
223 }
224
225 #if APR_HAS_UNICODE_FS
226 static apr_status_t make_sparse_file(apr_file_t *file)
227 {
228     BY_HANDLE_FILE_INFORMATION info;
229     apr_status_t rv;
230     DWORD bytesread = 0;
231     DWORD res;
232
233     /* test */
234
235     if (GetFileInformationByHandle(file->filehand, &info)
236             && (info.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE))
237         return APR_SUCCESS;
238
239     if (file->pOverlapped) {
240         file->pOverlapped->Offset     = 0;
241         file->pOverlapped->OffsetHigh = 0;
242     }
243
244     if (DeviceIoControl(file->filehand, FSCTL_SET_SPARSE, NULL, 0, NULL, 0,
245                         &bytesread, file->pOverlapped)) {
246         rv = APR_SUCCESS;
247     }
248     else 
249     {
250         rv = apr_get_os_error();
251
252         if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING))
253         {
254             do {
255                 res = WaitForSingleObject(file->pOverlapped->hEvent, 
256                                           (file->timeout > 0)
257                                             ? (DWORD)(file->timeout/1000)
258                                             : ((file->timeout == -1) 
259                                                  ? INFINITE : 0));
260             } while (res == WAIT_ABANDONED);
261
262             if (res != WAIT_OBJECT_0) {
263                 CancelIo(file->filehand);
264             }
265
266             if (GetOverlappedResult(file->filehand, file->pOverlapped, 
267                                     &bytesread, TRUE))
268                 rv = APR_SUCCESS;
269             else
270                 rv = apr_get_os_error();
271         }
272     }
273     return rv;
274 }
275 #endif
276
277 apr_status_t file_cleanup(void *thefile)
278 {
279     apr_file_t *file = thefile;
280     apr_status_t flush_rv = APR_SUCCESS;
281
282     if (file->filehand != INVALID_HANDLE_VALUE) {
283
284         if (file->buffered) {
285             /* XXX: flush here is not mutex protected */
286             flush_rv = apr_file_flush((apr_file_t *)thefile);
287         }
288
289         /* In order to avoid later segfaults with handle 'reuse',
290          * we must protect against the case that a dup2'ed handle
291          * is being closed, and invalidate the corresponding StdHandle 
292          * We also tell msvcrt when stdhandles are closed.
293          */
294         if (file->flags & APR_STD_FLAGS)
295         {
296             if ((file->flags & APR_STD_FLAGS) == APR_STDERR_FLAG) {
297                 _close(2);
298                 SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
299             }
300             else if ((file->flags & APR_STD_FLAGS) == APR_STDOUT_FLAG) {
301                 _close(1);
302                 SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
303             }
304             else if ((file->flags & APR_STD_FLAGS) == APR_STDIN_FLAG) {
305                 _close(0);
306                 SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE);
307             }
308         }
309         else
310             CloseHandle(file->filehand);
311
312         file->filehand = INVALID_HANDLE_VALUE;
313     }
314     if (file->pOverlapped && file->pOverlapped->hEvent) {
315         CloseHandle(file->pOverlapped->hEvent);
316         file->pOverlapped = NULL;
317     }
318     return flush_rv;
319 }
320
321 APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **new, const char *fname,
322                                    apr_int32_t flag, apr_fileperms_t perm,
323                                    apr_pool_t *pool)
324 {
325     HANDLE handle = INVALID_HANDLE_VALUE;
326     DWORD oflags = 0;
327     DWORD createflags = 0;
328     DWORD attributes = 0;
329     DWORD sharemode = FILE_SHARE_READ | FILE_SHARE_WRITE;
330     apr_status_t rv;
331
332     if (flag & APR_FOPEN_NONBLOCK) {
333         return APR_ENOTIMPL;
334     }
335     if (flag & APR_FOPEN_READ) {
336         oflags |= GENERIC_READ;
337     }
338     if (flag & APR_FOPEN_WRITE) {
339         oflags |= GENERIC_WRITE;
340     }
341     if (flag & APR_WRITEATTRS) {
342         oflags |= FILE_WRITE_ATTRIBUTES;
343     }
344
345     if (apr_os_level >= APR_WIN_NT) 
346         sharemode |= FILE_SHARE_DELETE;
347
348     if (flag & APR_FOPEN_CREATE) {
349         if (flag & APR_FOPEN_EXCL) {
350             /* only create new if file does not already exist */
351             createflags = CREATE_NEW;
352         } else if (flag & APR_FOPEN_TRUNCATE) {
353             /* truncate existing file or create new */
354             createflags = CREATE_ALWAYS;
355         } else {
356             /* open existing but create if necessary */
357             createflags = OPEN_ALWAYS;
358         }
359     } else if (flag & APR_FOPEN_TRUNCATE) {
360         /* only truncate if file already exists */
361         createflags = TRUNCATE_EXISTING;
362     } else {
363         /* only open if file already exists */
364         createflags = OPEN_EXISTING;
365     }
366
367     if ((flag & APR_FOPEN_EXCL) && !(flag & APR_FOPEN_CREATE)) {
368         return APR_EACCES;
369     }   
370     
371     if (flag & APR_FOPEN_DELONCLOSE) {
372         attributes |= FILE_FLAG_DELETE_ON_CLOSE;
373     }
374
375     if (flag & APR_OPENLINK) {
376        attributes |= FILE_FLAG_OPEN_REPARSE_POINT;
377     }
378
379     /* Without READ or WRITE, we fail unless apr called apr_file_open
380      * internally with the private APR_OPENINFO flag.
381      *
382      * With the APR_OPENINFO flag on NT, use the option flag
383      * FILE_FLAG_BACKUP_SEMANTICS to allow us to open directories.
384      * See the static resolve_ident() fn in file_io/win32/filestat.c
385      */
386     if (!(flag & (APR_FOPEN_READ | APR_FOPEN_WRITE))) {
387         if (flag & APR_OPENINFO) {
388             if (apr_os_level >= APR_WIN_NT) {
389                 attributes |= FILE_FLAG_BACKUP_SEMANTICS;
390             }
391         }
392         else {
393             return APR_EACCES;
394         }
395         if (flag & APR_READCONTROL)
396             oflags |= READ_CONTROL;
397     }
398
399     if (flag & APR_FOPEN_XTHREAD) {
400         /* This win32 specific feature is required 
401          * to allow multiple threads to work with the file.
402          */
403         attributes |= FILE_FLAG_OVERLAPPED;
404     }
405
406 #if APR_HAS_UNICODE_FS
407     IF_WIN_OS_IS_UNICODE
408     {
409         apr_wchar_t wfname[APR_PATH_MAX];
410
411         if (flag & APR_FOPEN_SENDFILE_ENABLED) {
412             /* This feature is required to enable sendfile operations
413              * against the file on Win32. Also implies APR_FOPEN_XTHREAD.
414              */
415             flag |= APR_FOPEN_XTHREAD;
416             attributes |= FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED;
417         }
418
419         if ((rv = utf8_to_unicode_path(wfname, sizeof(wfname) 
420                                              / sizeof(apr_wchar_t), fname)))
421             return rv;
422         handle = CreateFileW(wfname, oflags, sharemode,
423                              NULL, createflags, attributes, 0);
424     }
425 #endif
426 #if APR_HAS_ANSI_FS
427     ELSE_WIN_OS_IS_ANSI {
428         handle = CreateFileA(fname, oflags, sharemode,
429                              NULL, createflags, attributes, 0);
430         /* This feature is not supported on this platform. */
431         flag &= ~APR_FOPEN_SENDFILE_ENABLED;
432     }
433 #endif
434     if (handle == INVALID_HANDLE_VALUE) {
435         return apr_get_os_error();
436     }
437
438     (*new) = (apr_file_t *)apr_pcalloc(pool, sizeof(apr_file_t));
439     (*new)->pool = pool;
440     (*new)->filehand = handle;
441     (*new)->fname = apr_pstrdup(pool, fname);
442     (*new)->flags = flag;
443     (*new)->timeout = -1;
444     (*new)->ungetchar = -1;
445
446     if (flag & APR_FOPEN_APPEND) {
447         (*new)->append = 1;
448         SetFilePointer((*new)->filehand, 0, NULL, FILE_END);
449     }
450     if (flag & APR_FOPEN_BUFFERED) {
451         (*new)->buffered = 1;
452         (*new)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
453         (*new)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
454     }
455     /* Need the mutex to handled buffered and O_APPEND style file i/o */
456     if ((*new)->buffered || (*new)->append) {
457         rv = apr_thread_mutex_create(&(*new)->mutex, 
458                                      APR_THREAD_MUTEX_DEFAULT, pool);
459         if (rv) {
460             if (file_cleanup(*new) == APR_SUCCESS) {
461                 apr_pool_cleanup_kill(pool, *new, file_cleanup);
462             }
463             return rv;
464         }
465     }
466
467 #if APR_HAS_UNICODE_FS
468     if ((apr_os_level >= APR_WIN_2000) && ((*new)->flags & APR_FOPEN_SPARSE)) {
469         if ((rv = make_sparse_file(*new)) != APR_SUCCESS)
470             /* The great mystery; do we close the file and return an error?
471              * Do we add a new APR_INCOMPLETE style error saying opened, but
472              * NOTSPARSE?  For now let's simply mark the file as not-sparse.
473              */
474             (*new)->flags &= ~APR_FOPEN_SPARSE;
475     }
476     else
477 #endif
478         /* This feature is not supported on this platform. */
479         (*new)->flags &= ~APR_FOPEN_SPARSE;
480
481 #if APR_FILES_AS_SOCKETS
482     /* Create a pollset with room for one descriptor. */
483     /* ### check return codes */
484     (void) apr_pollset_create(&(*new)->pollset, 1, pool, 0);
485 #endif
486     if (!(flag & APR_FOPEN_NOCLEANUP)) {
487         apr_pool_cleanup_register((*new)->pool, (void *)(*new), file_cleanup,
488                                   apr_pool_cleanup_null);
489     }
490     return APR_SUCCESS;
491 }
492
493 APR_DECLARE(apr_status_t) apr_file_close(apr_file_t *file)
494 {
495     apr_status_t stat;
496     if ((stat = file_cleanup(file)) == APR_SUCCESS) {
497         apr_pool_cleanup_kill(file->pool, file, file_cleanup);
498
499         if (file->mutex) {
500             apr_thread_mutex_destroy(file->mutex);
501         }
502
503         return APR_SUCCESS;
504     }
505     return stat;
506 }
507
508 APR_DECLARE(apr_status_t) apr_file_remove(const char *path, apr_pool_t *pool)
509 {
510 #if APR_HAS_UNICODE_FS
511     IF_WIN_OS_IS_UNICODE
512     {
513         apr_wchar_t wpath[APR_PATH_MAX];
514         apr_status_t rv;
515         if ((rv = utf8_to_unicode_path(wpath, sizeof(wpath) 
516                                             / sizeof(apr_wchar_t), path))) {
517             return rv;
518         }
519         if (DeleteFileW(wpath))
520             return APR_SUCCESS;
521     }
522 #endif
523 #if APR_HAS_ANSI_FS
524     ELSE_WIN_OS_IS_ANSI
525         if (DeleteFile(path))
526             return APR_SUCCESS;
527 #endif
528     return apr_get_os_error();
529 }
530
531 APR_DECLARE(apr_status_t) apr_file_rename(const char *frompath,
532                                           const char *topath,
533                                           apr_pool_t *pool)
534 {
535     IF_WIN_OS_IS_UNICODE
536     {
537 #if APR_HAS_UNICODE_FS
538         apr_wchar_t wfrompath[APR_PATH_MAX], wtopath[APR_PATH_MAX];
539         apr_status_t rv;
540         if ((rv = utf8_to_unicode_path(wfrompath,
541                                        sizeof(wfrompath) / sizeof(apr_wchar_t),
542                                        frompath))) {
543             return rv;
544         }
545         if ((rv = utf8_to_unicode_path(wtopath,
546                                        sizeof(wtopath) / sizeof(apr_wchar_t),
547                                        topath))) {
548             return rv;
549         }
550 #ifndef _WIN32_WCE
551         if (MoveFileExW(wfrompath, wtopath, MOVEFILE_REPLACE_EXISTING |
552                                             MOVEFILE_COPY_ALLOWED))
553 #else
554         if (MoveFileW(wfrompath, wtopath))
555 #endif
556             return APR_SUCCESS;
557 #else
558         if (MoveFileEx(frompath, topath, MOVEFILE_REPLACE_EXISTING |
559                                          MOVEFILE_COPY_ALLOWED))
560             return APR_SUCCESS;
561 #endif
562     }
563 #if APR_HAS_ANSI_FS
564     ELSE_WIN_OS_IS_ANSI
565     {
566         /* Windows 95 and 98 do not support MoveFileEx, so we'll use
567          * the old MoveFile function.  However, MoveFile requires that
568          * the new file not already exist...so we have to delete that
569          * file if it does.  Perhaps we should back up the to-be-deleted
570          * file in case something happens?
571          */
572         HANDLE handle = INVALID_HANDLE_VALUE;
573
574         if ((handle = CreateFile(topath, GENERIC_WRITE, 0, 0,  
575             OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE )
576         {
577             CloseHandle(handle);
578             if (!DeleteFile(topath))
579                 return apr_get_os_error();
580         }
581         if (MoveFile(frompath, topath))
582             return APR_SUCCESS;
583     }        
584 #endif
585     return apr_get_os_error();
586 }
587
588 APR_DECLARE(apr_status_t) apr_file_link(const char *from_path, 
589                                            const char *to_path)
590 {
591     apr_status_t rv = APR_SUCCESS;
592
593 #if APR_HAS_UNICODE_FS
594     IF_WIN_OS_IS_UNICODE
595     {
596         apr_wchar_t wfrom_path[APR_PATH_MAX];
597         apr_wchar_t wto_path[APR_PATH_MAX];
598
599         if ((rv = utf8_to_unicode_path(wfrom_path,
600                                        sizeof(wfrom_path) / sizeof(apr_wchar_t),
601                                        from_path)))
602             return rv;
603         if ((rv = utf8_to_unicode_path(wto_path,
604                                        sizeof(wto_path) / sizeof(apr_wchar_t),
605                                        to_path)))
606             return rv;
607
608         if (!CreateHardLinkW(wto_path, wfrom_path, NULL))
609                 return apr_get_os_error();
610     }
611 #endif
612 #if APR_HAS_ANSI_FS
613     ELSE_WIN_OS_IS_ANSI {
614         if (!CreateHardLinkA(to_path, from_path, NULL))
615                 return apr_get_os_error();
616     }
617 #endif
618     return rv;
619 }
620
621 APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile,
622                                           apr_file_t *file)
623 {
624     *thefile = file->filehand;
625     return APR_SUCCESS;
626 }
627
628 APR_DECLARE(apr_status_t) apr_os_file_put(apr_file_t **file,
629                                           apr_os_file_t *thefile,
630                                           apr_int32_t flags,
631                                           apr_pool_t *pool)
632 {
633     (*file) = apr_pcalloc(pool, sizeof(apr_file_t));
634     (*file)->pool = pool;
635     (*file)->filehand = *thefile;
636     (*file)->ungetchar = -1; /* no char avail */
637     (*file)->timeout = -1;
638     (*file)->flags = flags;
639
640     if (flags & APR_FOPEN_APPEND) {
641         (*file)->append = 1;
642     }
643     if (flags & APR_FOPEN_BUFFERED) {
644         (*file)->buffered = 1;
645         (*file)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
646         (*file)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
647     }
648
649     if ((*file)->append || (*file)->buffered) {
650         apr_status_t rv;
651         rv = apr_thread_mutex_create(&(*file)->mutex, 
652                                      APR_THREAD_MUTEX_DEFAULT, pool);
653         if (rv) {
654             return rv;
655         }
656     }
657
658 #if APR_FILES_AS_SOCKETS
659     /* Create a pollset with room for one descriptor. */
660     /* ### check return codes */
661     (void) apr_pollset_create(&(*file)->pollset, 1, pool, 0);
662 #endif
663     /* Should we be testing if thefile is a handle to 
664      * a PIPE and set up the mechanics appropriately?
665      *
666      *  (*file)->pipe;
667      */
668     return APR_SUCCESS;
669 }    
670
671 APR_DECLARE(apr_status_t) apr_file_eof(apr_file_t *fptr)
672 {
673     if (fptr->eof_hit == 1) {
674         return APR_EOF;
675     }
676     return APR_SUCCESS;
677 }   
678
679 APR_DECLARE(apr_status_t) apr_file_open_flags_stderr(apr_file_t **thefile, 
680                                                      apr_int32_t flags, 
681                                                      apr_pool_t *pool)
682 {
683 #ifdef _WIN32_WCE
684     return APR_ENOTIMPL;
685 #else
686     apr_os_file_t file_handle;
687
688     apr_set_os_error(APR_SUCCESS);
689     file_handle = GetStdHandle(STD_ERROR_HANDLE);
690     if (!file_handle)
691         file_handle = INVALID_HANDLE_VALUE;
692
693     return apr_os_file_put(thefile, &file_handle,
694                            flags | APR_FOPEN_WRITE | APR_STDERR_FLAG, pool);
695 #endif
696 }
697
698 APR_DECLARE(apr_status_t) apr_file_open_flags_stdout(apr_file_t **thefile, 
699                                                      apr_int32_t flags,
700                                                      apr_pool_t *pool)
701 {
702 #ifdef _WIN32_WCE
703     return APR_ENOTIMPL;
704 #else
705     apr_os_file_t file_handle;
706
707     apr_set_os_error(APR_SUCCESS);
708     file_handle = GetStdHandle(STD_OUTPUT_HANDLE);
709     if (!file_handle)
710         file_handle = INVALID_HANDLE_VALUE;
711
712     return apr_os_file_put(thefile, &file_handle,
713                            flags | APR_FOPEN_WRITE | APR_STDOUT_FLAG, pool);
714 #endif
715 }
716
717 APR_DECLARE(apr_status_t) apr_file_open_flags_stdin(apr_file_t **thefile, 
718                                                     apr_int32_t flags,
719                                                     apr_pool_t *pool)
720 {
721 #ifdef _WIN32_WCE
722     return APR_ENOTIMPL;
723 #else
724     apr_os_file_t file_handle;
725
726     apr_set_os_error(APR_SUCCESS);
727     file_handle = GetStdHandle(STD_INPUT_HANDLE);
728     if (!file_handle)
729         file_handle = INVALID_HANDLE_VALUE;
730
731     return apr_os_file_put(thefile, &file_handle,
732                            flags | APR_FOPEN_READ | APR_STDIN_FLAG, pool);
733 #endif
734 }
735
736 APR_DECLARE(apr_status_t) apr_file_open_stderr(apr_file_t **thefile, apr_pool_t *pool)
737 {
738     return apr_file_open_flags_stderr(thefile, 0, pool);
739 }
740
741 APR_DECLARE(apr_status_t) apr_file_open_stdout(apr_file_t **thefile, apr_pool_t *pool)
742 {
743     return apr_file_open_flags_stdout(thefile, 0, pool);
744 }
745
746 APR_DECLARE(apr_status_t) apr_file_open_stdin(apr_file_t **thefile, apr_pool_t *pool)
747 {
748     return apr_file_open_flags_stdin(thefile, 0, pool);
749 }
750
751 APR_POOL_IMPLEMENT_ACCESSOR(file);
752
753 APR_IMPLEMENT_INHERIT_SET(file, flags, pool, file_cleanup)
754  
755 APR_IMPLEMENT_INHERIT_UNSET(file, flags, pool, file_cleanup)