]> git.xonotic.org Git - xonotic/gmqcc.git/blob - util.c
Merge branch 'master' of github.com:graphitemaster/gmqcc
[xonotic/gmqcc.git] / util.c
1 /*
2  * Copyright (C) 2012
3  *     Dale Weiler
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is furnished to do
10  * so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 #include <stdarg.h>
24 #include <errno.h>
25 #include "gmqcc.h"
26
27 uint64_t mem_ab = 0;
28 uint64_t mem_db = 0;
29 uint64_t mem_at = 0;
30 uint64_t mem_dt = 0;
31
32 struct memblock_t {
33     const char  *file;
34     unsigned int line;
35     unsigned int byte;
36     struct memblock_t *next;
37     struct memblock_t *prev;
38 };
39
40 static struct memblock_t *mem_start = NULL;
41
42 void *util_memory_a(unsigned int byte, unsigned int line, const char *file) {
43     struct memblock_t *info = malloc(sizeof(struct memblock_t) + byte);
44     void              *data = (void*)(info+1);
45     if (!info) return NULL;
46     info->line = line;
47     info->byte = byte;
48     info->file = file;
49     info->prev = NULL;
50     info->next = mem_start;
51     if (mem_start)
52         mem_start->prev = info;
53     mem_start = info;
54
55     util_debug("MEM", "allocation:   % 8u (bytes) address 0x%08X @ %s:%u\n", byte, data, file, line);
56     mem_at++;
57     mem_ab += info->byte;
58
59     return data;
60 }
61
62 void util_memory_d(void *ptrn, unsigned int line, const char *file) {
63     struct memblock_t *info = NULL;
64
65     if (!ptrn) return;
66     info = ((struct memblock_t*)ptrn - 1);
67
68     util_debug("MEM", "released:     % 8u (bytes) address 0x%08X @ %s:%u\n", info->byte, ptrn, file, line);
69     mem_db += info->byte;
70     mem_dt++;
71
72     if (info->prev)
73         info->prev->next = info->next;
74     if (info->next)
75         info->next->prev = info->prev;
76     if (info == mem_start)
77         mem_start = info->next;
78
79     free(info);
80 }
81
82 void *util_memory_r(void *ptrn, unsigned int byte, unsigned int line, const char *file) {
83     struct memblock_t *oldinfo = NULL;
84
85     struct memblock_t *newinfo;
86
87     if (!ptrn)
88         return util_memory_a(byte, line, file);
89     if (!byte) {
90         util_memory_d(ptrn, line, file);
91         return NULL;
92     }
93
94     oldinfo = ((struct memblock_t*)ptrn - 1);
95     newinfo = malloc(sizeof(struct memblock_t) + byte);
96
97     util_debug("MEM", "reallocation: % 8u -> %u (bytes) address 0x%08X -> 0x%08X @ %s:%u\n", oldinfo->byte, byte, ptrn, (void*)(newinfo+1), file, line);
98
99     /* new data */
100     if (!newinfo) {
101         util_memory_d(oldinfo+1, line, file);
102         return NULL;
103     }
104     newinfo->line = line;
105     newinfo->byte = byte;
106     newinfo->file = file;
107     newinfo->next = oldinfo->next;
108     newinfo->prev = oldinfo->prev;
109     if (mem_start == oldinfo)
110         mem_start = newinfo;
111
112     /* copy old */
113     memcpy(newinfo+1, oldinfo+1, oldinfo->byte);
114
115     /* drop old */
116     mem_db += newinfo->byte;
117     mem_db -= oldinfo->byte;
118     free(oldinfo);
119
120     /* update */
121     return newinfo+1;
122 }
123
124 void util_meminfo() {
125     struct memblock_t *info;
126
127     if (!opts_memchk)
128         return;
129
130     for (info = mem_start; info; info = info->next) {
131         util_debug("MEM", "lost:       % 8u (bytes) at %s:%u\n",
132             info->byte,
133             info->file,
134             info->line);
135     }
136
137     util_debug("MEM", "Memory information:\n\
138         Total allocations:   %llu\n\
139         Total deallocations: %llu\n\
140         Total allocated:     %llu (bytes)\n\
141         Total deallocated:   %llu (bytes)\n\
142         Leaks found:         lost %llu (bytes) in %d allocations\n",
143             mem_at,   mem_dt,
144             mem_ab,   mem_db,
145            (mem_ab -  mem_db),
146            (mem_at -  mem_dt)
147     );
148 }
149
150 /*
151  * Some string utility functions, because strdup uses malloc, and we want
152  * to track all memory (without replacing malloc).
153  */
154 char *util_strdup(const char *s) {
155     size_t  len = 0;
156     char   *ptr = NULL;
157
158     if (!s)
159         return NULL;
160
161     if ((len = strlen(s)) && (ptr = mem_a(len+1))) {
162         memcpy(ptr, s, len);
163         ptr[len] = '\0';
164     }
165     return ptr;
166 }
167
168 /*
169  * Remove quotes from a string, escapes from \ in string
170  * as well.  This function shouldn't be used to create a
171  * char array that is later freed (it uses pointer arith)
172  */
173 char *util_strrq(const char *s) {
174     char *dst = (char*)s;
175     char *src = (char*)s;
176     char  chr;
177     while ((chr = *src++) != '\0') {
178         if (chr == '\\') {
179             *dst++ = chr;
180             if ((chr = *src++) == '\0')
181                 break;
182             *dst++ = chr;
183         } else if (chr != '"')
184             *dst++ = chr;
185     }
186     *dst = '\0';
187     return dst;
188 }
189
190 /*
191  * Chops a substring from an existing string by creating a
192  * copy of it and null terminating it at the required position.
193  */
194 char *util_strchp(const char *s, const char *e) {
195     const char *c = NULL;
196     if (!s || !e)
197         return NULL;
198
199     c = s;
200     while (c != e)
201         c++;
202
203     return util_strdup(s);
204 }
205
206 /*
207  * Returns true if string is all uppercase, otherwise
208  * it returns false.
209  */
210 bool util_strupper(const char *str) {
211     while (*str) {
212         if(!isupper(*str))
213             return false;
214         str++;
215     }
216     return true;
217 }
218
219 /*
220  * Returns true if string is all digits, otherwise
221  * it returns false.
222  */
223 bool util_strdigit(const char *str) {
224     while (*str) {
225         if(!isdigit(*str))
226             return false;
227         str++;
228     }
229     return true;
230 }
231
232 bool util_strncmpexact(const char *src, const char *ned, size_t len) {
233     return (!strncmp(src, ned, len) && !src[len]);
234 }
235
236 void util_debug(const char *area, const char *ms, ...) {
237     va_list  va;
238     if (!opts_debug)
239         return;
240
241     if (!strcmp(area, "MEM") && !opts_memchk)
242         return;
243
244     va_start(va, ms);
245     fprintf (stdout, "DEBUG: ");
246     fputc   ('[',  stdout);
247     fprintf(stdout, "%s", area);
248     fputs   ("] ", stdout);
249     vfprintf(stdout, ms, va);
250     va_end  (va);
251 }
252
253 /*
254  * Endianess swapping, all data must be stored little-endian.  This
255  * reorders by stride and length, much nicer than other functions for
256  * certian-sized types like short or int.
257  */
258 void util_endianswap(void *m, int s, int l) {
259     size_t w = 0;
260     size_t i = 0;
261
262     /* ignore if we're already LE */
263     if(*((char *)&s))
264         return;
265
266     for(; w < l; w++) {
267         for(;  i < s << 1; i++) {
268             unsigned char *p = (unsigned char *)m+w*s;
269             unsigned char  t = p[i];
270             p[i]             = p[s-i-1];
271             p[s-i-1]         = t;
272         }
273     }
274 }
275
276 /*
277  * CRC algorithms vary in the width of the polynomial, the value of said polynomial,
278  * the initial value used for the register, weather the bits of each byte are reflected
279  * before being processed, weather the algorithm itself feeds input bytes through the
280  * register or XORs them with a byte from one end and then straight into the table, as
281  * well as (but not limited to the idea of reflected versions) where the final register
282  * value becomes reversed, and finally weather the value itself is used to XOR the final
283  * register value.  AS such you can already imagine how painfully annoying CRCs are,
284  * of course we stand to target Quake, which expects it's certian set of rules for proper
285  * calculation of a CRC.
286  *
287  * In most traditional CRC algorithms on uses a reflected table driven method where a value
288  * or register is reflected if it's bits are swapped around it's center.  For example:
289  * take the bits 0101 is the 4-bit reflection of 1010, and respectfully 0011 would be the
290  * reflection of 1100. Quakle however expects a NON-Reflected CRC on the output, but still
291  * requires a final XOR on the values (0xFFFF and 0x0000) this is a standard CCITT CRC-16
292  * which I respectfully as a programmer don't agree with.
293  *
294  * So now you know what we target, and why we target it, despite how unsettling it may seem
295  * but those are what Quake seems to request.
296  */
297
298 /*
299  * This is an implementation of CRC32 & CRC16. The polynomials have been
300  * offline computed for faster generation at the cost of larger code size.
301  *
302  * CRC32 Polynomial: 0xEDB88320
303  * CRC16 Polynomial: 0x00001021
304  */
305 static const uint32_t util_crc32_table[] = {
306     0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
307     0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
308     0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
309     0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
310     0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
311     0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
312     0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
313     0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
314     0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
315     0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
316     0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
317     0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
318     0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
319     0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
320     0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
321     0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
322     0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
323     0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
324     0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
325     0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
326     0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
327     0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
328     0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
329     0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
330     0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
331     0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
332     0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
333     0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
334     0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
335     0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
336     0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
337     0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
338     0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
339     0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
340     0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
341     0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
342     0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
343     0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
344     0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
345     0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
346     0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
347     0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
348     0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
349 };
350 static const uint16_t util_crc16_table[] = {
351     0x0000,     0x1021,     0x2042,     0x3063,     0x4084,     0x50A5,
352     0x60C6,     0x70E7,     0x8108,     0x9129,     0xA14A,     0xB16B,
353     0xC18C,     0xD1AD,     0xE1CE,     0xF1EF,     0x1231,     0x0210,
354     0x3273,     0x2252,     0x52B5,     0x4294,     0x72F7,     0x62D6,
355     0x9339,     0x8318,     0xB37B,     0xA35A,     0xD3BD,     0xC39C,
356     0xF3FF,     0xE3DE,     0x2462,     0x3443,     0x0420,     0x1401,
357     0x64E6,     0x74C7,     0x44A4,     0x5485,     0xA56A,     0xB54B,
358     0x8528,     0x9509,     0xE5EE,     0xF5CF,     0xC5AC,     0xD58D,
359     0x3653,     0x2672,     0x1611,     0x0630,     0x76D7,     0x66F6,
360     0x5695,     0x46B4,     0xB75B,     0xA77A,     0x9719,     0x8738,
361     0xF7DF,     0xE7FE,     0xD79D,     0xC7BC,     0x48C4,     0x58E5,
362     0x6886,     0x78A7,     0x0840,     0x1861,     0x2802,     0x3823,
363     0xC9CC,     0xD9ED,     0xE98E,     0xF9AF,     0x8948,     0x9969,
364     0xA90A,     0xB92B,     0x5AF5,     0x4AD4,     0x7AB7,     0x6A96,
365     0x1A71,     0x0A50,     0x3A33,     0x2A12,     0xDBFD,     0xCBDC,
366     0xFBBF,     0xEB9E,     0x9B79,     0x8B58,     0xBB3B,     0xAB1A,
367     0x6CA6,     0x7C87,     0x4CE4,     0x5CC5,     0x2C22,     0x3C03,
368     0x0C60,     0x1C41,     0xEDAE,     0xFD8F,     0xCDEC,     0xDDCD,
369     0xAD2A,     0xBD0B,     0x8D68,     0x9D49,     0x7E97,     0x6EB6,
370     0x5ED5,     0x4EF4,     0x3E13,     0x2E32,     0x1E51,     0x0E70,
371     0xFF9F,     0xEFBE,     0xDFDD,     0xCFFC,     0xBF1B,     0xAF3A,
372     0x9F59,     0x8F78,     0x9188,     0x81A9,     0xB1CA,     0xA1EB,
373     0xD10C,     0xC12D,     0xF14E,     0xE16F,     0x1080,     0x00A1,
374     0x30C2,     0x20E3,     0x5004,     0x4025,     0x7046,     0x6067,
375     0x83B9,     0x9398,     0xA3FB,     0xB3DA,     0xC33D,     0xD31C,
376     0xE37F,     0xF35E,     0x02B1,     0x1290,     0x22F3,     0x32D2,
377     0x4235,     0x5214,     0x6277,     0x7256,     0xB5EA,     0xA5CB,
378     0x95A8,     0x8589,     0xF56E,     0xE54F,     0xD52C,     0xC50D,
379     0x34E2,     0x24C3,     0x14A0,     0x0481,     0x7466,     0x6447,
380     0x5424,     0x4405,     0xA7DB,     0xB7FA,     0x8799,     0x97B8,
381     0xE75F,     0xF77E,     0xC71D,     0xD73C,     0x26D3,     0x36F2,
382     0x0691,     0x16B0,     0x6657,     0x7676,     0x4615,     0x5634,
383     0xD94C,     0xC96D,     0xF90E,     0xE92F,     0x99C8,     0x89E9,
384     0xB98A,     0xA9AB,     0x5844,     0x4865,     0x7806,     0x6827,
385     0x18C0,     0x08E1,     0x3882,     0x28A3,     0xCB7D,     0xDB5C,
386     0xEB3F,     0xFB1E,     0x8BF9,     0x9BD8,     0xABBB,     0xBB9A,
387     0x4A75,     0x5A54,     0x6A37,     0x7A16,     0x0AF1,     0x1AD0,
388     0x2AB3,     0x3A92,     0xFD2E,     0xED0F,     0xDD6C,     0xCD4D,
389     0xBDAA,     0xAD8B,     0x9DE8,     0x8DC9,     0x7C26,     0x6C07,
390     0x5C64,     0x4C45,     0x3CA2,     0x2C83,     0x1CE0,     0x0CC1,
391     0xEF1F,     0xFF3E,     0xCF5D,     0xDF7C,     0xAF9B,     0xBFBA,
392     0x8FD9,     0x9FF8,     0x6E17,     0x7E36,     0x4E55,     0x5E74,
393     0x2E93,     0x3EB2,     0x0ED1,     0x1EF0
394 };
395
396 /*
397  * Implements a CRC function for X worth bits using (uint[X]_t)
398  * as type. and util_crc[X]_table.
399
400  * Quake expects a non-reflective CRC.
401  */
402 #define CRC(X) \
403 uint##X##_t util_crc##X(uint##X##_t current, const char *k, size_t len) {  \
404     register uint##X##_t h= current;                                  \
405     for (; len; --len, ++k)                                           \
406         h = util_crc##X##_table[(h>>8)^((unsigned char)*k)]^(h<<8);   \
407     return h;                                                         \
408 }
409 CRC(32)
410 CRC(16)
411 #undef CRC
412 /*
413 #define CRC(X) \
414 uint##X##_t util_crc##X(const char *k, int len, const short clamp) {  \
415     register uint##X##_t h= (uint##X##_t)0xFFFFFFFF;                  \
416     for (; len; --len, ++k)                                           \
417         h = util_crc##X##_table[(h^((unsigned char)*k))&0xFF]^(h>>8); \
418     return (~h)%clamp;                                                \
419 }
420 */
421
422
423 /*
424  * Implements libc getline for systems that don't have it, which is
425  * assmed all.  This works the same as getline().
426  */
427 int util_getline(char **lineptr, size_t *n, FILE *stream) {
428     int   chr;
429     int   ret;
430     char *pos;
431
432     if (!lineptr || !n || !stream)
433         return -1;
434     if (!*lineptr) {
435         if (!(*lineptr = (char*)mem_a((*n=64))))
436             return -1;
437     }
438
439     chr = *n;
440     pos = *lineptr;
441
442     for (;;) {
443         int c = getc(stream);
444
445         if (chr < 2) {
446             char *tmp = (char*)mem_a((*n+=(*n>16)?*n:64));
447             if  (!tmp)
448                 return -1;
449
450             memcpy(tmp, *lineptr, pos - *lineptr);
451             chr = *n + *lineptr - pos;
452             if (!(*lineptr = tmp)) {
453                 mem_d (tmp);
454                 return -1;
455             }
456             pos = *n - chr + *lineptr;
457         }
458
459         if (ferror(stream))
460             return -1;
461         if (c == EOF) {
462             if (pos == *lineptr)
463                 return -1;
464             else
465                 break;
466         }
467
468         *pos++ = c;
469         chr--;
470         if (c == '\n')
471             break;
472     }
473     *pos = '\0';
474     return (ret = pos - *lineptr);
475 }
476
477 size_t util_strtocmd(const char *in, char *out, size_t outsz) {
478     size_t sz = 1;
479     for (; *in && sz < outsz; ++in, ++out, ++sz) {
480         if (*in == '-')
481             *out = '_';
482         else if (isalpha(*in) && !isupper(*in))
483             *out = *in + 'A' - 'a';
484         else
485             *out = *in;
486     }
487     *out = 0;
488     return sz-1;
489 }
490
491 size_t util_strtononcmd(const char *in, char *out, size_t outsz) {
492     size_t sz = 1;
493     for (; *in && sz < outsz; ++in, ++out, ++sz) {
494         if (*in == '_')
495             *out = '-';
496         else if (isalpha(*in) && isupper(*in))
497             *out = *in + 'a' - 'A';
498         else
499             *out = *in;
500     }
501     *out = 0;
502     return sz-1;
503 }
504
505 FILE *util_fopen(const char *filename, const char *mode)
506 {
507 #ifdef WIN32
508     FILE *out;
509     if (fopen_s(&out, filename, mode) != 0)
510         return NULL;
511     return out;
512 #else
513     return fopen(filename, mode);
514 #endif
515 }
516