12b15cb3dSCy Schubert /**
22b15cb3dSCy Schubert * @file text_mmap.c
3ea906c41SOllivier Robert *
42b15cb3dSCy Schubert * Map a text file, ensuring the text always has an ending NUL byte.
52b15cb3dSCy Schubert *
62b15cb3dSCy Schubert * @addtogroup autoopts
72b15cb3dSCy Schubert * @{
8ea906c41SOllivier Robert */
92b15cb3dSCy Schubert /*
102b15cb3dSCy Schubert * This file is part of AutoOpts, a companion to AutoGen.
112b15cb3dSCy Schubert * AutoOpts is free software.
12*a466cc55SCy Schubert * AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
132b15cb3dSCy Schubert *
142b15cb3dSCy Schubert * AutoOpts is available under any one of two licenses. The license
152b15cb3dSCy Schubert * in use must be one of these two and the choice is under the control
162b15cb3dSCy Schubert * of the user of the license.
172b15cb3dSCy Schubert *
182b15cb3dSCy Schubert * The GNU Lesser General Public License, version 3 or later
192b15cb3dSCy Schubert * See the files "COPYING.lgplv3" and "COPYING.gplv3"
202b15cb3dSCy Schubert *
212b15cb3dSCy Schubert * The Modified Berkeley Software Distribution License
222b15cb3dSCy Schubert * See the file "COPYING.mbsd"
232b15cb3dSCy Schubert *
242b15cb3dSCy Schubert * These files have the following sha256 sums:
252b15cb3dSCy Schubert *
262b15cb3dSCy Schubert * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
272b15cb3dSCy Schubert * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
282b15cb3dSCy Schubert * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
292b15cb3dSCy Schubert */
302b15cb3dSCy Schubert #if defined(HAVE_MMAP)
31ea906c41SOllivier Robert # ifndef MAP_ANONYMOUS
32ea906c41SOllivier Robert # ifdef MAP_ANON
33ea906c41SOllivier Robert # define MAP_ANONYMOUS MAP_ANON
34ea906c41SOllivier Robert # endif
35ea906c41SOllivier Robert # endif
36ea906c41SOllivier Robert
372b15cb3dSCy Schubert # if ! defined(MAP_ANONYMOUS) && ! defined(HAVE_DEV_ZERO)
382b15cb3dSCy Schubert /*
392b15cb3dSCy Schubert * We must have either /dev/zero or anonymous mapping for
402b15cb3dSCy Schubert * this to work.
412b15cb3dSCy Schubert */
422b15cb3dSCy Schubert # undef HAVE_MMAP
432b15cb3dSCy Schubert
442b15cb3dSCy Schubert # else
452b15cb3dSCy Schubert # ifdef _SC_PAGESIZE
462b15cb3dSCy Schubert # define GETPAGESIZE() sysconf(_SC_PAGESIZE)
472b15cb3dSCy Schubert # else
482b15cb3dSCy Schubert # define GETPAGESIZE() getpagesize()
492b15cb3dSCy Schubert # endif
502b15cb3dSCy Schubert # endif
512b15cb3dSCy Schubert #endif
522b15cb3dSCy Schubert
53ea906c41SOllivier Robert /*
54ea906c41SOllivier Robert * Some weird systems require that a specifically invalid FD number
55ea906c41SOllivier Robert * get passed in as an argument value. Which value is that? Well,
56ea906c41SOllivier Robert * as everybody knows, if open(2) fails, it returns -1, so that must
57ea906c41SOllivier Robert * be the value. :)
58ea906c41SOllivier Robert */
59ea906c41SOllivier Robert #define AO_INVALID_FD -1
60ea906c41SOllivier Robert
61ea906c41SOllivier Robert #define FILE_WRITABLE(_prt,_flg) \
62ea906c41SOllivier Robert ( (_prt & PROT_WRITE) \
63ea906c41SOllivier Robert && ((_flg & (MAP_SHARED|MAP_PRIVATE)) == MAP_SHARED))
64276da39aSCy Schubert #define MAP_FAILED_PTR (VOIDP(MAP_FAILED))
65ea906c41SOllivier Robert
662b15cb3dSCy Schubert /**
672b15cb3dSCy Schubert * Load the contents of a text file. There are two separate implementations,
682b15cb3dSCy Schubert * depending up on whether mmap(3) is available.
692b15cb3dSCy Schubert *
702b15cb3dSCy Schubert * If not available, malloc the file length plus one byte. Read it in
712b15cb3dSCy Schubert * and NUL terminate.
722b15cb3dSCy Schubert *
732b15cb3dSCy Schubert * If available, first check to see if the text file size is a multiple of a
742b15cb3dSCy Schubert * page size. If it is, map the file size plus an extra page from either
752b15cb3dSCy Schubert * anonymous memory or from /dev/zero. Then map the file text on top of the
762b15cb3dSCy Schubert * first pages of the anonymous/zero pages. Otherwise, just map the file
772b15cb3dSCy Schubert * because there will be NUL bytes provided at the end.
782b15cb3dSCy Schubert *
792b15cb3dSCy Schubert * @param mapinfo a structure holding everything we need to know
802b15cb3dSCy Schubert * about the mapping.
812b15cb3dSCy Schubert *
822b15cb3dSCy Schubert * @param pzFile name of the file, for error reporting.
832b15cb3dSCy Schubert */
842b15cb3dSCy Schubert static void
load_text_file(tmap_info_t * mapinfo,char const * pzFile)852b15cb3dSCy Schubert load_text_file(tmap_info_t * mapinfo, char const * pzFile)
862b15cb3dSCy Schubert {
872b15cb3dSCy Schubert #if ! defined(HAVE_MMAP)
882b15cb3dSCy Schubert mapinfo->txt_data = AGALOC(mapinfo->txt_size+1, "file text");
892b15cb3dSCy Schubert if (mapinfo->txt_data == NULL) {
902b15cb3dSCy Schubert mapinfo->txt_errno = ENOMEM;
912b15cb3dSCy Schubert return;
922b15cb3dSCy Schubert }
932b15cb3dSCy Schubert
942b15cb3dSCy Schubert {
952b15cb3dSCy Schubert size_t sz = mapinfo->txt_size;
962b15cb3dSCy Schubert char * pz = mapinfo->txt_data;
972b15cb3dSCy Schubert
982b15cb3dSCy Schubert while (sz > 0) {
992b15cb3dSCy Schubert ssize_t rdct = read(mapinfo->txt_fd, pz, sz);
1002b15cb3dSCy Schubert if (rdct <= 0) {
1012b15cb3dSCy Schubert mapinfo->txt_errno = errno;
1022b15cb3dSCy Schubert fserr_warn("libopts", "read", pzFile);
1032b15cb3dSCy Schubert free(mapinfo->txt_data);
1042b15cb3dSCy Schubert return;
1052b15cb3dSCy Schubert }
1062b15cb3dSCy Schubert
1072b15cb3dSCy Schubert pz += rdct;
1082b15cb3dSCy Schubert sz -= rdct;
1092b15cb3dSCy Schubert }
1102b15cb3dSCy Schubert
1112b15cb3dSCy Schubert *pz = NUL;
1122b15cb3dSCy Schubert }
1132b15cb3dSCy Schubert
1142b15cb3dSCy Schubert mapinfo->txt_errno = 0;
1152b15cb3dSCy Schubert
1162b15cb3dSCy Schubert #else /* HAVE mmap */
1172b15cb3dSCy Schubert size_t const pgsz = (size_t)GETPAGESIZE();
1182b15cb3dSCy Schubert void * map_addr = NULL;
1192b15cb3dSCy Schubert
1202b15cb3dSCy Schubert (void)pzFile;
1212b15cb3dSCy Schubert
1222b15cb3dSCy Schubert mapinfo->txt_full_size = (mapinfo->txt_size + pgsz) & ~(pgsz - 1);
1232b15cb3dSCy Schubert if (mapinfo->txt_full_size == (mapinfo->txt_size + pgsz)) {
1242b15cb3dSCy Schubert /*
1252b15cb3dSCy Schubert * The text is a multiple of a page boundary. We must map an
1262b15cb3dSCy Schubert * extra page so the text ends with a NUL.
1272b15cb3dSCy Schubert */
1282b15cb3dSCy Schubert #if defined(MAP_ANONYMOUS)
1292b15cb3dSCy Schubert map_addr = mmap(NULL, mapinfo->txt_full_size, PROT_READ|PROT_WRITE,
1302b15cb3dSCy Schubert MAP_ANONYMOUS|MAP_PRIVATE, AO_INVALID_FD, 0);
1312b15cb3dSCy Schubert #else
1322b15cb3dSCy Schubert mapinfo->txt_zero_fd = open("/dev/zero", O_RDONLY);
1332b15cb3dSCy Schubert
1342b15cb3dSCy Schubert if (mapinfo->txt_zero_fd == AO_INVALID_FD) {
1352b15cb3dSCy Schubert mapinfo->txt_errno = errno;
1362b15cb3dSCy Schubert return;
1372b15cb3dSCy Schubert }
1382b15cb3dSCy Schubert map_addr = mmap(NULL, mapinfo->txt_full_size, PROT_READ|PROT_WRITE,
1392b15cb3dSCy Schubert MAP_PRIVATE, mapinfo->txt_zero_fd, 0);
1402b15cb3dSCy Schubert #endif
1412b15cb3dSCy Schubert if (map_addr == MAP_FAILED_PTR) {
1422b15cb3dSCy Schubert mapinfo->txt_errno = errno;
1432b15cb3dSCy Schubert return;
1442b15cb3dSCy Schubert }
1452b15cb3dSCy Schubert mapinfo->txt_flags |= MAP_FIXED;
1462b15cb3dSCy Schubert }
1472b15cb3dSCy Schubert
1482b15cb3dSCy Schubert mapinfo->txt_data =
1492b15cb3dSCy Schubert mmap(map_addr, mapinfo->txt_size, mapinfo->txt_prot,
1502b15cb3dSCy Schubert mapinfo->txt_flags, mapinfo->txt_fd, 0);
1512b15cb3dSCy Schubert
1522b15cb3dSCy Schubert if (mapinfo->txt_data == MAP_FAILED_PTR)
1532b15cb3dSCy Schubert mapinfo->txt_errno = errno;
1542b15cb3dSCy Schubert #endif /* HAVE_MMAP */
1552b15cb3dSCy Schubert }
1562b15cb3dSCy Schubert
1572b15cb3dSCy Schubert /**
1582b15cb3dSCy Schubert * Make sure all the parameters are correct: we have a file name that
1592b15cb3dSCy Schubert * is a text file that we can read.
1602b15cb3dSCy Schubert *
1612b15cb3dSCy Schubert * @param fname the text file to map
1622b15cb3dSCy Schubert * @param prot the memory protections requested (read/write/etc.)
1632b15cb3dSCy Schubert * @param flags mmap flags
1642b15cb3dSCy Schubert * @param mapinfo a structure holding everything we need to know
1652b15cb3dSCy Schubert * about the mapping.
1662b15cb3dSCy Schubert */
1672b15cb3dSCy Schubert static void
validate_mmap(char const * fname,int prot,int flags,tmap_info_t * mapinfo)1682b15cb3dSCy Schubert validate_mmap(char const * fname, int prot, int flags, tmap_info_t * mapinfo)
1692b15cb3dSCy Schubert {
1702b15cb3dSCy Schubert memset(mapinfo, 0, sizeof(*mapinfo));
1712b15cb3dSCy Schubert #if defined(HAVE_MMAP) && ! defined(MAP_ANONYMOUS)
1722b15cb3dSCy Schubert mapinfo->txt_zero_fd = AO_INVALID_FD;
1732b15cb3dSCy Schubert #endif
1742b15cb3dSCy Schubert mapinfo->txt_fd = AO_INVALID_FD;
1752b15cb3dSCy Schubert mapinfo->txt_prot = prot;
1762b15cb3dSCy Schubert mapinfo->txt_flags = flags;
1772b15cb3dSCy Schubert
1782b15cb3dSCy Schubert /*
1792b15cb3dSCy Schubert * Map mmap flags and protections into open flags and do the open.
1802b15cb3dSCy Schubert */
1812b15cb3dSCy Schubert {
1822b15cb3dSCy Schubert /*
1832b15cb3dSCy Schubert * See if we will be updating the file. If we can alter the memory
1842b15cb3dSCy Schubert * and if we share the data and we are *not* copy-on-writing the data,
1852b15cb3dSCy Schubert * then our updates will show in the file, so we must open with
1862b15cb3dSCy Schubert * write access.
1872b15cb3dSCy Schubert */
188*a466cc55SCy Schubert int o_flag =
189*a466cc55SCy Schubert #ifdef _WIN32
190*a466cc55SCy Schubert _O_BINARY |
191*a466cc55SCy Schubert #endif
192*a466cc55SCy Schubert FILE_WRITABLE(prot, flags) ? O_RDWR : O_RDONLY;
1932b15cb3dSCy Schubert
1942b15cb3dSCy Schubert /*
1952b15cb3dSCy Schubert * If you're not sharing the file and you are writing to it,
1962b15cb3dSCy Schubert * then don't let anyone else have access to the file.
1972b15cb3dSCy Schubert */
1982b15cb3dSCy Schubert if (((flags & MAP_SHARED) == 0) && (prot & PROT_WRITE))
1992b15cb3dSCy Schubert o_flag |= O_EXCL;
2002b15cb3dSCy Schubert
2012b15cb3dSCy Schubert mapinfo->txt_fd = open(fname, o_flag);
2022b15cb3dSCy Schubert if (mapinfo->txt_fd < 0) {
2032b15cb3dSCy Schubert mapinfo->txt_errno = errno;
2042b15cb3dSCy Schubert mapinfo->txt_fd = AO_INVALID_FD;
2052b15cb3dSCy Schubert return;
2062b15cb3dSCy Schubert }
2072b15cb3dSCy Schubert }
2082b15cb3dSCy Schubert
2092b15cb3dSCy Schubert /*
2102b15cb3dSCy Schubert * Make sure we can stat the regular file. Save the file size.
2112b15cb3dSCy Schubert */
2122b15cb3dSCy Schubert {
2132b15cb3dSCy Schubert struct stat sb;
2142b15cb3dSCy Schubert if (fstat(mapinfo->txt_fd, &sb) != 0) {
2152b15cb3dSCy Schubert mapinfo->txt_errno = errno;
2162b15cb3dSCy Schubert close(mapinfo->txt_fd);
2172b15cb3dSCy Schubert return;
2182b15cb3dSCy Schubert }
2192b15cb3dSCy Schubert
2202b15cb3dSCy Schubert if (! S_ISREG(sb.st_mode)) {
2212b15cb3dSCy Schubert mapinfo->txt_errno = errno = EINVAL;
2222b15cb3dSCy Schubert close(mapinfo->txt_fd);
2232b15cb3dSCy Schubert return;
2242b15cb3dSCy Schubert }
2252b15cb3dSCy Schubert
2262b15cb3dSCy Schubert mapinfo->txt_size = (size_t)sb.st_size;
2272b15cb3dSCy Schubert }
2282b15cb3dSCy Schubert
2292b15cb3dSCy Schubert if (mapinfo->txt_fd == AO_INVALID_FD)
2302b15cb3dSCy Schubert mapinfo->txt_errno = errno;
2312b15cb3dSCy Schubert }
2322b15cb3dSCy Schubert
2332b15cb3dSCy Schubert /**
2342b15cb3dSCy Schubert * Close any files opened by the mapping.
2352b15cb3dSCy Schubert *
2362b15cb3dSCy Schubert * @param mi a structure holding everything we need to know about the map.
2372b15cb3dSCy Schubert */
2382b15cb3dSCy Schubert static void
close_mmap_files(tmap_info_t * mi)2392b15cb3dSCy Schubert close_mmap_files(tmap_info_t * mi)
2402b15cb3dSCy Schubert {
2412b15cb3dSCy Schubert if (mi->txt_fd == AO_INVALID_FD)
2422b15cb3dSCy Schubert return;
2432b15cb3dSCy Schubert
2442b15cb3dSCy Schubert close(mi->txt_fd);
2452b15cb3dSCy Schubert mi->txt_fd = AO_INVALID_FD;
2462b15cb3dSCy Schubert
2472b15cb3dSCy Schubert #if defined(HAVE_MMAP) && ! defined(MAP_ANONYMOUS)
2482b15cb3dSCy Schubert if (mi->txt_zero_fd == AO_INVALID_FD)
2492b15cb3dSCy Schubert return;
2502b15cb3dSCy Schubert
2512b15cb3dSCy Schubert close(mi->txt_zero_fd);
2522b15cb3dSCy Schubert mi->txt_zero_fd = AO_INVALID_FD;
2532b15cb3dSCy Schubert #endif
2542b15cb3dSCy Schubert }
2552b15cb3dSCy Schubert
256ea906c41SOllivier Robert /*=export_func text_mmap
257ea906c41SOllivier Robert * private:
258ea906c41SOllivier Robert *
259ea906c41SOllivier Robert * what: map a text file with terminating NUL
260ea906c41SOllivier Robert *
261ea906c41SOllivier Robert * arg: char const *, pzFile, name of the file to map
262ea906c41SOllivier Robert * arg: int, prot, mmap protections (see mmap(2))
263ea906c41SOllivier Robert * arg: int, flags, mmap flags (see mmap(2))
264ea906c41SOllivier Robert * arg: tmap_info_t *, mapinfo, returned info about the mapping
265ea906c41SOllivier Robert *
266ea906c41SOllivier Robert * ret-type: void *
267ea906c41SOllivier Robert * ret-desc: The mmaped data address
268ea906c41SOllivier Robert *
269ea906c41SOllivier Robert * doc:
270ea906c41SOllivier Robert *
271ea906c41SOllivier Robert * This routine will mmap a file into memory ensuring that there is at least
272ea906c41SOllivier Robert * one @file{NUL} character following the file data. It will return the
273ea906c41SOllivier Robert * address where the file contents have been mapped into memory. If there is a
2742b15cb3dSCy Schubert * problem, then it will return @code{MAP_FAILED} and set @code{errno}
275ea906c41SOllivier Robert * appropriately.
276ea906c41SOllivier Robert *
2772b15cb3dSCy Schubert * The named file does not exist, @code{stat(2)} will set @code{errno} as it
2782b15cb3dSCy Schubert * will. If the file is not a regular file, @code{errno} will be
279ea906c41SOllivier Robert * @code{EINVAL}. At that point, @code{open(2)} is attempted with the access
280ea906c41SOllivier Robert * bits set appropriately for the requested @code{mmap(2)} protections and flag
2812b15cb3dSCy Schubert * bits. On failure, @code{errno} will be set according to the documentation
2822b15cb3dSCy Schubert * for @code{open(2)}. If @code{mmap(2)} fails, @code{errno} will be set as
283ea906c41SOllivier Robert * that routine sets it. If @code{text_mmap} works to this point, a valid
284ea906c41SOllivier Robert * address will be returned, but there may still be ``issues''.
285ea906c41SOllivier Robert *
286ea906c41SOllivier Robert * If the file size is not an even multiple of the system page size, then
2872b15cb3dSCy Schubert * @code{text_map} will return at this point and @code{errno} will be zero.
288ea906c41SOllivier Robert * Otherwise, an anonymous map is attempted. If not available, then an attempt
289ea906c41SOllivier Robert * is made to @code{mmap(2)} @file{/dev/zero}. If any of these fail, the
290ea906c41SOllivier Robert * address of the file's data is returned, bug @code{no} @file{NUL} characters
291ea906c41SOllivier Robert * are mapped after the end of the data.
292ea906c41SOllivier Robert *
293ea906c41SOllivier Robert * see: mmap(2), open(2), stat(2)
294ea906c41SOllivier Robert *
295ea906c41SOllivier Robert * err: Any error code issued by mmap(2), open(2), stat(2) is possible.
296ea906c41SOllivier Robert * Additionally, if the specified file is not a regular file, then
297ea906c41SOllivier Robert * errno will be set to @code{EINVAL}.
298ea906c41SOllivier Robert *
299ea906c41SOllivier Robert * example:
300ea906c41SOllivier Robert * #include <mylib.h>
301ea906c41SOllivier Robert * tmap_info_t mi;
302ea906c41SOllivier Robert * int no_nul;
303ea906c41SOllivier Robert * void * data = text_mmap("file", PROT_WRITE, MAP_PRIVATE, &mi);
304ea906c41SOllivier Robert * if (data == MAP_FAILED) return;
305ea906c41SOllivier Robert * no_nul = (mi.txt_size == mi.txt_full_size);
306ea906c41SOllivier Robert * << use the data >>
307ea906c41SOllivier Robert * text_munmap(&mi);
308ea906c41SOllivier Robert =*/
309ea906c41SOllivier Robert void *
text_mmap(char const * pzFile,int prot,int flags,tmap_info_t * mi)3102b15cb3dSCy Schubert text_mmap(char const * pzFile, int prot, int flags, tmap_info_t * mi)
311ea906c41SOllivier Robert {
3122b15cb3dSCy Schubert validate_mmap(pzFile, prot, flags, mi);
3132b15cb3dSCy Schubert if (mi->txt_errno != 0)
314ea906c41SOllivier Robert return MAP_FAILED_PTR;
315ea906c41SOllivier Robert
3162b15cb3dSCy Schubert load_text_file(mi, pzFile);
317ea906c41SOllivier Robert
3182b15cb3dSCy Schubert if (mi->txt_errno == 0)
3192b15cb3dSCy Schubert return mi->txt_data;
320ea906c41SOllivier Robert
3212b15cb3dSCy Schubert close_mmap_files(mi);
322ea906c41SOllivier Robert
3232b15cb3dSCy Schubert errno = mi->txt_errno;
3242b15cb3dSCy Schubert mi->txt_data = MAP_FAILED_PTR;
3252b15cb3dSCy Schubert return mi->txt_data;
326ea906c41SOllivier Robert }
327ea906c41SOllivier Robert
328ea906c41SOllivier Robert
329ea906c41SOllivier Robert /*=export_func text_munmap
330ea906c41SOllivier Robert * private:
331ea906c41SOllivier Robert *
332ea906c41SOllivier Robert * what: unmap the data mapped in by text_mmap
333ea906c41SOllivier Robert *
334ea906c41SOllivier Robert * arg: tmap_info_t *, mapinfo, info about the mapping
335ea906c41SOllivier Robert *
336ea906c41SOllivier Robert * ret-type: int
3372b15cb3dSCy Schubert * ret-desc: -1 or 0. @code{errno} will have the error code.
338ea906c41SOllivier Robert *
339ea906c41SOllivier Robert * doc:
340ea906c41SOllivier Robert *
341ea906c41SOllivier Robert * This routine will unmap the data mapped in with @code{text_mmap} and close
342ea906c41SOllivier Robert * the associated file descriptors opened by that function.
343ea906c41SOllivier Robert *
344ea906c41SOllivier Robert * see: munmap(2), close(2)
345ea906c41SOllivier Robert *
346ea906c41SOllivier Robert * err: Any error code issued by munmap(2) or close(2) is possible.
347ea906c41SOllivier Robert =*/
348ea906c41SOllivier Robert int
text_munmap(tmap_info_t * mi)3492b15cb3dSCy Schubert text_munmap(tmap_info_t * mi)
350ea906c41SOllivier Robert {
3512b15cb3dSCy Schubert errno = 0;
3522b15cb3dSCy Schubert
353ea906c41SOllivier Robert #ifdef HAVE_MMAP
3542b15cb3dSCy Schubert (void)munmap(mi->txt_data, mi->txt_full_size);
355ea906c41SOllivier Robert
356*a466cc55SCy Schubert #else // don't HAVE_MMAP
357ea906c41SOllivier Robert /*
358ea906c41SOllivier Robert * IF the memory is writable *AND* it is not private (copy-on-write)
359ea906c41SOllivier Robert * *AND* the memory is "sharable" (seen by other processes)
3602b15cb3dSCy Schubert * THEN rewrite the data. Emulate mmap visibility.
361ea906c41SOllivier Robert */
3622b15cb3dSCy Schubert if ( FILE_WRITABLE(mi->txt_prot, mi->txt_flags)
363*a466cc55SCy Schubert && (lseek(mi->txt_fd, 0, SEEK_SET) >= 0) )
3642b15cb3dSCy Schubert write(mi->txt_fd, mi->txt_data, mi->txt_size);
365ea906c41SOllivier Robert
3662b15cb3dSCy Schubert free(mi->txt_data);
367ea906c41SOllivier Robert #endif /* HAVE_MMAP */
3682b15cb3dSCy Schubert
3692b15cb3dSCy Schubert mi->txt_errno = errno;
3702b15cb3dSCy Schubert close_mmap_files(mi);
3712b15cb3dSCy Schubert
3722b15cb3dSCy Schubert return mi->txt_errno;
373ea906c41SOllivier Robert }
374ea906c41SOllivier Robert
3752b15cb3dSCy Schubert /** @}
3762b15cb3dSCy Schubert *
377ea906c41SOllivier Robert * Local Variables:
378ea906c41SOllivier Robert * mode: C
379ea906c41SOllivier Robert * c-file-style: "stroustrup"
380ea906c41SOllivier Robert * indent-tabs-mode: nil
381ea906c41SOllivier Robert * End:
382ea906c41SOllivier Robert * end of autoopts/text_mmap.c */
383