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