1 #include "cube.h"
2
3 ///////////////////////// character conversion ///////////////
4
5 #define CUBECTYPE(s, p, d, a, A, u, U) \
6 0, U, U, U, U, U, U, U, U, s, s, s, s, s, U, U, \
7 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \
8 s, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, \
9 d, d, d, d, d, d, d, d, d, d, p, p, p, p, p, p, \
10 p, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, \
11 A, A, A, A, A, A, A, A, A, A, A, p, p, p, p, p, \
12 p, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, \
13 a, a, a, a, a, a, a, a, a, a, a, p, p, p, p, U, \
14 U, u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, \
15 u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, \
16 u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \
17 u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \
18 u, U, u, U, u, U, u, U, U, u, U, u, U, u, U, U, \
19 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \
20 U, U, U, U, u, u, u, u, u, u, u, u, u, u, u, u, \
21 u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, u
22
23 extern const uchar cubectype[256] =
24 {
25 CUBECTYPE(CT_SPACE,
26 CT_PRINT,
27 CT_PRINT|CT_DIGIT,
28 CT_PRINT|CT_ALPHA|CT_LOWER,
29 CT_PRINT|CT_ALPHA|CT_UPPER,
30 CT_PRINT|CT_UNICODE|CT_ALPHA|CT_LOWER,
31 CT_PRINT|CT_UNICODE|CT_ALPHA|CT_UPPER)
32 };
33 extern const int cube2unichars[256] =
34 {
35 0, 192, 193, 194, 195, 196, 197, 198, 199, 9, 10, 11, 12, 13, 200, 201,
36 202, 203, 204, 205, 206, 207, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219,
37 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
38 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
39 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
40 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
41 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
42 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 220,
43 221, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
44 238, 239, 241, 242, 243, 244, 245, 246, 248, 249, 250, 251, 252, 253, 255, 0x104,
45 0x105, 0x106, 0x107, 0x10C, 0x10D, 0x10E, 0x10F, 0x118, 0x119, 0x11A, 0x11B, 0x11E, 0x11F, 0x130, 0x131, 0x141,
46 0x142, 0x143, 0x144, 0x147, 0x148, 0x150, 0x151, 0x152, 0x153, 0x158, 0x159, 0x15A, 0x15B, 0x15E, 0x15F, 0x160,
47 0x161, 0x164, 0x165, 0x16E, 0x16F, 0x170, 0x171, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x404, 0x411,
48 0x413, 0x414, 0x416, 0x417, 0x418, 0x419, 0x41B, 0x41F, 0x423, 0x424, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B,
49 0x42C, 0x42D, 0x42E, 0x42F, 0x431, 0x432, 0x433, 0x434, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D,
50 0x43F, 0x442, 0x444, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x454, 0x490, 0x491
51 };
52 extern const int uni2cubeoffsets[8] =
53 {
54 0, 256, 658, 658, 512, 658, 658, 658
55 };
56 extern const uchar uni2cubechars[878] =
57 {
58 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
60 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
61 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0,
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 0, 22, 23, 24, 25, 26, 27, 0, 28, 29, 30, 31, 127, 128, 0, 129,
65 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 0, 146, 147, 148, 149, 150, 151, 0, 152, 153, 154, 155, 156, 157, 0, 158,
66 0, 0, 0, 0, 159, 160, 161, 162, 0, 0, 0, 0, 163, 164, 165, 166, 0, 0, 0, 0, 0, 0, 0, 0, 167, 168, 169, 170, 0, 0, 171, 172,
67 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68 0, 175, 176, 177, 178, 0, 0, 179, 180, 0, 0, 0, 0, 0, 0, 0, 181, 182, 183, 184, 0, 0, 0, 0, 185, 186, 187, 188, 0, 0, 189, 190,
69 191, 192, 0, 0, 193, 194, 0, 0, 0, 0, 0, 0, 0, 0, 195, 196, 197, 198, 0, 0, 0, 0, 0, 0, 199, 200, 201, 202, 203, 204, 205, 0,
70 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
71 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74 0, 17, 0, 0, 206, 83, 73, 21, 74, 0, 0, 0, 0, 0, 0, 0, 65, 207, 66, 208, 209, 69, 210, 211, 212, 213, 75, 214, 77, 72, 79, 215,
75 80, 67, 84, 216, 217, 88, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 97, 228, 229, 230, 231, 101, 232, 233, 234, 235, 236, 237, 238, 239, 111, 240,
76 112, 99, 241, 121, 242, 120, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 0, 141, 0, 0, 253, 115, 105, 145, 106, 0, 0, 0, 0, 0, 0, 0,
77 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
78 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
79 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
80 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
81 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
82 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
83 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
84 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
85 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
86 };
87 extern const uchar cubelowerchars[256] =
88 {
89 0, 130, 131, 132, 133, 134, 135, 136, 137, 9, 10, 11, 12, 13, 138, 139,
90 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
91 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
92 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
93 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
94 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
95 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
96 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 156,
97 157, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
98 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160,
99 160, 162, 162, 164, 164, 166, 166, 168, 168, 170, 170, 172, 172, 105, 174, 176,
100 176, 178, 178, 180, 180, 182, 182, 184, 184, 186, 186, 188, 188, 190, 190, 192,
101 192, 194, 194, 196, 196, 198, 198, 158, 201, 201, 203, 203, 205, 205, 206, 207,
102 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
103 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
104 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
105 };
106 extern const uchar cubeupperchars[256] =
107 {
108 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
109 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
110 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
111 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
112 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
113 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
114 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
115 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127,
116 128, 129, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19,
117 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 127, 128, 199, 159,
118 159, 161, 161, 163, 163, 165, 165, 167, 167, 169, 169, 171, 171, 173, 73, 175,
119 175, 177, 177, 179, 179, 181, 181, 183, 183, 185, 185, 187, 187, 189, 189, 191,
120 191, 193, 193, 195, 195, 197, 197, 199, 200, 200, 202, 202, 204, 204, 206, 207,
121 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
122 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
123 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
124 };
125
decodeutf8(uchar * dstbuf,size_t dstlen,const uchar * srcbuf,size_t srclen,size_t * carry)126 size_t decodeutf8(uchar *dstbuf, size_t dstlen, const uchar *srcbuf, size_t srclen, size_t *carry)
127 {
128 uchar *dst = dstbuf, *dstend = &dstbuf[dstlen];
129 const uchar *src = srcbuf, *srcend = &srcbuf[srclen];
130 if(dstbuf == srcbuf)
131 {
132 int len = min(dstlen, srclen);
133 for(const uchar *end4 = &srcbuf[len&~3]; src < end4; src += 4) if(*(const int *)src & 0x80808080) goto decode;
134 for(const uchar *end = &srcbuf[len]; src < end; src++) if(*src & 0x80) goto decode;
135 if(carry) *carry += len;
136 return len;
137 }
138
139 decode:
140 dst += src - srcbuf;
141 while(src < srcend && dst < dstend)
142 {
143 int c = *src++;
144 if(c < 0x80) *dst++ = c;
145 else if(c >= 0xC0)
146 {
147 int uni;
148 if(c >= 0xE0)
149 {
150 if(c >= 0xF0)
151 {
152 if(c >= 0xF8)
153 {
154 if(c >= 0xFC)
155 {
156 if(c >= 0xFE) continue;
157 uni = c&1; if(srcend - src < 5) break;
158 c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F);
159 }
160 else { uni = c&3; if(srcend - src < 4) break; }
161 c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F);
162 }
163 else { uni = c&7; if(srcend - src < 3) break; }
164 c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F);
165 }
166 else { uni = c&0xF; if(srcend - src < 2) break; }
167 c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F);
168 }
169 else { uni = c&0x1F; if(srcend - src < 1) break; }
170 c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F);
171 c = uni2cube(uni);
172 if(!c) continue;
173 *dst++ = c;
174 }
175 }
176 if(carry) *carry += src - srcbuf;
177 return dst - dstbuf;
178 }
179
encodeutf8(uchar * dstbuf,size_t dstlen,const uchar * srcbuf,size_t srclen,size_t * carry)180 size_t encodeutf8(uchar *dstbuf, size_t dstlen, const uchar *srcbuf, size_t srclen, size_t *carry)
181 {
182 uchar *dst = dstbuf, *dstend = &dstbuf[dstlen];
183 const uchar *src = srcbuf, *srcend = &srcbuf[srclen];
184 if(src < srcend && dst < dstend) do
185 {
186 int uni = cube2uni(*src);
187 if(uni <= 0x7F)
188 {
189 if(dst >= dstend) goto done;
190 const uchar *end = min(srcend, &src[dstend-dst]);
191 do
192 {
193 if(uni == '\f')
194 {
195 if(++src >= srcend) goto done;
196 goto uni1;
197 }
198 *dst++ = uni;
199 if(++src >= end) goto done;
200 uni = cube2uni(*src);
201 }
202 while(uni <= 0x7F);
203 }
204 if(uni <= 0x7FF) { if(dst + 2 > dstend) goto done; *dst++ = 0xC0 | (uni>>6); goto uni2; }
205 else if(uni <= 0xFFFF) { if(dst + 3 > dstend) goto done; *dst++ = 0xE0 | (uni>>12); goto uni3; }
206 else if(uni <= 0x1FFFFF) { if(dst + 4 > dstend) goto done; *dst++ = 0xF0 | (uni>>18); goto uni4; }
207 else if(uni <= 0x3FFFFFF) { if(dst + 5 > dstend) goto done; *dst++ = 0xF8 | (uni>>24); goto uni5; }
208 else if(uni <= 0x7FFFFFFF) { if(dst + 6 > dstend) goto done; *dst++ = 0xFC | (uni>>30); goto uni6; }
209 else goto uni1;
210 uni6: *dst++ = 0x80 | ((uni>>24)&0x3F);
211 uni5: *dst++ = 0x80 | ((uni>>18)&0x3F);
212 uni4: *dst++ = 0x80 | ((uni>>12)&0x3F);
213 uni3: *dst++ = 0x80 | ((uni>>6)&0x3F);
214 uni2: *dst++ = 0x80 | (uni&0x3F);
215 uni1:;
216 }
217 while(++src < srcend);
218
219 done:
220 if(carry) *carry += src - srcbuf;
221 return dst - dstbuf;
222 }
223
224 ///////////////////////// file system ///////////////////////
225
226 #ifdef WIN32
227 #include <shlobj.h>
228 #else
229 #include <unistd.h>
230 #include <sys/stat.h>
231 #include <sys/types.h>
232 #include <dirent.h>
233 #endif
234
235 string homedir = "";
236 struct packagedir
237 {
238 char *dir, *filter;
239 size_t dirlen, filterlen;
240 };
241 vector<packagedir> packagedirs;
242
makerelpath(const char * dir,const char * file,const char * prefix,const char * cmd)243 char *makerelpath(const char *dir, const char *file, const char *prefix, const char *cmd)
244 {
245 static string tmp;
246 if(prefix) copystring(tmp, prefix);
247 else tmp[0] = '\0';
248 if(file[0]=='<')
249 {
250 const char *end = strrchr(file, '>');
251 if(end)
252 {
253 size_t len = strlen(tmp);
254 copystring(&tmp[len], file, min(sizeof(tmp)-len, size_t(end+2-file)));
255 file = end+1;
256 }
257 }
258 if(cmd) concatstring(tmp, cmd);
259 if(dir)
260 {
261 defformatstring(pname, "%s/%s", dir, file);
262 concatstring(tmp, pname);
263 }
264 else concatstring(tmp, file);
265 return tmp;
266 }
267
268
path(char * s)269 char *path(char *s)
270 {
271 for(char *curpart = s;;)
272 {
273 char *endpart = strchr(curpart, '&');
274 if(endpart) *endpart = '\0';
275 if(curpart[0]=='<')
276 {
277 char *file = strrchr(curpart, '>');
278 if(!file) return s;
279 curpart = file+1;
280 }
281 for(char *t = curpart; (t = strpbrk(t, "/\\")); *t++ = PATHDIV);
282 for(char *prevdir = NULL, *curdir = curpart;;)
283 {
284 prevdir = curdir[0]==PATHDIV ? curdir+1 : curdir;
285 curdir = strchr(prevdir, PATHDIV);
286 if(!curdir) break;
287 if(prevdir+1==curdir && prevdir[0]=='.')
288 {
289 memmove(prevdir, curdir+1, strlen(curdir+1)+1);
290 curdir = prevdir;
291 }
292 else if(curdir[1]=='.' && curdir[2]=='.' && curdir[3]==PATHDIV)
293 {
294 if(prevdir+2==curdir && prevdir[0]=='.' && prevdir[1]=='.') continue;
295 memmove(prevdir, curdir+4, strlen(curdir+4)+1);
296 curdir = prevdir;
297 }
298 }
299 if(endpart)
300 {
301 *endpart = '&';
302 curpart = endpart+1;
303 }
304 else break;
305 }
306 return s;
307 }
308
path(const char * s,bool copy)309 char *path(const char *s, bool copy)
310 {
311 static string tmp;
312 copystring(tmp, s);
313 path(tmp);
314 return tmp;
315 }
316
parentdir(const char * directory)317 const char *parentdir(const char *directory)
318 {
319 const char *p = directory + strlen(directory);
320 while(p > directory && *p != '/' && *p != '\\') p--;
321 static string parent;
322 size_t len = p-directory+1;
323 copystring(parent, directory, len);
324 return parent;
325 }
326
fileexists(const char * path,const char * mode)327 bool fileexists(const char *path, const char *mode)
328 {
329 bool exists = true;
330 if(mode[0]=='w' || mode[0]=='a') path = parentdir(path);
331 #ifdef WIN32
332 if(GetFileAttributes(path[0] ? path : ".\\") == INVALID_FILE_ATTRIBUTES) exists = false;
333 #else
334 if(access(path[0] ? path : ".", mode[0]=='w' || mode[0]=='a' ? W_OK : (mode[0]=='d' ? X_OK : R_OK)) == -1) exists = false;
335 #endif
336 return exists;
337 }
338
createdir(const char * path)339 bool createdir(const char *path)
340 {
341 size_t len = strlen(path);
342 if(path[len-1]==PATHDIV)
343 {
344 static string strip;
345 path = copystring(strip, path, len);
346 }
347 #ifdef WIN32
348 return CreateDirectory(path, NULL)!=0;
349 #else
350 return mkdir(path, 0777)==0;
351 #endif
352 }
353
fixpackagedir(char * dir)354 size_t fixpackagedir(char *dir)
355 {
356 path(dir);
357 size_t len = strlen(dir);
358 if(len > 0 && dir[len-1] != PATHDIV)
359 {
360 dir[len] = PATHDIV;
361 dir[len+1] = '\0';
362 }
363 return len;
364 }
365
subhomedir(char * dst,int len,const char * src)366 bool subhomedir(char *dst, int len, const char *src)
367 {
368 const char *sub = strstr(src, "$HOME");
369 if(!sub) sub = strchr(src, '~');
370 if(sub && sub-src < len)
371 {
372 #ifdef WIN32
373 char home[MAX_PATH+1];
374 home[0] = '\0';
375 if(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, home) != S_OK || !home[0]) return false;
376 #else
377 const char *home = getenv("HOME");
378 if(!home || !home[0]) return false;
379 #endif
380 dst[sub-src] = '\0';
381 concatstring(dst, home);
382 concatstring(dst, sub+(*sub == '~' ? 1 : strlen("$HOME")));
383 }
384 return true;
385 }
386
sethomedir(const char * dir)387 const char *sethomedir(const char *dir)
388 {
389 string pdir;
390 copystring(pdir, dir);
391 if(!subhomedir(pdir, sizeof(pdir), dir) || !fixpackagedir(pdir)) return NULL;
392 copystring(homedir, pdir);
393 return homedir;
394 }
395
addpackagedir(const char * dir)396 const char *addpackagedir(const char *dir)
397 {
398 string pdir;
399 copystring(pdir, dir);
400 if(!subhomedir(pdir, sizeof(pdir), dir) || !fixpackagedir(pdir)) return NULL;
401 char *filter = pdir;
402 for(;;)
403 {
404 static int len = strlen("media");
405 filter = strstr(filter, "media");
406 if(!filter) break;
407 if(filter > pdir && filter[-1] == PATHDIV && filter[len] == PATHDIV) break;
408 filter += len;
409 }
410 packagedir &pf = packagedirs.add();
411 pf.dir = filter ? newstring(pdir, filter-pdir) : newstring(pdir);
412 pf.dirlen = filter ? filter-pdir : strlen(pdir);
413 pf.filter = filter ? newstring(filter) : NULL;
414 pf.filterlen = filter ? strlen(filter) : 0;
415 return pf.dir;
416 }
417
findfile(const char * filename,const char * mode)418 const char *findfile(const char *filename, const char *mode)
419 {
420 static string s;
421 if(homedir[0])
422 {
423 formatstring(s, "%s%s", homedir, filename);
424 if(fileexists(s, mode)) return s;
425 if(mode[0]=='w' || mode[0]=='a')
426 {
427 string dirs;
428 copystring(dirs, s);
429 char *dir = strchr(dirs[0]==PATHDIV ? dirs+1 : dirs, PATHDIV);
430 while(dir)
431 {
432 *dir = '\0';
433 if(!fileexists(dirs, "d") && !createdir(dirs)) return s;
434 *dir = PATHDIV;
435 dir = strchr(dir+1, PATHDIV);
436 }
437 return s;
438 }
439 }
440 if(mode[0]=='w' || mode[0]=='a') return filename;
441 loopv(packagedirs)
442 {
443 packagedir &pf = packagedirs[i];
444 if(pf.filter && strncmp(filename, pf.filter, pf.filterlen)) continue;
445 formatstring(s, "%s%s", pf.dir, filename);
446 if(fileexists(s, mode)) return s;
447 }
448 if(mode[0]=='e') return NULL;
449 return filename;
450 }
451
listdir(const char * dirname,bool rel,const char * ext,vector<char * > & files)452 bool listdir(const char *dirname, bool rel, const char *ext, vector<char *> &files)
453 {
454 size_t extsize = ext ? strlen(ext)+1 : 0;
455 #ifdef WIN32
456 defformatstring(pathname, rel ? ".\\%s\\*.%s" : "%s\\*.%s", dirname, ext ? ext : "*");
457 WIN32_FIND_DATA FindFileData;
458 HANDLE Find = FindFirstFile(pathname, &FindFileData);
459 if(Find != INVALID_HANDLE_VALUE)
460 {
461 do {
462 if(!ext) files.add(newstring(FindFileData.cFileName));
463 else
464 {
465 size_t namelen = strlen(FindFileData.cFileName);
466 if(namelen > extsize)
467 {
468 namelen -= extsize;
469 if(FindFileData.cFileName[namelen] == '.' && strncmp(FindFileData.cFileName+namelen+1, ext, extsize-1)==0)
470 files.add(newstring(FindFileData.cFileName, namelen));
471 }
472 }
473 } while(FindNextFile(Find, &FindFileData));
474 FindClose(Find);
475 return true;
476 }
477 #else
478 defformatstring(pathname, rel ? "./%s" : "%s", dirname);
479 DIR *d = opendir(pathname);
480 if(d)
481 {
482 struct dirent *de;
483 while((de = readdir(d)) != NULL)
484 {
485 if(!ext) files.add(newstring(de->d_name));
486 else
487 {
488 size_t namelen = strlen(de->d_name);
489 if(namelen > extsize)
490 {
491 namelen -= extsize;
492 if(de->d_name[namelen] == '.' && strncmp(de->d_name+namelen+1, ext, extsize-1)==0)
493 files.add(newstring(de->d_name, namelen));
494 }
495 }
496 }
497 closedir(d);
498 return true;
499 }
500 #endif
501 else return false;
502 }
503
listfiles(const char * dir,const char * ext,vector<char * > & files)504 int listfiles(const char *dir, const char *ext, vector<char *> &files)
505 {
506 string dirname;
507 copystring(dirname, dir);
508 path(dirname);
509 size_t dirlen = strlen(dirname);
510 while(dirlen > 1 && dirname[dirlen-1] == PATHDIV) dirname[--dirlen] = '\0';
511 int dirs = 0;
512 if(listdir(dirname, true, ext, files)) dirs++;
513 string s;
514 if(homedir[0])
515 {
516 formatstring(s, "%s%s", homedir, dirname);
517 if(listdir(s, false, ext, files)) dirs++;
518 }
519 loopv(packagedirs)
520 {
521 packagedir &pf = packagedirs[i];
522 if(pf.filter && strncmp(dirname, pf.filter, dirlen == pf.filterlen-1 ? dirlen : pf.filterlen))
523 continue;
524 formatstring(s, "%s%s", pf.dir, dirname);
525 if(listdir(s, false, ext, files)) dirs++;
526 }
527 #ifndef STANDALONE
528 dirs += listzipfiles(dirname, ext, files);
529 #endif
530 return dirs;
531 }
532
533 #ifndef STANDALONE
rwopsseek(SDL_RWops * rw,Sint64 pos,int whence)534 static Sint64 rwopsseek(SDL_RWops *rw, Sint64 pos, int whence)
535 {
536 stream *f = (stream *)rw->hidden.unknown.data1;
537 if((!pos && whence==SEEK_CUR) || f->seek(pos, whence)) return (int)f->tell();
538 return -1;
539 }
540
rwopsread(SDL_RWops * rw,void * buf,size_t size,size_t nmemb)541 static size_t rwopsread(SDL_RWops *rw, void *buf, size_t size, size_t nmemb)
542 {
543 stream *f = (stream *)rw->hidden.unknown.data1;
544 return f->read(buf, size*nmemb)/size;
545 }
546
rwopswrite(SDL_RWops * rw,const void * buf,size_t size,size_t nmemb)547 static size_t rwopswrite(SDL_RWops *rw, const void *buf, size_t size, size_t nmemb)
548 {
549 stream *f = (stream *)rw->hidden.unknown.data1;
550 return f->write(buf, size*nmemb)/size;
551 }
552
rwopsclose(SDL_RWops * rw)553 static int rwopsclose(SDL_RWops *rw)
554 {
555 return 0;
556 }
557
rwops()558 SDL_RWops *stream::rwops()
559 {
560 SDL_RWops *rw = SDL_AllocRW();
561 if(!rw) return NULL;
562 rw->hidden.unknown.data1 = this;
563 rw->seek = rwopsseek;
564 rw->read = rwopsread;
565 rw->write = rwopswrite;
566 rw->close = rwopsclose;
567 return rw;
568 }
569 #endif
570
size()571 stream::offset stream::size()
572 {
573 offset pos = tell(), endpos;
574 if(pos < 0 || !seek(0, SEEK_END)) return -1;
575 endpos = tell();
576 return pos == endpos || seek(pos, SEEK_SET) ? endpos : -1;
577 }
578
getline(char * str,size_t len)579 bool stream::getline(char *str, size_t len)
580 {
581 loopi(len-1)
582 {
583 if(read(&str[i], 1) != 1) { str[i] = '\0'; return i > 0; }
584 else if(str[i] == '\n') { str[i+1] = '\0'; return true; }
585 }
586 if(len > 0) str[len-1] = '\0';
587 return true;
588 }
589
printf(const char * fmt,...)590 size_t stream::printf(const char *fmt, ...)
591 {
592 char buf[512];
593 char *str = buf;
594 va_list args;
595 #if defined(WIN32) && !defined(__GNUC__)
596 va_start(args, fmt);
597 int len = _vscprintf(fmt, args);
598 if(len <= 0) { va_end(args); return 0; }
599 if(len >= (int)sizeof(buf)) str = new char[len+1];
600 _vsnprintf(str, len+1, fmt, args);
601 va_end(args);
602 #else
603 va_start(args, fmt);
604 int len = vsnprintf(buf, sizeof(buf), fmt, args);
605 va_end(args);
606 if(len <= 0) return 0;
607 if(len >= (int)sizeof(buf))
608 {
609 str = new char[len+1];
610 va_start(args, fmt);
611 vsnprintf(str, len+1, fmt, args);
612 va_end(args);
613 }
614 #endif
615 size_t n = write(str, len);
616 if(str != buf) delete[] str;
617 return n;
618 }
619
620 struct filestream : stream
621 {
622 FILE *file;
623
filestreamfilestream624 filestream() : file(NULL) {}
~filestreamfilestream625 ~filestream() { close(); }
626
openfilestream627 bool open(const char *name, const char *mode)
628 {
629 if(file) return false;
630 file = fopen(name, mode);
631 return file!=NULL;
632 }
633
opentempfilestream634 bool opentemp(const char *name, const char *mode)
635 {
636 if(file) return false;
637 #ifdef WIN32
638 file = fopen(name, mode);
639 #else
640 file = tmpfile();
641 #endif
642 return file!=NULL;
643 }
644
closefilestream645 void close()
646 {
647 if(file) { fclose(file); file = NULL; }
648 }
649
endfilestream650 bool end() { return feof(file)!=0; }
tellfilestream651 offset tell()
652 {
653 #ifdef WIN32
654 #ifdef __GNUC__
655 return ftello64(file);
656 #else
657 return _ftelli64(file);
658 #endif
659 #else
660 return ftello(file);
661 #endif
662 }
seekfilestream663 bool seek(offset pos, int whence)
664 {
665 #ifdef WIN32
666 #ifdef __GNUC__
667 return fseeko64(file, pos, whence) >= 0;
668 #else
669 return _fseeki64(file, pos, whence) >= 0;
670 #endif
671 #else
672 return fseeko(file, pos, whence) >= 0;
673 #endif
674 }
675
readfilestream676 size_t read(void *buf, size_t len) { return fread(buf, 1, len, file); }
writefilestream677 size_t write(const void *buf, size_t len) { return fwrite(buf, 1, len, file); }
flushfilestream678 bool flush() { return !fflush(file); }
getcharfilestream679 int getchar() { return fgetc(file); }
putcharfilestream680 bool putchar(int c) { return fputc(c, file)!=EOF; }
getlinefilestream681 bool getline(char *str, size_t len) { return fgets(str, len, file)!=NULL; }
putstringfilestream682 bool putstring(const char *str) { return fputs(str, file)!=EOF; }
683
printffilestream684 size_t printf(const char *fmt, ...)
685 {
686 va_list v;
687 va_start(v, fmt);
688 int result = vfprintf(file, fmt, v);
689 va_end(v);
690 return max(result, 0);
691 }
692 };
693
694 #ifndef STANDALONE
695 VAR(dbggz, 0, 0, 1);
696 #endif
697
698 struct gzstream : stream
699 {
700 enum
701 {
702 MAGIC1 = 0x1F,
703 MAGIC2 = 0x8B,
704 BUFSIZE = 16384,
705 OS_UNIX = 0x03
706 };
707
708 enum
709 {
710 F_ASCII = 0x01,
711 F_CRC = 0x02,
712 F_EXTRA = 0x04,
713 F_NAME = 0x08,
714 F_COMMENT = 0x10,
715 F_RESERVED = 0xE0
716 };
717
718 stream *file;
719 z_stream zfile;
720 uchar *buf;
721 bool reading, writing, autoclose;
722 uint crc;
723 size_t headersize;
724
gzstreamgzstream725 gzstream() : file(NULL), buf(NULL), reading(false), writing(false), autoclose(false), crc(0), headersize(0)
726 {
727 zfile.zalloc = NULL;
728 zfile.zfree = NULL;
729 zfile.opaque = NULL;
730 zfile.next_in = zfile.next_out = NULL;
731 zfile.avail_in = zfile.avail_out = 0;
732 }
733
~gzstreamgzstream734 ~gzstream()
735 {
736 close();
737 }
738
writeheadergzstream739 void writeheader()
740 {
741 uchar header[] = { MAGIC1, MAGIC2, Z_DEFLATED, 0, 0, 0, 0, 0, 0, OS_UNIX };
742 file->write(header, sizeof(header));
743 }
744
readbufgzstream745 void readbuf(size_t size = BUFSIZE)
746 {
747 if(!zfile.avail_in) zfile.next_in = (Bytef *)buf;
748 size = min(size, size_t(&buf[BUFSIZE] - &zfile.next_in[zfile.avail_in]));
749 size_t n = file->read(zfile.next_in + zfile.avail_in, size);
750 if(n > 0) zfile.avail_in += n;
751 }
752
readbytegzstream753 uchar readbyte(size_t size = BUFSIZE)
754 {
755 if(!zfile.avail_in) readbuf(size);
756 if(!zfile.avail_in) return 0;
757 zfile.avail_in--;
758 return *(uchar *)zfile.next_in++;
759 }
760
skipbytesgzstream761 void skipbytes(size_t n)
762 {
763 while(n > 0 && zfile.avail_in > 0)
764 {
765 size_t skipped = min(n, size_t(zfile.avail_in));
766 zfile.avail_in -= skipped;
767 zfile.next_in += skipped;
768 n -= skipped;
769 }
770 if(n <= 0) return;
771 file->seek(n, SEEK_CUR);
772 }
773
checkheadergzstream774 bool checkheader()
775 {
776 readbuf(10);
777 if(readbyte() != MAGIC1 || readbyte() != MAGIC2 || readbyte() != Z_DEFLATED) return false;
778 uchar flags = readbyte();
779 if(flags & F_RESERVED) return false;
780 skipbytes(6);
781 if(flags & F_EXTRA)
782 {
783 size_t len = readbyte(512);
784 len |= size_t(readbyte(512))<<8;
785 skipbytes(len);
786 }
787 if(flags & F_NAME) while(readbyte(512));
788 if(flags & F_COMMENT) while(readbyte(512));
789 if(flags & F_CRC) skipbytes(2);
790 headersize = size_t(file->tell() - zfile.avail_in);
791 return zfile.avail_in > 0 || !file->end();
792 }
793
opengzstream794 bool open(stream *f, const char *mode, bool needclose, int level)
795 {
796 if(file) return false;
797 for(; *mode; mode++)
798 {
799 if(*mode=='r') { reading = true; break; }
800 else if(*mode=='w') { writing = true; break; }
801 }
802 if(reading)
803 {
804 if(inflateInit2(&zfile, -MAX_WBITS) != Z_OK) reading = false;
805 }
806 else if(writing && deflateInit2(&zfile, level, Z_DEFLATED, -MAX_WBITS, min(MAX_MEM_LEVEL, 8), Z_DEFAULT_STRATEGY) != Z_OK) writing = false;
807 if(!reading && !writing) return false;
808
809 autoclose = needclose;
810 file = f;
811 crc = crc32(0, NULL, 0);
812 buf = new uchar[BUFSIZE];
813
814 if(reading)
815 {
816 if(!checkheader()) { stopreading(); return false; }
817 }
818 else if(writing) writeheader();
819 return true;
820 }
821
getcrcgzstream822 uint getcrc() { return crc; }
823
finishreadinggzstream824 void finishreading()
825 {
826 if(!reading) return;
827 #ifndef STANDALONE
828 if(dbggz)
829 {
830 uint checkcrc = 0, checksize = 0;
831 loopi(4) checkcrc |= uint(readbyte()) << (i*8);
832 loopi(4) checksize |= uint(readbyte()) << (i*8);
833 if(checkcrc != crc)
834 conoutf(CON_DEBUG, "gzip crc check failed: read %X, calculated %X", checkcrc, crc);
835 if(checksize != zfile.total_out)
836 conoutf(CON_DEBUG, "gzip size check failed: read %u, calculated %u", checksize, uint(zfile.total_out));
837 }
838 #endif
839 }
840
stopreadinggzstream841 void stopreading()
842 {
843 if(!reading) return;
844 inflateEnd(&zfile);
845 reading = false;
846 }
847
finishwritinggzstream848 void finishwriting()
849 {
850 if(!writing) return;
851 for(;;)
852 {
853 int err = zfile.avail_out > 0 ? deflate(&zfile, Z_FINISH) : Z_OK;
854 if(err != Z_OK && err != Z_STREAM_END) break;
855 flushbuf();
856 if(err == Z_STREAM_END) break;
857 }
858 uchar trailer[8] =
859 {
860 uchar(crc&0xFF), uchar((crc>>8)&0xFF), uchar((crc>>16)&0xFF), uchar((crc>>24)&0xFF),
861 uchar(zfile.total_in&0xFF), uchar((zfile.total_in>>8)&0xFF), uchar((zfile.total_in>>16)&0xFF), uchar((zfile.total_in>>24)&0xFF)
862 };
863 file->write(trailer, sizeof(trailer));
864 }
865
stopwritinggzstream866 void stopwriting()
867 {
868 if(!writing) return;
869 deflateEnd(&zfile);
870 writing = false;
871 }
872
closegzstream873 void close()
874 {
875 if(reading) finishreading();
876 stopreading();
877 if(writing) finishwriting();
878 stopwriting();
879 DELETEA(buf);
880 if(autoclose) DELETEP(file);
881 }
882
endgzstream883 bool end() { return !reading && !writing; }
tellgzstream884 offset tell() { return reading ? zfile.total_out : (writing ? zfile.total_in : offset(-1)); }
rawtellgzstream885 offset rawtell() { return file ? file->tell() : offset(-1); }
886
sizegzstream887 offset size()
888 {
889 if(!file) return -1;
890 offset pos = tell();
891 if(!file->seek(-4, SEEK_END)) return -1;
892 uint isize = file->getlil<uint>();
893 return file->seek(pos, SEEK_SET) ? isize : offset(-1);
894 }
895
rawsizegzstream896 offset rawsize() { return file ? file->size() : offset(-1); }
897
seekgzstream898 bool seek(offset pos, int whence)
899 {
900 if(writing || !reading) return false;
901
902 if(whence == SEEK_END)
903 {
904 uchar skip[512];
905 while(read(skip, sizeof(skip)) == sizeof(skip));
906 return !pos;
907 }
908 else if(whence == SEEK_CUR) pos += zfile.total_out;
909
910 if(pos >= (offset)zfile.total_out) pos -= zfile.total_out;
911 else if(pos < 0 || !file->seek(headersize, SEEK_SET)) return false;
912 else
913 {
914 if(zfile.next_in && zfile.total_in <= uint(zfile.next_in - buf))
915 {
916 zfile.avail_in += zfile.total_in;
917 zfile.next_in -= zfile.total_in;
918 }
919 else
920 {
921 zfile.avail_in = 0;
922 zfile.next_in = NULL;
923 }
924 inflateReset(&zfile);
925 crc = crc32(0, NULL, 0);
926 }
927
928 uchar skip[512];
929 while(pos > 0)
930 {
931 size_t skipped = (size_t)min(pos, (offset)sizeof(skip));
932 if(read(skip, skipped) != skipped) { stopreading(); return false; }
933 pos -= skipped;
934 }
935
936 return true;
937 }
938
readgzstream939 size_t read(void *buf, size_t len)
940 {
941 if(!reading || !buf || !len) return 0;
942 zfile.next_out = (Bytef *)buf;
943 zfile.avail_out = len;
944 while(zfile.avail_out > 0)
945 {
946 if(!zfile.avail_in)
947 {
948 readbuf(BUFSIZE);
949 if(!zfile.avail_in) { stopreading(); break; }
950 }
951 int err = inflate(&zfile, Z_NO_FLUSH);
952 if(err == Z_STREAM_END) { crc = crc32(crc, (Bytef *)buf, len - zfile.avail_out); finishreading(); stopreading(); return len - zfile.avail_out; }
953 else if(err != Z_OK) { stopreading(); break; }
954 }
955 crc = crc32(crc, (Bytef *)buf, len - zfile.avail_out);
956 return len - zfile.avail_out;
957 }
958
flushbufgzstream959 bool flushbuf(bool full = false)
960 {
961 if(full) deflate(&zfile, Z_SYNC_FLUSH);
962 if(zfile.next_out && zfile.avail_out < BUFSIZE)
963 {
964 if(file->write(buf, BUFSIZE - zfile.avail_out) != BUFSIZE - zfile.avail_out || (full && !file->flush()))
965 return false;
966 }
967 zfile.next_out = buf;
968 zfile.avail_out = BUFSIZE;
969 return true;
970 }
971
flushgzstream972 bool flush() { return flushbuf(true); }
973
writegzstream974 size_t write(const void *buf, size_t len)
975 {
976 if(!writing || !buf || !len) return 0;
977 zfile.next_in = (Bytef *)buf;
978 zfile.avail_in = len;
979 while(zfile.avail_in > 0)
980 {
981 if(!zfile.avail_out && !flushbuf()) { stopwriting(); break; }
982 int err = deflate(&zfile, Z_NO_FLUSH);
983 if(err != Z_OK) { stopwriting(); break; }
984 }
985 crc = crc32(crc, (Bytef *)buf, len - zfile.avail_in);
986 return len - zfile.avail_in;
987 }
988 };
989
990 struct utf8stream : stream
991 {
992 enum
993 {
994 BUFSIZE = 4096
995 };
996 stream *file;
997 offset pos;
998 size_t bufread, bufcarry, buflen;
999 bool reading, writing, autoclose;
1000 uchar buf[BUFSIZE];
1001
utf8streamutf8stream1002 utf8stream() : file(NULL), pos(0), bufread(0), bufcarry(0), buflen(0), reading(false), writing(false), autoclose(false)
1003 {
1004 }
1005
~utf8streamutf8stream1006 ~utf8stream()
1007 {
1008 close();
1009 }
1010
readbufutf8stream1011 bool readbuf(size_t size = BUFSIZE)
1012 {
1013 if(bufread >= bufcarry) { if(bufcarry > 0 && bufcarry < buflen) memmove(buf, &buf[bufcarry], buflen - bufcarry); buflen -= bufcarry; bufread = bufcarry = 0; }
1014 size_t n = file->read(&buf[buflen], min(size, BUFSIZE - buflen));
1015 if(n <= 0) return false;
1016 buflen += n;
1017 size_t carry = bufcarry;
1018 bufcarry += decodeutf8(&buf[bufcarry], BUFSIZE-bufcarry, &buf[bufcarry], buflen-bufcarry, &carry);
1019 if(carry > bufcarry && carry < buflen) { memmove(&buf[bufcarry], &buf[carry], buflen - carry); buflen -= carry - bufcarry; }
1020 return true;
1021 }
1022
checkheaderutf8stream1023 bool checkheader()
1024 {
1025 size_t n = file->read(buf, 3);
1026 if(n == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) return true;
1027 buflen = n;
1028 return false;
1029 }
1030
openutf8stream1031 bool open(stream *f, const char *mode, bool needclose)
1032 {
1033 if(file) return false;
1034 for(; *mode; mode++)
1035 {
1036 if(*mode=='r') { reading = true; break; }
1037 else if(*mode=='w') { writing = true; break; }
1038 }
1039 if(!reading && !writing) return false;
1040
1041 autoclose = needclose;
1042 file = f;
1043
1044 if(reading) checkheader();
1045
1046 return true;
1047 }
1048
finishreadingutf8stream1049 void finishreading()
1050 {
1051 if(!reading) return;
1052 }
1053
stopreadingutf8stream1054 void stopreading()
1055 {
1056 if(!reading) return;
1057 reading = false;
1058 }
1059
stopwritingutf8stream1060 void stopwriting()
1061 {
1062 if(!writing) return;
1063 writing = false;
1064 }
1065
closeutf8stream1066 void close()
1067 {
1068 stopreading();
1069 stopwriting();
1070 if(autoclose) DELETEP(file);
1071 }
1072
endutf8stream1073 bool end() { return !reading && !writing; }
tellutf8stream1074 offset tell() { return reading || writing ? pos : offset(-1); }
1075
seekutf8stream1076 bool seek(offset off, int whence)
1077 {
1078 if(writing || !reading) return false;
1079
1080 if(whence == SEEK_END)
1081 {
1082 uchar skip[512];
1083 while(read(skip, sizeof(skip)) == sizeof(skip));
1084 return !off;
1085 }
1086 else if(whence == SEEK_CUR) off += pos;
1087
1088 if(off >= pos) off -= pos;
1089 else if(off < 0 || !file->seek(0, SEEK_SET)) return false;
1090 else
1091 {
1092 bufread = bufcarry = buflen = 0;
1093 pos = 0;
1094 checkheader();
1095 }
1096
1097 uchar skip[512];
1098 while(off > 0)
1099 {
1100 size_t skipped = (size_t)min(off, (offset)sizeof(skip));
1101 if(read(skip, skipped) != skipped) { stopreading(); return false; }
1102 off -= skipped;
1103 }
1104
1105 return true;
1106 }
1107
readutf8stream1108 size_t read(void *dst, size_t len)
1109 {
1110 if(!reading || !dst || !len) return 0;
1111 size_t next = 0;
1112 while(next < len)
1113 {
1114 if(bufread >= bufcarry) { if(readbuf(BUFSIZE)) continue; stopreading(); break; }
1115 size_t n = min(len - next, bufcarry - bufread);
1116 memcpy(&((uchar *)dst)[next], &buf[bufread], n);
1117 next += n;
1118 bufread += n;
1119 }
1120 pos += next;
1121 return next;
1122 }
1123
getlineutf8stream1124 bool getline(char *dst, size_t len)
1125 {
1126 if(!reading || !dst || !len) return false;
1127 --len;
1128 size_t next = 0;
1129 while(next < len)
1130 {
1131 if(bufread >= bufcarry) { if(readbuf(BUFSIZE)) continue; stopreading(); if(!next) return false; break; }
1132 size_t n = min(len - next, bufcarry - bufread);
1133 uchar *endline = (uchar *)memchr(&buf[bufread], '\n', n);
1134 if(endline) { n = endline+1 - &buf[bufread]; len = next + n; }
1135 memcpy(&((uchar *)dst)[next], &buf[bufread], n);
1136 next += n;
1137 bufread += n;
1138 }
1139 dst[next] = '\0';
1140 pos += next;
1141 return true;
1142 }
1143
writeutf8stream1144 size_t write(const void *src, size_t len)
1145 {
1146 if(!writing || !src || !len) return 0;
1147 uchar dst[512];
1148 size_t next = 0;
1149 while(next < len)
1150 {
1151 size_t carry = 0, n = encodeutf8(dst, sizeof(dst), &((uchar *)src)[next], len - next, &carry);
1152 if(n > 0 && file->write(dst, n) != n) { stopwriting(); break; }
1153 next += carry;
1154 }
1155 pos += next;
1156 return next;
1157 }
1158
flushutf8stream1159 bool flush() { return file->flush(); }
1160 };
1161
openrawfile(const char * filename,const char * mode)1162 stream *openrawfile(const char *filename, const char *mode)
1163 {
1164 const char *found = findfile(filename, mode);
1165 if(!found) return NULL;
1166 filestream *file = new filestream;
1167 if(!file->open(found, mode)) { delete file; return NULL; }
1168 return file;
1169 }
1170
openfile(const char * filename,const char * mode)1171 stream *openfile(const char *filename, const char *mode)
1172 {
1173 #ifndef STANDALONE
1174 stream *s = openzipfile(filename, mode);
1175 if(s) return s;
1176 #endif
1177 return openrawfile(filename, mode);
1178 }
1179
opentempfile(const char * name,const char * mode)1180 stream *opentempfile(const char *name, const char *mode)
1181 {
1182 const char *found = findfile(name, mode);
1183 filestream *file = new filestream;
1184 if(!file->opentemp(found ? found : name, mode)) { delete file; return NULL; }
1185 return file;
1186 }
1187
opengzfile(const char * filename,const char * mode,stream * file,int level)1188 stream *opengzfile(const char *filename, const char *mode, stream *file, int level)
1189 {
1190 stream *source = file ? file : openfile(filename, mode);
1191 if(!source) return NULL;
1192 gzstream *gz = new gzstream;
1193 if(!gz->open(source, mode, !file, level)) { if(!file) delete source; return NULL; }
1194 return gz;
1195 }
1196
openutf8file(const char * filename,const char * mode,stream * file)1197 stream *openutf8file(const char *filename, const char *mode, stream *file)
1198 {
1199 stream *source = file ? file : openfile(filename, mode);
1200 if(!source) return NULL;
1201 utf8stream *utf8 = new utf8stream;
1202 if(!utf8->open(source, mode, !file)) { if(!file) delete source; return NULL; }
1203 return utf8;
1204 }
1205
loadfile(const char * fn,size_t * size,bool utf8)1206 char *loadfile(const char *fn, size_t *size, bool utf8)
1207 {
1208 stream *f = openfile(fn, "rb");
1209 if(!f) return NULL;
1210 size_t len = f->size();
1211 if(len <= 0) { delete f; return NULL; }
1212 char *buf = new char[len+1];
1213 if(!buf) { delete f; return NULL; }
1214 size_t offset = 0;
1215 if(utf8 && len >= 3)
1216 {
1217 if(f->read(buf, 3) != 3) { delete f; delete[] buf; return NULL; }
1218 if(((uchar *)buf)[0] == 0xEF && ((uchar *)buf)[1] == 0xBB && ((uchar *)buf)[2] == 0xBF) len -= 3;
1219 else offset += 3;
1220 }
1221 size_t rlen = f->read(&buf[offset], len-offset);
1222 delete f;
1223 if(rlen != len-offset) { delete[] buf; return NULL; }
1224 if(utf8) len = decodeutf8((uchar *)buf, len, (uchar *)buf, len);
1225 buf[len] = '\0';
1226 if(size!=NULL) *size = len;
1227 return buf;
1228 }
1229
1230