1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      File I/O routines.
12  *
13  *      See LICENSE.txt for copyright information.
14  */
15 
16 
17 #include "allegro5/allegro.h"
18 #include "allegro5/internal/aintern.h"
19 #include "allegro5/internal/aintern_file.h"
20 
21 
22 /* Function: al_fopen
23  */
al_fopen(const char * path,const char * mode)24 ALLEGRO_FILE *al_fopen(const char *path, const char *mode)
25 {
26    return al_fopen_interface(al_get_new_file_interface(), path, mode);
27 }
28 
29 
30 /* Function: al_fopen_interface
31  */
al_fopen_interface(const ALLEGRO_FILE_INTERFACE * drv,const char * path,const char * mode)32 ALLEGRO_FILE *al_fopen_interface(const ALLEGRO_FILE_INTERFACE *drv,
33    const char *path, const char *mode)
34 {
35    ALLEGRO_FILE *f = NULL;
36 
37    ASSERT(drv);
38    ASSERT(path);
39    ASSERT(mode);
40 
41    if (drv->fi_fopen) {
42       f = al_malloc(sizeof(*f));
43       if (!f) {
44          al_set_errno(ENOMEM);
45       }
46       else {
47          f->vtable = drv;
48          f->userdata = drv->fi_fopen(path, mode);
49          f->ungetc_len = 0;
50          if (!f->userdata) {
51             al_free(f);
52             f = NULL;
53          }
54       }
55    }
56 
57    return f;
58 }
59 
60 
61 /* Function: al_create_file_handle
62  */
al_create_file_handle(const ALLEGRO_FILE_INTERFACE * drv,void * userdata)63 ALLEGRO_FILE *al_create_file_handle(const ALLEGRO_FILE_INTERFACE *drv,
64    void *userdata)
65 {
66    ALLEGRO_FILE *f;
67 
68    ASSERT(drv);
69 
70    f = al_malloc(sizeof(*f));
71    if (!f) {
72       al_set_errno(ENOMEM);
73    }
74    else {
75       f->vtable = drv;
76       f->userdata = userdata;
77       f->ungetc_len = 0;
78    }
79 
80    return f;
81 }
82 
83 
84 /* Function: al_fclose
85  */
al_fclose(ALLEGRO_FILE * f)86 bool al_fclose(ALLEGRO_FILE *f)
87 {
88    if (f) {
89       bool ret = f->vtable->fi_fclose(f);
90       al_free(f);
91       return ret;
92    }
93 
94    al_set_errno(EINVAL);
95    return false;
96 }
97 
98 
99 /* Function: al_fread
100  */
al_fread(ALLEGRO_FILE * f,void * ptr,size_t size)101 size_t al_fread(ALLEGRO_FILE *f, void *ptr, size_t size)
102 {
103    ASSERT(f);
104    ASSERT(ptr || size == 0);
105 
106    if (f->ungetc_len) {
107       int bytes_ungetc = 0;
108       unsigned char *cptr = ptr;
109 
110       while (f->ungetc_len > 0 && size > 0) {
111          *cptr++ = f->ungetc[--f->ungetc_len];
112          ++bytes_ungetc;
113          --size;
114       }
115 
116       return bytes_ungetc + f->vtable->fi_fread(f, cptr, size);
117    }
118    else {
119       return f->vtable->fi_fread(f, ptr, size);
120    }
121 }
122 
123 
124 /* Function: al_fwrite
125  */
al_fwrite(ALLEGRO_FILE * f,const void * ptr,size_t size)126 size_t al_fwrite(ALLEGRO_FILE *f, const void *ptr, size_t size)
127 {
128    ASSERT(f);
129    ASSERT(ptr || size == 0);
130 
131    f->ungetc_len = 0;
132    return f->vtable->fi_fwrite(f, ptr, size);
133 }
134 
135 
136 /* Function: al_fflush
137  */
al_fflush(ALLEGRO_FILE * f)138 bool al_fflush(ALLEGRO_FILE *f)
139 {
140    ASSERT(f);
141 
142    return f->vtable->fi_fflush(f);
143 }
144 
145 
146 /* Function: al_ftell
147  */
al_ftell(ALLEGRO_FILE * f)148 int64_t al_ftell(ALLEGRO_FILE *f)
149 {
150    ASSERT(f);
151 
152    return f->vtable->fi_ftell(f) - f->ungetc_len;
153 }
154 
155 
156 /* Function: al_fseek
157  */
al_fseek(ALLEGRO_FILE * f,int64_t offset,int whence)158 bool al_fseek(ALLEGRO_FILE *f, int64_t offset, int whence)
159 {
160    ASSERT(f);
161    /* offset can be negative */
162    ASSERT(
163       whence == ALLEGRO_SEEK_SET ||
164       whence == ALLEGRO_SEEK_CUR ||
165       whence == ALLEGRO_SEEK_END
166    );
167 
168    if (f->ungetc_len) {
169       if (whence == ALLEGRO_SEEK_CUR) {
170          offset -= f->ungetc_len;
171       }
172       f->ungetc_len = 0;
173    }
174 
175    return f->vtable->fi_fseek(f, offset, whence);
176 }
177 
178 
179 /* Function: al_feof
180  */
al_feof(ALLEGRO_FILE * f)181 bool al_feof(ALLEGRO_FILE *f)
182 {
183    ASSERT(f);
184 
185    return f->ungetc_len == 0 && f->vtable->fi_feof(f);
186 }
187 
188 
189 /* Function: al_ferror
190  */
al_ferror(ALLEGRO_FILE * f)191 int al_ferror(ALLEGRO_FILE *f)
192 {
193    ASSERT(f);
194 
195    return f->vtable->fi_ferror(f);
196 }
197 
198 
199 /* Function: al_ferrmsg
200  */
al_ferrmsg(ALLEGRO_FILE * f)201 const char *al_ferrmsg(ALLEGRO_FILE *f)
202 {
203    const char *msg;
204 
205    ASSERT(f);
206    msg = f->vtable->fi_ferrmsg(f);
207    ASSERT(msg);
208    return msg;
209 }
210 
211 
212 /* Function: al_fclearerr
213  */
al_fclearerr(ALLEGRO_FILE * f)214 void al_fclearerr(ALLEGRO_FILE *f)
215 {
216    ASSERT(f);
217 
218    f->vtable->fi_fclearerr(f);
219 }
220 
221 
222 /* Function: al_fgetc
223  */
al_fgetc(ALLEGRO_FILE * f)224 int al_fgetc(ALLEGRO_FILE *f)
225 {
226    uint8_t c;
227    ASSERT(f);
228 
229    if (al_fread(f, &c, 1) != 1) {
230       return EOF;
231    }
232 
233    return c;
234 }
235 
236 
237 /* Function: al_fputc
238  */
al_fputc(ALLEGRO_FILE * f,int c)239 int al_fputc(ALLEGRO_FILE *f, int c)
240 {
241    uint8_t b = (c & 0xff);
242    ASSERT(f);
243 
244    if (al_fwrite(f, &b, 1) != 1) {
245       return EOF;
246    }
247 
248    return b;
249 }
250 
251 
252 /* Function: al_fread16le
253  */
al_fread16le(ALLEGRO_FILE * f)254 int16_t al_fread16le(ALLEGRO_FILE *f)
255 {
256    unsigned char b[2];
257    ASSERT(f);
258 
259    if (al_fread(f, b, 2) == 2) {
260       return (((int16_t)b[1] << 8) | (int16_t)b[0]);
261    }
262 
263    return EOF;
264 }
265 
266 
267 /* Function: al_fread32le
268  */
al_fread32le(ALLEGRO_FILE * f)269 int32_t al_fread32le(ALLEGRO_FILE *f)
270 {
271    unsigned char b[4];
272    ASSERT(f);
273 
274    if (al_fread(f, b, 4) == 4) {
275       return (((int32_t)b[3] << 24) | ((int32_t)b[2] << 16) |
276               ((int32_t)b[1] << 8) | (int32_t)b[0]);
277    }
278 
279    return EOF;
280 }
281 
282 
283 /* Function: al_fwrite16le
284  */
al_fwrite16le(ALLEGRO_FILE * f,int16_t w)285 size_t al_fwrite16le(ALLEGRO_FILE *f, int16_t w)
286 {
287    uint8_t b1, b2;
288    ASSERT(f);
289 
290    b1 = (w & 0xFF00) >> 8;
291    b2 = w & 0x00FF;
292 
293    if (al_fputc(f, b2) == b2) {
294       if (al_fputc(f, b1) == b1) {
295          return 2;
296       }
297       return 1;
298    }
299    return 0;
300 }
301 
302 
303 /* Function: al_fwrite32le
304  */
al_fwrite32le(ALLEGRO_FILE * f,int32_t l)305 size_t al_fwrite32le(ALLEGRO_FILE *f, int32_t l)
306 {
307    uint8_t b1, b2, b3, b4;
308    ASSERT(f);
309 
310    b1 = ((l & 0xFF000000L) >> 24);
311    b2 = ((l & 0x00FF0000L) >> 16);
312    b3 = ((l & 0x0000FF00L) >> 8);
313    b4 = l & 0x00FF;
314 
315    if (al_fputc(f, b4) == b4) {
316       if (al_fputc(f, b3) == b3) {
317          if (al_fputc(f, b2) == b2) {
318             if (al_fputc(f, b1) == b1) {
319                return 4;
320             }
321             return 3;
322          }
323          return 2;
324       }
325       return 1;
326    }
327    return 0;
328 }
329 
330 
331 /* Function: al_fread16be
332  */
al_fread16be(ALLEGRO_FILE * f)333 int16_t al_fread16be(ALLEGRO_FILE *f)
334 {
335    unsigned char b[2];
336    ASSERT(f);
337 
338    if (al_fread(f, b, 2) == 2) {
339       return (((int16_t)b[0] << 8) | (int16_t)b[1]);
340    }
341 
342    return EOF;
343 }
344 
345 
346 /* Function: al_fread32be
347  */
al_fread32be(ALLEGRO_FILE * f)348 int32_t al_fread32be(ALLEGRO_FILE *f)
349 {
350    unsigned char b[4];
351    ASSERT(f);
352 
353    if (al_fread(f, b, 4) == 4) {
354       return (((int32_t)b[0] << 24) | ((int32_t)b[1] << 16) |
355               ((int32_t)b[2] << 8) | (int32_t)b[3]);
356    }
357 
358    return EOF;
359 }
360 
361 
362 /* Function: al_fwrite16be
363  */
al_fwrite16be(ALLEGRO_FILE * f,int16_t w)364 size_t al_fwrite16be(ALLEGRO_FILE *f, int16_t w)
365 {
366    uint8_t b1, b2;
367    ASSERT(f);
368 
369    b1 = (w & 0xFF00) >> 8;
370    b2 = w & 0x00FF;
371 
372    if (al_fputc(f, b1) == b1) {
373       if (al_fputc(f, b2) == b2) {
374          return 2;
375       }
376       return 1;
377    }
378    return 0;
379 }
380 
381 
382 /* Function: al_fwrite32be
383  */
al_fwrite32be(ALLEGRO_FILE * f,int32_t l)384 size_t al_fwrite32be(ALLEGRO_FILE *f, int32_t l)
385 {
386    uint8_t b1, b2, b3, b4;
387    ASSERT(f);
388 
389    b1 = ((l & 0xFF000000L) >> 24);
390    b2 = ((l & 0x00FF0000L) >> 16);
391    b3 = ((l & 0x0000FF00L) >> 8);
392    b4 = l & 0x00FF;
393 
394    if (al_fputc(f, b1) == b1) {
395       if (al_fputc(f, b2) == b2) {
396          if (al_fputc(f, b3) == b3) {
397             if (al_fputc(f, b4) == b4) {
398                return 4;
399             }
400             return 3;
401          }
402          return 2;
403       }
404       return 1;
405    }
406    return 0;
407 }
408 
409 
410 /* Function: al_fgets
411  */
al_fgets(ALLEGRO_FILE * f,char * const buf,size_t max)412 char *al_fgets(ALLEGRO_FILE *f, char * const buf, size_t max)
413 {
414    char *p = buf;
415    int c;
416    ASSERT(f);
417    ASSERT(buf);
418 
419    /* Handle silly cases. */
420    if (max == 0) {
421       return NULL;
422    }
423    if (max == 1) {
424       *buf = '\0';
425       return buf;
426    }
427 
428    /* Return NULL if already at end of file. */
429    if ((c = al_fgetc(f)) == EOF) {
430       return NULL;
431    }
432 
433    /* Fill buffer until empty, or we reach a newline or EOF or error. */
434    do {
435       *p++ = c;
436       max--;
437       if (max == 1 || c == '\n')
438          break;
439       c = al_fgetc(f);
440    } while (c != EOF);
441 
442    /* Return NULL on error. */
443    if (c == EOF && al_ferror(f)) {
444       return NULL;
445    }
446 
447    /* Add null terminator. */
448    ASSERT(max >= 1);
449    *p = '\0';
450 
451    return buf;
452 }
453 
454 
455 /* Function: al_fget_ustr
456  */
al_fget_ustr(ALLEGRO_FILE * f)457 ALLEGRO_USTR *al_fget_ustr(ALLEGRO_FILE *f)
458 {
459    ALLEGRO_USTR *us;
460    char buf[128];
461 
462    if (!al_fgets(f, buf, sizeof(buf))) {
463       return NULL;
464    }
465 
466    us = al_ustr_new("");
467 
468    do {
469       al_ustr_append_cstr(us, buf);
470       if (al_ustr_has_suffix_cstr(us, "\n"))
471          break;
472    } while (al_fgets(f, buf, sizeof(buf)));
473 
474    return us;
475 }
476 
477 
478 /* Function: al_fputs
479  */
al_fputs(ALLEGRO_FILE * f,char const * p)480 int al_fputs(ALLEGRO_FILE *f, char const *p)
481 {
482    size_t n;
483    ASSERT(f);
484    ASSERT(p);
485 
486    n = strlen(p);
487    if (al_fwrite(f, p, n) != n) {
488       return EOF;
489    }
490 
491    return n;
492 }
493 
494 
495 /* Function: al_fungetc
496  */
al_fungetc(ALLEGRO_FILE * f,int c)497 int al_fungetc(ALLEGRO_FILE *f, int c)
498 {
499    ASSERT(f != NULL);
500 
501    if (f->vtable->fi_fungetc) {
502       return f->vtable->fi_fungetc(f, c);
503    }
504    else {
505       /* If the interface does not provide an implementation for ungetc,
506        * then a default one will be used. (Note that if the interface does
507        * implement it, then this ungetc buffer will never be filled, and all
508        * other references to it within this file will always be ignored.)
509        */
510       if (f->ungetc_len == ALLEGRO_UNGETC_SIZE) {
511          return EOF;
512       }
513 
514       f->ungetc[f->ungetc_len++] = (unsigned char) c;
515 
516       return c;
517    }
518 }
519 
520 
521 /* Function: al_fsize
522  */
al_fsize(ALLEGRO_FILE * f)523 int64_t al_fsize(ALLEGRO_FILE *f)
524 {
525    ASSERT(f != NULL);
526 
527    return f->vtable->fi_fsize(f);
528 }
529 
530 
531 /* Function: al_get_file_userdata
532  */
al_get_file_userdata(ALLEGRO_FILE * f)533 void *al_get_file_userdata(ALLEGRO_FILE *f)
534 {
535    ASSERT(f != NULL);
536 
537    return f->userdata;
538 }
539 
540 
541 /* Function: al_vfprintf
542  */
al_vfprintf(ALLEGRO_FILE * pfile,const char * format,va_list args)543 int al_vfprintf(ALLEGRO_FILE *pfile, const char *format, va_list args)
544 {
545    int rv = -1;
546    ALLEGRO_USTR *ustr = 0;
547    size_t size = 0;
548    bool success;
549 
550    if (pfile != 0 && format != 0)
551    {
552       ustr = al_ustr_new("");
553       if (ustr)
554       {
555          success = al_ustr_vappendf(ustr, format, args);
556          if (success)
557          {
558             size = al_ustr_size(ustr);
559             if (size > 0)
560             {
561                rv = al_fwrite(pfile, (const void*)(al_cstr(ustr)), size);
562                if (rv != (int)size) {
563                   rv = -1;
564                }
565             }
566          }
567          al_ustr_free(ustr);
568       }
569    }
570    return rv;
571 }
572 
573 
574 /* Function: al_fprintf
575  */
al_fprintf(ALLEGRO_FILE * pfile,const char * format,...)576 int al_fprintf(ALLEGRO_FILE *pfile, const char *format, ...)
577 {
578    int rv = -1;
579    va_list args;
580 
581    if (pfile != 0 && format != 0)
582    {
583       va_start(args, format);
584       rv = al_vfprintf(pfile, format, args);
585       va_end(args);
586    }
587    return rv;
588 }
589 
590 
591 /* vim: set sts=3 sw=3 et: */
592