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*276da39aSCy Schubert * AutoOpts is Copyright (C) 1992-2015 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)) 64*276da39aSCy 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 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 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 */ 1882b15cb3dSCy Schubert int o_flag = FILE_WRITABLE(prot, flags) ? O_RDWR : O_RDONLY; 1892b15cb3dSCy Schubert 1902b15cb3dSCy Schubert /* 1912b15cb3dSCy Schubert * If you're not sharing the file and you are writing to it, 1922b15cb3dSCy Schubert * then don't let anyone else have access to the file. 1932b15cb3dSCy Schubert */ 1942b15cb3dSCy Schubert if (((flags & MAP_SHARED) == 0) && (prot & PROT_WRITE)) 1952b15cb3dSCy Schubert o_flag |= O_EXCL; 1962b15cb3dSCy Schubert 1972b15cb3dSCy Schubert mapinfo->txt_fd = open(fname, o_flag); 1982b15cb3dSCy Schubert if (mapinfo->txt_fd < 0) { 1992b15cb3dSCy Schubert mapinfo->txt_errno = errno; 2002b15cb3dSCy Schubert mapinfo->txt_fd = AO_INVALID_FD; 2012b15cb3dSCy Schubert return; 2022b15cb3dSCy Schubert } 2032b15cb3dSCy Schubert } 2042b15cb3dSCy Schubert 2052b15cb3dSCy Schubert /* 2062b15cb3dSCy Schubert * Make sure we can stat the regular file. Save the file size. 2072b15cb3dSCy Schubert */ 2082b15cb3dSCy Schubert { 2092b15cb3dSCy Schubert struct stat sb; 2102b15cb3dSCy Schubert if (fstat(mapinfo->txt_fd, &sb) != 0) { 2112b15cb3dSCy Schubert mapinfo->txt_errno = errno; 2122b15cb3dSCy Schubert close(mapinfo->txt_fd); 2132b15cb3dSCy Schubert return; 2142b15cb3dSCy Schubert } 2152b15cb3dSCy Schubert 2162b15cb3dSCy Schubert if (! S_ISREG(sb.st_mode)) { 2172b15cb3dSCy Schubert mapinfo->txt_errno = errno = EINVAL; 2182b15cb3dSCy Schubert close(mapinfo->txt_fd); 2192b15cb3dSCy Schubert return; 2202b15cb3dSCy Schubert } 2212b15cb3dSCy Schubert 2222b15cb3dSCy Schubert mapinfo->txt_size = (size_t)sb.st_size; 2232b15cb3dSCy Schubert } 2242b15cb3dSCy Schubert 2252b15cb3dSCy Schubert if (mapinfo->txt_fd == AO_INVALID_FD) 2262b15cb3dSCy Schubert mapinfo->txt_errno = errno; 2272b15cb3dSCy Schubert } 2282b15cb3dSCy Schubert 2292b15cb3dSCy Schubert /** 2302b15cb3dSCy Schubert * Close any files opened by the mapping. 2312b15cb3dSCy Schubert * 2322b15cb3dSCy Schubert * @param mi a structure holding everything we need to know about the map. 2332b15cb3dSCy Schubert */ 2342b15cb3dSCy Schubert static void 2352b15cb3dSCy Schubert close_mmap_files(tmap_info_t * mi) 2362b15cb3dSCy Schubert { 2372b15cb3dSCy Schubert if (mi->txt_fd == AO_INVALID_FD) 2382b15cb3dSCy Schubert return; 2392b15cb3dSCy Schubert 2402b15cb3dSCy Schubert close(mi->txt_fd); 2412b15cb3dSCy Schubert mi->txt_fd = AO_INVALID_FD; 2422b15cb3dSCy Schubert 2432b15cb3dSCy Schubert #if defined(HAVE_MMAP) && ! defined(MAP_ANONYMOUS) 2442b15cb3dSCy Schubert if (mi->txt_zero_fd == AO_INVALID_FD) 2452b15cb3dSCy Schubert return; 2462b15cb3dSCy Schubert 2472b15cb3dSCy Schubert close(mi->txt_zero_fd); 2482b15cb3dSCy Schubert mi->txt_zero_fd = AO_INVALID_FD; 2492b15cb3dSCy Schubert #endif 2502b15cb3dSCy Schubert } 2512b15cb3dSCy Schubert 252ea906c41SOllivier Robert /*=export_func text_mmap 253ea906c41SOllivier Robert * private: 254ea906c41SOllivier Robert * 255ea906c41SOllivier Robert * what: map a text file with terminating NUL 256ea906c41SOllivier Robert * 257ea906c41SOllivier Robert * arg: char const *, pzFile, name of the file to map 258ea906c41SOllivier Robert * arg: int, prot, mmap protections (see mmap(2)) 259ea906c41SOllivier Robert * arg: int, flags, mmap flags (see mmap(2)) 260ea906c41SOllivier Robert * arg: tmap_info_t *, mapinfo, returned info about the mapping 261ea906c41SOllivier Robert * 262ea906c41SOllivier Robert * ret-type: void * 263ea906c41SOllivier Robert * ret-desc: The mmaped data address 264ea906c41SOllivier Robert * 265ea906c41SOllivier Robert * doc: 266ea906c41SOllivier Robert * 267ea906c41SOllivier Robert * This routine will mmap a file into memory ensuring that there is at least 268ea906c41SOllivier Robert * one @file{NUL} character following the file data. It will return the 269ea906c41SOllivier Robert * address where the file contents have been mapped into memory. If there is a 2702b15cb3dSCy Schubert * problem, then it will return @code{MAP_FAILED} and set @code{errno} 271ea906c41SOllivier Robert * appropriately. 272ea906c41SOllivier Robert * 2732b15cb3dSCy Schubert * The named file does not exist, @code{stat(2)} will set @code{errno} as it 2742b15cb3dSCy Schubert * will. If the file is not a regular file, @code{errno} will be 275ea906c41SOllivier Robert * @code{EINVAL}. At that point, @code{open(2)} is attempted with the access 276ea906c41SOllivier Robert * bits set appropriately for the requested @code{mmap(2)} protections and flag 2772b15cb3dSCy Schubert * bits. On failure, @code{errno} will be set according to the documentation 2782b15cb3dSCy Schubert * for @code{open(2)}. If @code{mmap(2)} fails, @code{errno} will be set as 279ea906c41SOllivier Robert * that routine sets it. If @code{text_mmap} works to this point, a valid 280ea906c41SOllivier Robert * address will be returned, but there may still be ``issues''. 281ea906c41SOllivier Robert * 282ea906c41SOllivier Robert * If the file size is not an even multiple of the system page size, then 2832b15cb3dSCy Schubert * @code{text_map} will return at this point and @code{errno} will be zero. 284ea906c41SOllivier Robert * Otherwise, an anonymous map is attempted. If not available, then an attempt 285ea906c41SOllivier Robert * is made to @code{mmap(2)} @file{/dev/zero}. If any of these fail, the 286ea906c41SOllivier Robert * address of the file's data is returned, bug @code{no} @file{NUL} characters 287ea906c41SOllivier Robert * are mapped after the end of the data. 288ea906c41SOllivier Robert * 289ea906c41SOllivier Robert * see: mmap(2), open(2), stat(2) 290ea906c41SOllivier Robert * 291ea906c41SOllivier Robert * err: Any error code issued by mmap(2), open(2), stat(2) is possible. 292ea906c41SOllivier Robert * Additionally, if the specified file is not a regular file, then 293ea906c41SOllivier Robert * errno will be set to @code{EINVAL}. 294ea906c41SOllivier Robert * 295ea906c41SOllivier Robert * example: 296ea906c41SOllivier Robert * #include <mylib.h> 297ea906c41SOllivier Robert * tmap_info_t mi; 298ea906c41SOllivier Robert * int no_nul; 299ea906c41SOllivier Robert * void * data = text_mmap("file", PROT_WRITE, MAP_PRIVATE, &mi); 300ea906c41SOllivier Robert * if (data == MAP_FAILED) return; 301ea906c41SOllivier Robert * no_nul = (mi.txt_size == mi.txt_full_size); 302ea906c41SOllivier Robert * << use the data >> 303ea906c41SOllivier Robert * text_munmap(&mi); 304ea906c41SOllivier Robert =*/ 305ea906c41SOllivier Robert void * 3062b15cb3dSCy Schubert text_mmap(char const * pzFile, int prot, int flags, tmap_info_t * mi) 307ea906c41SOllivier Robert { 3082b15cb3dSCy Schubert validate_mmap(pzFile, prot, flags, mi); 3092b15cb3dSCy Schubert if (mi->txt_errno != 0) 310ea906c41SOllivier Robert return MAP_FAILED_PTR; 311ea906c41SOllivier Robert 3122b15cb3dSCy Schubert load_text_file(mi, pzFile); 313ea906c41SOllivier Robert 3142b15cb3dSCy Schubert if (mi->txt_errno == 0) 3152b15cb3dSCy Schubert return mi->txt_data; 316ea906c41SOllivier Robert 3172b15cb3dSCy Schubert close_mmap_files(mi); 318ea906c41SOllivier Robert 3192b15cb3dSCy Schubert errno = mi->txt_errno; 3202b15cb3dSCy Schubert mi->txt_data = MAP_FAILED_PTR; 3212b15cb3dSCy Schubert return mi->txt_data; 322ea906c41SOllivier Robert } 323ea906c41SOllivier Robert 324ea906c41SOllivier Robert 325ea906c41SOllivier Robert /*=export_func text_munmap 326ea906c41SOllivier Robert * private: 327ea906c41SOllivier Robert * 328ea906c41SOllivier Robert * what: unmap the data mapped in by text_mmap 329ea906c41SOllivier Robert * 330ea906c41SOllivier Robert * arg: tmap_info_t *, mapinfo, info about the mapping 331ea906c41SOllivier Robert * 332ea906c41SOllivier Robert * ret-type: int 3332b15cb3dSCy Schubert * ret-desc: -1 or 0. @code{errno} will have the error code. 334ea906c41SOllivier Robert * 335ea906c41SOllivier Robert * doc: 336ea906c41SOllivier Robert * 337ea906c41SOllivier Robert * This routine will unmap the data mapped in with @code{text_mmap} and close 338ea906c41SOllivier Robert * the associated file descriptors opened by that function. 339ea906c41SOllivier Robert * 340ea906c41SOllivier Robert * see: munmap(2), close(2) 341ea906c41SOllivier Robert * 342ea906c41SOllivier Robert * err: Any error code issued by munmap(2) or close(2) is possible. 343ea906c41SOllivier Robert =*/ 344ea906c41SOllivier Robert int 3452b15cb3dSCy Schubert text_munmap(tmap_info_t * mi) 346ea906c41SOllivier Robert { 3472b15cb3dSCy Schubert errno = 0; 3482b15cb3dSCy Schubert 349ea906c41SOllivier Robert #ifdef HAVE_MMAP 3502b15cb3dSCy Schubert (void)munmap(mi->txt_data, mi->txt_full_size); 351ea906c41SOllivier Robert 3522b15cb3dSCy Schubert #else /* don't HAVE_MMAP */ 353ea906c41SOllivier Robert /* 354ea906c41SOllivier Robert * IF the memory is writable *AND* it is not private (copy-on-write) 355ea906c41SOllivier Robert * *AND* the memory is "sharable" (seen by other processes) 3562b15cb3dSCy Schubert * THEN rewrite the data. Emulate mmap visibility. 357ea906c41SOllivier Robert */ 3582b15cb3dSCy Schubert if ( FILE_WRITABLE(mi->txt_prot, mi->txt_flags) 3592b15cb3dSCy Schubert && (lseek(mi->txt_fd, 0, SEEK_SET) >= 0) ) { 3602b15cb3dSCy Schubert write(mi->txt_fd, mi->txt_data, mi->txt_size); 361ea906c41SOllivier Robert } 362ea906c41SOllivier Robert 3632b15cb3dSCy Schubert free(mi->txt_data); 364ea906c41SOllivier Robert #endif /* HAVE_MMAP */ 3652b15cb3dSCy Schubert 3662b15cb3dSCy Schubert mi->txt_errno = errno; 3672b15cb3dSCy Schubert close_mmap_files(mi); 3682b15cb3dSCy Schubert 3692b15cb3dSCy Schubert return mi->txt_errno; 370ea906c41SOllivier Robert } 371ea906c41SOllivier Robert 3722b15cb3dSCy Schubert /** @} 3732b15cb3dSCy Schubert * 374ea906c41SOllivier Robert * Local Variables: 375ea906c41SOllivier Robert * mode: C 376ea906c41SOllivier Robert * c-file-style: "stroustrup" 377ea906c41SOllivier Robert * indent-tabs-mode: nil 378ea906c41SOllivier Robert * End: 379ea906c41SOllivier Robert * end of autoopts/text_mmap.c */ 380