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
38apr_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     * definately 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
101apr_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
140void *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
226static 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
277apr_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
321APR_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_READ) {
333        oflags |= GENERIC_READ;
334    }
335    if (flag & APR_FOPEN_WRITE) {
336        oflags |= GENERIC_WRITE;
337    }
338    if (flag & APR_WRITEATTRS) {
339        oflags |= FILE_WRITE_ATTRIBUTES;
340    }
341
342    if (apr_os_level >= APR_WIN_NT)
343        sharemode |= FILE_SHARE_DELETE;
344
345    if (flag & APR_FOPEN_CREATE) {
346        if (flag & APR_FOPEN_EXCL) {
347            /* only create new if file does not already exist */
348            createflags = CREATE_NEW;
349        } else if (flag & APR_FOPEN_TRUNCATE) {
350            /* truncate existing file or create new */
351            createflags = CREATE_ALWAYS;
352        } else {
353            /* open existing but create if necessary */
354            createflags = OPEN_ALWAYS;
355        }
356    } else if (flag & APR_FOPEN_TRUNCATE) {
357        /* only truncate if file already exists */
358        createflags = TRUNCATE_EXISTING;
359    } else {
360        /* only open if file already exists */
361        createflags = OPEN_EXISTING;
362    }
363
364    if ((flag & APR_FOPEN_EXCL) && !(flag & APR_FOPEN_CREATE)) {
365        return APR_EACCES;
366    }
367
368    if (flag & APR_FOPEN_DELONCLOSE) {
369        attributes |= FILE_FLAG_DELETE_ON_CLOSE;
370    }
371
372    if (flag & APR_OPENLINK) {
373       attributes |= FILE_FLAG_OPEN_REPARSE_POINT;
374    }
375
376    /* Without READ or WRITE, we fail unless apr called apr_file_open
377     * internally with the private APR_OPENINFO flag.
378     *
379     * With the APR_OPENINFO flag on NT, use the option flag
380     * FILE_FLAG_BACKUP_SEMANTICS to allow us to open directories.
381     * See the static resolve_ident() fn in file_io/win32/filestat.c
382     */
383    if (!(flag & (APR_FOPEN_READ | APR_FOPEN_WRITE))) {
384        if (flag & APR_OPENINFO) {
385            if (apr_os_level >= APR_WIN_NT) {
386                attributes |= FILE_FLAG_BACKUP_SEMANTICS;
387            }
388        }
389        else {
390            return APR_EACCES;
391        }
392        if (flag & APR_READCONTROL)
393            oflags |= READ_CONTROL;
394    }
395
396    if (flag & APR_FOPEN_XTHREAD) {
397        /* This win32 specific feature is required
398         * to allow multiple threads to work with the file.
399         */
400        attributes |= FILE_FLAG_OVERLAPPED;
401    }
402
403#if APR_HAS_UNICODE_FS
404    IF_WIN_OS_IS_UNICODE
405    {
406        apr_wchar_t wfname[APR_PATH_MAX];
407
408        if (flag & APR_FOPEN_SENDFILE_ENABLED) {
409            /* This feature is required to enable sendfile operations
410             * against the file on Win32. Also implies APR_FOPEN_XTHREAD.
411             */
412            flag |= APR_FOPEN_XTHREAD;
413            attributes |= FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED;
414        }
415
416        if ((rv = utf8_to_unicode_path(wfname, sizeof(wfname)
417                                             / sizeof(apr_wchar_t), fname)))
418            return rv;
419        handle = CreateFileW(wfname, oflags, sharemode,
420                             NULL, createflags, attributes, 0);
421    }
422#endif
423#if APR_HAS_ANSI_FS
424    ELSE_WIN_OS_IS_ANSI {
425        handle = CreateFileA(fname, oflags, sharemode,
426                             NULL, createflags, attributes, 0);
427        /* This feature is not supported on this platform. */
428        flag &= ~APR_FOPEN_SENDFILE_ENABLED;
429    }
430#endif
431    if (handle == INVALID_HANDLE_VALUE) {
432        return apr_get_os_error();
433    }
434
435    (*new) = (apr_file_t *)apr_pcalloc(pool, sizeof(apr_file_t));
436    (*new)->pool = pool;
437    (*new)->filehand = handle;
438    (*new)->fname = apr_pstrdup(pool, fname);
439    (*new)->flags = flag;
440    (*new)->timeout = -1;
441    (*new)->ungetchar = -1;
442
443    if (flag & APR_FOPEN_APPEND) {
444        (*new)->append = 1;
445        SetFilePointer((*new)->filehand, 0, NULL, FILE_END);
446    }
447    if (flag & APR_FOPEN_BUFFERED) {
448        (*new)->buffered = 1;
449        (*new)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
450        (*new)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
451    }
452    /* Need the mutex to handled buffered and O_APPEND style file i/o */
453    if ((*new)->buffered || (*new)->append) {
454        rv = apr_thread_mutex_create(&(*new)->mutex,
455                                     APR_THREAD_MUTEX_DEFAULT, pool);
456        if (rv) {
457            if (file_cleanup(*new) == APR_SUCCESS) {
458                apr_pool_cleanup_kill(pool, *new, file_cleanup);
459            }
460            return rv;
461        }
462    }
463
464#if APR_HAS_UNICODE_FS
465    if ((apr_os_level >= APR_WIN_2000) && ((*new)->flags & APR_FOPEN_SPARSE)) {
466        if ((rv = make_sparse_file(*new)) != APR_SUCCESS)
467            /* The great mystery; do we close the file and return an error?
468             * Do we add a new APR_INCOMPLETE style error saying opened, but
469             * NOTSPARSE?  For now let's simply mark the file as not-sparse.
470             */
471            (*new)->flags &= ~APR_FOPEN_SPARSE;
472    }
473    else
474#endif
475        /* This feature is not supported on this platform. */
476        (*new)->flags &= ~APR_FOPEN_SPARSE;
477
478    /* Create a pollset with room for one descriptor. */
479    /* ### check return codes */
480    (void) apr_pollset_create(&(*new)->pollset, 1, pool, 0);
481
482    if (!(flag & APR_FOPEN_NOCLEANUP)) {
483        apr_pool_cleanup_register((*new)->pool, (void *)(*new), file_cleanup,
484                                  apr_pool_cleanup_null);
485    }
486    return APR_SUCCESS;
487}
488
489APR_DECLARE(apr_status_t) apr_file_close(apr_file_t *file)
490{
491    apr_status_t stat;
492    if ((stat = file_cleanup(file)) == APR_SUCCESS) {
493        apr_pool_cleanup_kill(file->pool, file, file_cleanup);
494
495        if (file->mutex) {
496            apr_thread_mutex_destroy(file->mutex);
497        }
498
499        return APR_SUCCESS;
500    }
501    return stat;
502}
503
504APR_DECLARE(apr_status_t) apr_file_remove(const char *path, apr_pool_t *pool)
505{
506#if APR_HAS_UNICODE_FS
507    IF_WIN_OS_IS_UNICODE
508    {
509        apr_wchar_t wpath[APR_PATH_MAX];
510        apr_status_t rv;
511        if ((rv = utf8_to_unicode_path(wpath, sizeof(wpath)
512                                            / sizeof(apr_wchar_t), path))) {
513            return rv;
514        }
515        if (DeleteFileW(wpath))
516            return APR_SUCCESS;
517    }
518#endif
519#if APR_HAS_ANSI_FS
520    ELSE_WIN_OS_IS_ANSI
521        if (DeleteFile(path))
522            return APR_SUCCESS;
523#endif
524    return apr_get_os_error();
525}
526
527APR_DECLARE(apr_status_t) apr_file_rename(const char *frompath,
528                                          const char *topath,
529                                          apr_pool_t *pool)
530{
531    IF_WIN_OS_IS_UNICODE
532    {
533#if APR_HAS_UNICODE_FS
534        apr_wchar_t wfrompath[APR_PATH_MAX], wtopath[APR_PATH_MAX];
535        apr_status_t rv;
536        if ((rv = utf8_to_unicode_path(wfrompath,
537                                       sizeof(wfrompath) / sizeof(apr_wchar_t),
538                                       frompath))) {
539            return rv;
540        }
541        if ((rv = utf8_to_unicode_path(wtopath,
542                                       sizeof(wtopath) / sizeof(apr_wchar_t),
543                                       topath))) {
544            return rv;
545        }
546#ifndef _WIN32_WCE
547        if (MoveFileExW(wfrompath, wtopath, MOVEFILE_REPLACE_EXISTING |
548                                            MOVEFILE_COPY_ALLOWED))
549#else
550        if (MoveFileW(wfrompath, wtopath))
551#endif
552            return APR_SUCCESS;
553#else
554        if (MoveFileEx(frompath, topath, MOVEFILE_REPLACE_EXISTING |
555                                         MOVEFILE_COPY_ALLOWED))
556            return APR_SUCCESS;
557#endif
558    }
559#if APR_HAS_ANSI_FS
560    ELSE_WIN_OS_IS_ANSI
561    {
562        /* Windows 95 and 98 do not support MoveFileEx, so we'll use
563         * the old MoveFile function.  However, MoveFile requires that
564         * the new file not already exist...so we have to delete that
565         * file if it does.  Perhaps we should back up the to-be-deleted
566         * file in case something happens?
567         */
568        HANDLE handle = INVALID_HANDLE_VALUE;
569
570        if ((handle = CreateFile(topath, GENERIC_WRITE, 0, 0,
571            OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE )
572        {
573            CloseHandle(handle);
574            if (!DeleteFile(topath))
575                return apr_get_os_error();
576        }
577        if (MoveFile(frompath, topath))
578            return APR_SUCCESS;
579    }
580#endif
581    return apr_get_os_error();
582}
583
584APR_DECLARE(apr_status_t) apr_file_link(const char *from_path,
585                                           const char *to_path)
586{
587    apr_status_t rv = APR_SUCCESS;
588
589#if APR_HAS_UNICODE_FS
590    IF_WIN_OS_IS_UNICODE
591    {
592        apr_wchar_t wfrom_path[APR_PATH_MAX];
593        apr_wchar_t wto_path[APR_PATH_MAX];
594
595        if ((rv = utf8_to_unicode_path(wfrom_path,
596                                       sizeof(wfrom_path) / sizeof(apr_wchar_t),
597                                       from_path)))
598            return rv;
599        if ((rv = utf8_to_unicode_path(wto_path,
600                                       sizeof(wto_path) / sizeof(apr_wchar_t),
601                                       to_path)))
602            return rv;
603
604        if (!CreateHardLinkW(wto_path, wfrom_path, NULL))
605                return apr_get_os_error();
606    }
607#endif
608#if APR_HAS_ANSI_FS
609    ELSE_WIN_OS_IS_ANSI {
610        if (!CreateHardLinkA(to_path, from_path, NULL))
611                return apr_get_os_error();
612    }
613#endif
614    return rv;
615}
616
617APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile,
618                                          apr_file_t *file)
619{
620    *thefile = file->filehand;
621    return APR_SUCCESS;
622}
623
624APR_DECLARE(apr_status_t) apr_os_file_put(apr_file_t **file,
625                                          apr_os_file_t *thefile,
626                                          apr_int32_t flags,
627                                          apr_pool_t *pool)
628{
629    (*file) = apr_pcalloc(pool, sizeof(apr_file_t));
630    (*file)->pool = pool;
631    (*file)->filehand = *thefile;
632    (*file)->ungetchar = -1; /* no char avail */
633    (*file)->timeout = -1;
634    (*file)->flags = flags;
635
636    if (flags & APR_FOPEN_APPEND) {
637        (*file)->append = 1;
638    }
639    if (flags & APR_FOPEN_BUFFERED) {
640        (*file)->buffered = 1;
641        (*file)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
642        (*file)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
643    }
644
645    if ((*file)->append || (*file)->buffered) {
646        apr_status_t rv;
647        rv = apr_thread_mutex_create(&(*file)->mutex,
648                                     APR_THREAD_MUTEX_DEFAULT, pool);
649        if (rv) {
650            if (file_cleanup(*file) == APR_SUCCESS) {
651                apr_pool_cleanup_kill(pool, *file, file_cleanup);
652            }
653            return rv;
654        }
655    }
656
657    /* Create a pollset with room for one descriptor. */
658    /* ### check return codes */
659    (void) apr_pollset_create(&(*file)->pollset, 1, pool, 0);
660
661    /* Should we be testing if thefile is a handle to
662     * a PIPE and set up the mechanics appropriately?
663     *
664     *  (*file)->pipe;
665     */
666    return APR_SUCCESS;
667}
668
669APR_DECLARE(apr_status_t) apr_file_eof(apr_file_t *fptr)
670{
671    if (fptr->eof_hit == 1) {
672        return APR_EOF;
673    }
674    return APR_SUCCESS;
675}
676
677APR_DECLARE(apr_status_t) apr_file_open_flags_stderr(apr_file_t **thefile,
678                                                     apr_int32_t flags,
679                                                     apr_pool_t *pool)
680{
681#ifdef _WIN32_WCE
682    return APR_ENOTIMPL;
683#else
684    apr_os_file_t file_handle;
685
686    apr_set_os_error(APR_SUCCESS);
687    file_handle = GetStdHandle(STD_ERROR_HANDLE);
688    if (!file_handle)
689        file_handle = INVALID_HANDLE_VALUE;
690
691    return apr_os_file_put(thefile, &file_handle,
692                           flags | APR_FOPEN_WRITE | APR_STDERR_FLAG, pool);
693#endif
694}
695
696APR_DECLARE(apr_status_t) apr_file_open_flags_stdout(apr_file_t **thefile,
697                                                     apr_int32_t flags,
698                                                     apr_pool_t *pool)
699{
700#ifdef _WIN32_WCE
701    return APR_ENOTIMPL;
702#else
703    apr_os_file_t file_handle;
704
705    apr_set_os_error(APR_SUCCESS);
706    file_handle = GetStdHandle(STD_OUTPUT_HANDLE);
707    if (!file_handle)
708        file_handle = INVALID_HANDLE_VALUE;
709
710    return apr_os_file_put(thefile, &file_handle,
711                           flags | APR_FOPEN_WRITE | APR_STDOUT_FLAG, pool);
712#endif
713}
714
715APR_DECLARE(apr_status_t) apr_file_open_flags_stdin(apr_file_t **thefile,
716                                                    apr_int32_t flags,
717                                                    apr_pool_t *pool)
718{
719#ifdef _WIN32_WCE
720    return APR_ENOTIMPL;
721#else
722    apr_os_file_t file_handle;
723
724    apr_set_os_error(APR_SUCCESS);
725    file_handle = GetStdHandle(STD_INPUT_HANDLE);
726    if (!file_handle)
727        file_handle = INVALID_HANDLE_VALUE;
728
729    return apr_os_file_put(thefile, &file_handle,
730                           flags | APR_FOPEN_READ | APR_STDIN_FLAG, pool);
731#endif
732}
733
734APR_DECLARE(apr_status_t) apr_file_open_stderr(apr_file_t **thefile, apr_pool_t *pool)
735{
736    return apr_file_open_flags_stderr(thefile, 0, pool);
737}
738
739APR_DECLARE(apr_status_t) apr_file_open_stdout(apr_file_t **thefile, apr_pool_t *pool)
740{
741    return apr_file_open_flags_stdout(thefile, 0, pool);
742}
743
744APR_DECLARE(apr_status_t) apr_file_open_stdin(apr_file_t **thefile, apr_pool_t *pool)
745{
746    return apr_file_open_flags_stdin(thefile, 0, pool);
747}
748
749APR_POOL_IMPLEMENT_ACCESSOR(file);
750
751APR_IMPLEMENT_INHERIT_SET(file, flags, pool, file_cleanup)
752
753APR_IMPLEMENT_INHERIT_UNSET(file, flags, pool, file_cleanup)
754