1 /* ----------------------------------------------------------------------
2  * RPly library, read/write PLY files
3  * Diego Nehab, Princeton University
4  * http://www.cs.princeton.edu/~diego/professional/rply
5  *
6  * This library is distributed under the MIT License. See notice
7  * at the end of this file.
8  * ---------------------------------------------------------------------- */
9 #include <stdio.h>
10 #include <ctype.h>
11 #include <assert.h>
12 #include <string.h>
13 #include <limits.h>
14 #include <float.h>
15 #include <stdarg.h>
16 #include <stdlib.h>
17 #include <stddef.h>
18 
19 #include "rply.h"
20 
21 /* ----------------------------------------------------------------------
22  * Constants
23  * ---------------------------------------------------------------------- */
24 #define WORDSIZE 256
25 #define LINESIZE 1024
26 #define BUFFERSIZE (8*1024)
27 
28 typedef enum e_ply_io_mode_ {
29     PLY_READ,
30     PLY_WRITE
31 } e_ply_io_mode;
32 
33 static const char *const ply_storage_mode_list[] = {
34     "binary_big_endian", "binary_little_endian", "ascii", NULL
35 };     /* order matches e_ply_storage_mode enum */
36 
37 static const char *const ply_type_list[] = {
38     "int8", "uint8", "int16", "uint16",
39     "int32", "uint32", "float32", "float64",
40     "char", "uchar", "short", "ushort",
41     "int", "uint", "float", "double",
42     "list", NULL
43 };     /* order matches e_ply_type enum */
44 
45 /* ----------------------------------------------------------------------
46  * Property reading callback argument
47  *
48  * element: name of element being processed
49  * property: name of property being processed
50  * nelements: number of elements of this kind in file
51  * instance_index: index current element of this kind being processed
52  * length: number of values in current list (or 1 for scalars)
53  * value_index: index of current value int this list (or 0 for scalars)
54  * value: value of property
55  * pdata/idata: user data defined with ply_set_cb
56  *
57  * Returns handle to ply file if succesful, NULL otherwise.
58  * ---------------------------------------------------------------------- */
59 typedef struct t_ply_argument_ {
60     p_ply_element element;
61     long instance_index;
62     p_ply_property property;
63     long length, value_index;
64     double value;
65     void *pdata;
66     long idata;
67 } t_ply_argument;
68 
69 /* ----------------------------------------------------------------------
70  * Property information
71  *
72  * name: name of this property
73  * type: type of this property (list or type of scalar value)
74  * length_type, value_type: type of list property count and values
75  * read_cb: function to be called when this property is called
76  *
77  * Returns 1 if should continue processing file, 0 if should abort.
78  * ---------------------------------------------------------------------- */
79 typedef struct t_ply_property_ {
80     char name[WORDSIZE];
81     e_ply_type type, value_type, length_type;
82     p_ply_read_cb read_cb;
83     void *pdata;
84     long idata;
85 } t_ply_property;
86 
87 /* ----------------------------------------------------------------------
88  * Element information
89  *
90  * name: name of this property
91  * ninstances: number of elements of this type in file
92  * property: property descriptions for this element
93  * nproperty: number of properties in this element
94  *
95  * Returns 1 if should continue processing file, 0 if should abort.
96  * ---------------------------------------------------------------------- */
97 typedef struct t_ply_element_ {
98     char name[WORDSIZE];
99     long ninstances;
100     p_ply_property property;
101     long nproperties;
102 } t_ply_element;
103 
104 /* ----------------------------------------------------------------------
105  * Input/output driver
106  *
107  * Depending on file mode, different functions are used to read/write
108  * property fields. The drivers make it transparent to read/write in ascii,
109  * big endian or little endian cases.
110  * ---------------------------------------------------------------------- */
111 typedef int (*p_ply_ihandler)(p_ply ply, double *value);
112 typedef int (*p_ply_ichunk)(p_ply ply, void *anydata, size_t size);
113 typedef struct t_ply_idriver_ {
114     p_ply_ihandler ihandler[16];
115     p_ply_ichunk ichunk;
116     const char *name;
117 } t_ply_idriver;
118 typedef t_ply_idriver *p_ply_idriver;
119 
120 typedef int (*p_ply_ohandler)(p_ply ply, double value);
121 typedef int (*p_ply_ochunk)(p_ply ply, void *anydata, size_t size);
122 typedef struct t_ply_odriver_ {
123     p_ply_ohandler ohandler[16];
124     p_ply_ochunk ochunk;
125     const char *name;
126 } t_ply_odriver;
127 typedef t_ply_odriver *p_ply_odriver;
128 
129 /* ----------------------------------------------------------------------
130  * Ply file handle.
131  *
132  * io_mode: read or write (from e_ply_io_mode)
133  * storage_mode: mode of file associated with handle (from e_ply_storage_mode)
134  * element: elements description for this file
135  * nelement: number of different elements in file
136  * comment: comments for this file
137  * ncomments: number of comments in file
138  * obj_info: obj_info items for this file
139  * nobj_infos: number of obj_info items in file
140  * fp: file pointer associated with ply file
141  * c: last character read from ply file
142  * buffer: last word/chunck of data read from ply file
143  * buffer_first, buffer_last: interval of untouched good data in buffer
144  * buffer_token: start of parsed token (line or word) in buffer
145  * idriver, odriver: input driver used to get property fields from file
146  * argument: storage space for callback arguments
147  * welement, wproperty: element/property type being written
148  * winstance_index: index of instance of current element being written
149  * wvalue_index: index of list property value being written
150  * wlength: number of values in list property being written
151  * error_cb: callback for error messages
152  * ---------------------------------------------------------------------- */
153 typedef struct t_ply_ {
154     e_ply_io_mode io_mode;
155     e_ply_storage_mode storage_mode;
156     p_ply_element element;
157     long nelements;
158     char *comment;
159     long ncomments;
160     char *obj_info;
161     long nobj_infos;
162     FILE *fp;
163     int c;
164     char buffer[BUFFERSIZE];
165     size_t buffer_first, buffer_token, buffer_last;
166     p_ply_idriver idriver;
167     p_ply_odriver odriver;
168     t_ply_argument argument;
169     long welement, wproperty;
170     long winstance_index, wvalue_index, wlength;
171     p_ply_error_cb error_cb;
172 } t_ply;
173 
174 /* ----------------------------------------------------------------------
175  * I/O functions and drivers
176  * ---------------------------------------------------------------------- */
177 static t_ply_idriver ply_idriver_ascii;
178 static t_ply_idriver ply_idriver_binary;
179 static t_ply_idriver ply_idriver_binary_reverse;
180 static t_ply_odriver ply_odriver_ascii;
181 static t_ply_odriver ply_odriver_binary;
182 static t_ply_odriver ply_odriver_binary_reverse;
183 
184 static int ply_read_word(p_ply ply);
185 static int ply_check_word(p_ply ply);
186 static int ply_read_line(p_ply ply);
187 static int ply_check_line(p_ply ply);
188 static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size);
189 static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size);
190 static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size);
191 static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size);
192 static void ply_reverse(void *anydata, size_t size);
193 
194 /* ----------------------------------------------------------------------
195  * String functions
196  * ---------------------------------------------------------------------- */
197 static int ply_find_string(const char *item, const char* const list[]);
198 static p_ply_element ply_find_element(p_ply ply, const char *name);
199 static p_ply_property ply_find_property(p_ply_element element,
200         const char *name);
201 
202 /* ----------------------------------------------------------------------
203  * Header parsing
204  * ---------------------------------------------------------------------- */
205 static int ply_read_header_format(p_ply ply);
206 static int ply_read_header_comment(p_ply ply);
207 static int ply_read_header_obj_info(p_ply ply);
208 static int ply_read_header_property(p_ply ply);
209 static int ply_read_header_element(p_ply ply);
210 
211 /* ----------------------------------------------------------------------
212  * Error handling
213  * ---------------------------------------------------------------------- */
214 static void ply_error_cb(const char *message);
215 static void ply_error(p_ply ply, const char *fmt, ...);
216 
217 /* ----------------------------------------------------------------------
218  * Memory allocation and initialization
219  * ---------------------------------------------------------------------- */
220 static void ply_init(p_ply ply);
221 static void ply_element_init(p_ply_element element);
222 static void ply_property_init(p_ply_property property);
223 static p_ply ply_alloc(void);
224 static p_ply_element ply_grow_element(p_ply ply);
225 static p_ply_property ply_grow_property(p_ply ply, p_ply_element element);
226 static void *ply_grow_array(p_ply ply, void **pointer, long *nmemb, long size);
227 
228 /* ----------------------------------------------------------------------
229  * Special functions
230  * ---------------------------------------------------------------------- */
231 static e_ply_storage_mode ply_arch_endian(void);
232 static int ply_type_check(void);
233 
234 /* ----------------------------------------------------------------------
235  * Auxiliary read functions
236  * ---------------------------------------------------------------------- */
237 static int ply_read_element(p_ply ply, p_ply_element element,
238         p_ply_argument argument);
239 static int ply_read_property(p_ply ply, p_ply_element element,
240         p_ply_property property, p_ply_argument argument);
241 static int ply_read_list_property(p_ply ply, p_ply_element element,
242         p_ply_property property, p_ply_argument argument);
243 static int ply_read_scalar_property(p_ply ply, p_ply_element element,
244         p_ply_property property, p_ply_argument argument);
245 
246 
247 /* ----------------------------------------------------------------------
248  * Buffer support functions
249  * ---------------------------------------------------------------------- */
250 /* pointers to tokenized word and line in buffer */
251 #define BWORD(p) (p->buffer + p->buffer_token)
252 #define BLINE(p) (p->buffer + p->buffer_token)
253 
254 /* pointer to start of untouched bytes in buffer */
255 #define BFIRST(p) (p->buffer + p->buffer_first)
256 
257 /* number of bytes untouched in buffer */
258 #define BSIZE(p) (p->buffer_last - p->buffer_first)
259 
260 /* consumes data from buffer */
261 #define BSKIP(p, s) (p->buffer_first += s)
262 
263 /* refills the buffer */
BREFILL(p_ply ply)264 static int BREFILL(p_ply ply) {
265     /* move untouched data to beginning of buffer */
266     size_t size = BSIZE(ply);
267     memmove(ply->buffer, BFIRST(ply), size);
268     ply->buffer_last = size;
269     ply->buffer_first = ply->buffer_token = 0;
270     /* fill remaining with new data */
271     size = fread(ply->buffer+size, 1, BUFFERSIZE-size-1, ply->fp);
272     /* place sentinel so we can use str* functions with buffer */
273     ply->buffer[BUFFERSIZE-1] = '\0';
274     /* check if read failed */
275     if (size <= 0) return 0;
276     /* increase size to account for new data */
277     ply->buffer_last += size;
278     return 1;
279 }
280 
281 /* ----------------------------------------------------------------------
282  * Exported functions
283  * ---------------------------------------------------------------------- */
284 /* ----------------------------------------------------------------------
285  * Read support functions
286  * ---------------------------------------------------------------------- */
ply_open(const char * name,p_ply_error_cb error_cb)287 p_ply ply_open(const char *name, p_ply_error_cb error_cb) {
288     char magic[5] = "    ";
289     FILE *fp = NULL;
290     p_ply ply = NULL;
291     if (error_cb == NULL) error_cb = ply_error_cb;
292     if (!ply_type_check()) {
293         error_cb("Incompatible type system");
294         return NULL;
295     }
296     assert(name);
297     fp = fopen(name, "rb");
298     if (!fp) {
299         error_cb("Unable to open file");
300         return NULL;
301     }
302     if (fread(magic, 1, 4, fp) < 4) {
303         error_cb("Error reading from file");
304         fclose(fp);
305         return NULL;
306     }
307     if (strcmp(magic, "ply\n")) {
308         fclose(fp);
309         error_cb("Not a PLY file. Expected magic number 'ply\\n'");
310         return NULL;
311     }
312     ply = ply_alloc();
313     if (!ply) {
314         error_cb("Out of memory");
315         fclose(fp);
316         return NULL;
317     }
318     ply->fp = fp;
319     ply->io_mode = PLY_READ;
320     ply->error_cb = error_cb;
321     return ply;
322 }
323 
ply_read_header(p_ply ply)324 int ply_read_header(p_ply ply) {
325     assert(ply && ply->fp && ply->io_mode == PLY_READ);
326     if (!ply_read_word(ply)) return 0;
327     /* parse file format */
328     if (!ply_read_header_format(ply)) {
329         ply_error(ply, "Invalid file format");
330         return 0;
331     }
332     /* parse elements, comments or obj_infos until the end of header */
333     while (strcmp(BWORD(ply), "end_header")) {
334         if (!ply_read_header_comment(ply) &&
335                 !ply_read_header_element(ply) &&
336                 !ply_read_header_obj_info(ply)) {
337             ply_error(ply, "Unexpected token '%s'", BWORD(ply));
338             return 0;
339         }
340     }
341     return 1;
342 }
343 
ply_set_read_cb(p_ply ply,const char * element_name,const char * property_name,p_ply_read_cb read_cb,void * pdata,long idata)344 long ply_set_read_cb(p_ply ply, const char *element_name,
345         const char* property_name, p_ply_read_cb read_cb,
346         void *pdata, long idata) {
347     p_ply_element element = NULL;
348     p_ply_property property = NULL;
349     assert(ply && element_name && property_name);
350     element = ply_find_element(ply, element_name);
351     if (!element) return 0;
352     property = ply_find_property(element, property_name);
353     if (!property) return 0;
354     property->read_cb = read_cb;
355     property->pdata = pdata;
356     property->idata = idata;
357     return (int) element->ninstances;
358 }
359 
ply_read(p_ply ply)360 int ply_read(p_ply ply) {
361     long i;
362     p_ply_argument argument;
363     assert(ply && ply->fp && ply->io_mode == PLY_READ);
364     argument = &ply->argument;
365     /* for each element type */
366     for (i = 0; i < ply->nelements; i++) {
367         p_ply_element element = &ply->element[i];
368         argument->element = element;
369         if (!ply_read_element(ply, element, argument))
370             return 0;
371     }
372     return 1;
373 }
374 
375 /* ----------------------------------------------------------------------
376  * Write support functions
377  * ---------------------------------------------------------------------- */
ply_create(const char * name,e_ply_storage_mode storage_mode,p_ply_error_cb error_cb)378 p_ply ply_create(const char *name, e_ply_storage_mode storage_mode,
379         p_ply_error_cb error_cb) {
380     FILE *fp = NULL;
381     p_ply ply = NULL;
382     if (error_cb == NULL) error_cb = ply_error_cb;
383     if (!ply_type_check()) {
384         error_cb("Incompatible type system");
385         return NULL;
386     }
387     assert(name && storage_mode <= PLY_DEFAULT);
388     fp = fopen(name, "wb");
389     if (!fp) {
390         error_cb("Unable to create file");
391         return NULL;
392     }
393     ply = ply_alloc();
394     if (!ply) {
395         fclose(fp);
396         error_cb("Out of memory");
397         return NULL;
398     }
399     ply->io_mode = PLY_WRITE;
400     if (storage_mode == PLY_DEFAULT) storage_mode = ply_arch_endian();
401     if (storage_mode == PLY_ASCII) ply->odriver = &ply_odriver_ascii;
402     else if (storage_mode == ply_arch_endian())
403         ply->odriver = &ply_odriver_binary;
404     else ply->odriver = &ply_odriver_binary_reverse;
405     ply->storage_mode = storage_mode;
406     ply->fp = fp;
407     ply->error_cb = error_cb;
408     return ply;
409 }
410 
ply_add_element(p_ply ply,const char * name,long ninstances)411 int ply_add_element(p_ply ply, const char *name, long ninstances) {
412     p_ply_element element = NULL;
413     assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
414     assert(name && strlen(name) < WORDSIZE && ninstances >= 0);
415     if (strlen(name) >= WORDSIZE || ninstances < 0) {
416         ply_error(ply, "Invalid arguments");
417         return 0;
418     }
419     element = ply_grow_element(ply);
420     if (!element) return 0;
421     strcpy(element->name, name);
422     element->ninstances = ninstances;
423     return 1;
424 }
425 
ply_add_scalar_property(p_ply ply,const char * name,e_ply_type type)426 int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type) {
427     p_ply_element element = NULL;
428     p_ply_property property = NULL;
429     assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
430     assert(name && strlen(name) < WORDSIZE);
431     assert(type < PLY_LIST);
432     if (strlen(name) >= WORDSIZE || type >= PLY_LIST) {
433         ply_error(ply, "Invalid arguments");
434         return 0;
435     }
436     element = &ply->element[ply->nelements-1];
437     property = ply_grow_property(ply, element);
438     if (!property) return 0;
439     strcpy(property->name, name);
440     property->type = type;
441     return 1;
442 }
443 
ply_add_list_property(p_ply ply,const char * name,e_ply_type length_type,e_ply_type value_type)444 int ply_add_list_property(p_ply ply, const char *name,
445         e_ply_type length_type, e_ply_type value_type) {
446     p_ply_element element = NULL;
447     p_ply_property property = NULL;
448     assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
449     assert(name && strlen(name) < WORDSIZE);
450     if (strlen(name) >= WORDSIZE) {
451         ply_error(ply, "Invalid arguments");
452         return 0;
453     }
454     assert(length_type < PLY_LIST);
455     assert(value_type < PLY_LIST);
456     if (length_type >= PLY_LIST || value_type >= PLY_LIST) {
457         ply_error(ply, "Invalid arguments");
458         return 0;
459     }
460     element = &ply->element[ply->nelements-1];
461     property = ply_grow_property(ply, element);
462     if (!property) return 0;
463     strcpy(property->name, name);
464     property->type = PLY_LIST;
465     property->length_type = length_type;
466     property->value_type = value_type;
467     return 1;
468 }
469 
ply_add_property(p_ply ply,const char * name,e_ply_type type,e_ply_type length_type,e_ply_type value_type)470 int ply_add_property(p_ply ply, const char *name, e_ply_type type,
471         e_ply_type length_type, e_ply_type value_type) {
472     if (type == PLY_LIST)
473         return ply_add_list_property(ply, name, length_type, value_type);
474     else
475         return ply_add_scalar_property(ply, name, type);
476 }
477 
ply_add_comment(p_ply ply,const char * comment)478 int ply_add_comment(p_ply ply, const char *comment) {
479     char *new_comment = NULL;
480     assert(ply && comment && strlen(comment) < LINESIZE);
481     if (!comment || strlen(comment) >= LINESIZE) {
482         ply_error(ply, "Invalid arguments");
483         return 0;
484     }
485     new_comment = (char *) ply_grow_array(ply, (void **) &ply->comment,
486             &ply->ncomments, LINESIZE);
487     if (!new_comment) return 0;
488     strcpy(new_comment, comment);
489     return 1;
490 }
491 
ply_add_obj_info(p_ply ply,const char * obj_info)492 int ply_add_obj_info(p_ply ply, const char *obj_info) {
493     char *new_obj_info = NULL;
494     assert(ply && obj_info && strlen(obj_info) < LINESIZE);
495     if (!obj_info || strlen(obj_info) >= LINESIZE) {
496         ply_error(ply, "Invalid arguments");
497         return 0;
498     }
499     new_obj_info = (char *) ply_grow_array(ply, (void **) &ply->obj_info,
500             &ply->nobj_infos, LINESIZE);
501     if (!new_obj_info) return 0;
502     strcpy(new_obj_info, obj_info);
503     return 1;
504 }
505 
ply_write_header(p_ply ply)506 int ply_write_header(p_ply ply) {
507     long i, j;
508     assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
509     assert(ply->element || ply->nelements == 0);
510     assert(!ply->element || ply->nelements > 0);
511     if (fprintf(ply->fp, "ply\nformat %s 1.0\n",
512                 ply_storage_mode_list[ply->storage_mode]) <= 0) goto error;
513     for (i = 0; i < ply->ncomments; i++)
514         if (fprintf(ply->fp, "comment %s\n", ply->comment + LINESIZE*i) <= 0)
515             goto error;
516     for (i = 0; i < ply->nobj_infos; i++)
517         if (fprintf(ply->fp, "obj_info %s\n", ply->obj_info + LINESIZE*i) <= 0)
518             goto error;
519     for (i = 0; i < ply->nelements; i++) {
520         p_ply_element element = &ply->element[i];
521         assert(element->property || element->nproperties == 0);
522         assert(!element->property || element->nproperties > 0);
523         if (fprintf(ply->fp, "element %s %ld\n", element->name,
524                     element->ninstances) <= 0) goto error;
525         for (j = 0; j < element->nproperties; j++) {
526             p_ply_property property = &element->property[j];
527             if (property->type == PLY_LIST) {
528                 if (fprintf(ply->fp, "property list %s %s %s\n",
529                             ply_type_list[property->length_type],
530                             ply_type_list[property->value_type],
531                             property->name) <= 0) goto error;
532             } else {
533                 if (fprintf(ply->fp, "property %s %s\n",
534                             ply_type_list[property->type],
535                             property->name) <= 0) goto error;
536             }
537         }
538     }
539     return fprintf(ply->fp, "end_header\n") > 0;
540 error:
541     ply_error(ply, "Error writing to file");
542     return 0;
543 }
544 
ply_write(p_ply ply,double value)545 int ply_write(p_ply ply, double value) {
546     p_ply_element element = NULL;
547     p_ply_property property = NULL;
548     int type = -1;
549     int breakafter = 0;
550     if (ply->welement > ply->nelements) return 0;
551     element = &ply->element[ply->welement];
552     if (ply->wproperty > element->nproperties) return 0;
553     property = &element->property[ply->wproperty];
554     if (property->type == PLY_LIST) {
555         if (ply->wvalue_index == 0) {
556             type = property->length_type;
557             ply->wlength = (long) value;
558         } else type = property->value_type;
559     } else {
560         type = property->type;
561         ply->wlength = 0;
562     }
563     if (!ply->odriver->ohandler[type](ply, value)) {
564         ply_error(ply, "Failed writing %s of %s %d (%s: %s)",
565                     property->name, element->name,
566                     ply->winstance_index,
567                     ply->odriver->name, ply_type_list[type]);
568         return 0;
569     }
570     ply->wvalue_index++;
571     if (ply->wvalue_index > ply->wlength) {
572         ply->wvalue_index = 0;
573         ply->wproperty++;
574     }
575     if (ply->wproperty >= element->nproperties) {
576         ply->wproperty = 0;
577         ply->winstance_index++;
578         if (ply->storage_mode == PLY_ASCII) breakafter = 1;
579     }
580     if (ply->winstance_index >= element->ninstances) {
581         ply->winstance_index = 0;
582         ply->welement++;
583     }
584     return !breakafter || putc('\n', ply->fp) > 0;
585 }
586 
ply_close(p_ply ply)587 int ply_close(p_ply ply) {
588     long i;
589     assert(ply && ply->fp);
590     assert(ply->element || ply->nelements == 0);
591     assert(!ply->element || ply->nelements > 0);
592     /* write last chunk to file */
593     if (ply->io_mode == PLY_WRITE &&
594       fwrite(ply->buffer, 1, ply->buffer_last, ply->fp) < ply->buffer_last) {
595         ply_error(ply, "Error closing up");
596         return 0;
597     }
598     fclose(ply->fp);
599     /* free all memory used by handle */
600     if (ply->element) {
601         for (i = 0; i < ply->nelements; i++) {
602             p_ply_element element = &ply->element[i];
603             if (element->property) free(element->property);
604         }
605         free(ply->element);
606     }
607     if (ply->obj_info) free(ply->obj_info);
608     if (ply->comment) free(ply->comment);
609     free(ply);
610     return 1;
611 }
612 
613 /* ----------------------------------------------------------------------
614  * Query support functions
615  * ---------------------------------------------------------------------- */
ply_get_next_element(p_ply ply,p_ply_element last)616 p_ply_element ply_get_next_element(p_ply ply,
617         p_ply_element last) {
618     assert(ply);
619     if (!last) return ply->element;
620     last++;
621     if (last < ply->element + ply->nelements) return last;
622     else return NULL;
623 }
624 
ply_get_element_info(p_ply_element element,const char ** name,long * ninstances)625 int ply_get_element_info(p_ply_element element, const char** name,
626         long *ninstances) {
627     assert(element);
628     if (name) *name = element->name;
629     if (ninstances) *ninstances = (long) element->ninstances;
630     return 1;
631 }
632 
ply_get_next_property(p_ply_element element,p_ply_property last)633 p_ply_property ply_get_next_property(p_ply_element element,
634         p_ply_property last) {
635     assert(element);
636     if (!last) return element->property;
637     last++;
638     if (last < element->property + element->nproperties) return last;
639     else return NULL;
640 }
641 
ply_get_property_info(p_ply_property property,const char ** name,e_ply_type * type,e_ply_type * length_type,e_ply_type * value_type)642 int ply_get_property_info(p_ply_property property, const char** name,
643         e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type) {
644     assert(property);
645     if (name) *name = property->name;
646     if (type) *type = property->type;
647     if (length_type) *length_type = property->length_type;
648     if (value_type) *value_type = property->value_type;
649     return 1;
650 
651 }
652 
ply_get_next_comment(p_ply ply,const char * last)653 const char *ply_get_next_comment(p_ply ply, const char *last) {
654     assert(ply);
655     if (!last) return ply->comment;
656     last += LINESIZE;
657     if (last < ply->comment + LINESIZE*ply->ncomments) return last;
658     else return NULL;
659 }
660 
ply_get_next_obj_info(p_ply ply,const char * last)661 const char *ply_get_next_obj_info(p_ply ply, const char *last) {
662     assert(ply);
663     if (!last) return ply->obj_info;
664     last += LINESIZE;
665     if (last < ply->obj_info + LINESIZE*ply->nobj_infos) return last;
666     else return NULL;
667 }
668 
669 /* ----------------------------------------------------------------------
670  * Callback argument support functions
671  * ---------------------------------------------------------------------- */
ply_get_argument_element(p_ply_argument argument,p_ply_element * element,long * instance_index)672 int ply_get_argument_element(p_ply_argument argument,
673         p_ply_element *element, long *instance_index) {
674     assert(argument);
675     if (!argument) return 0;
676     if (element) *element = argument->element;
677     if (instance_index) *instance_index = argument->instance_index;
678     return 1;
679 }
680 
ply_get_argument_property(p_ply_argument argument,p_ply_property * property,long * length,long * value_index)681 int ply_get_argument_property(p_ply_argument argument,
682         p_ply_property *property, long *length, long *value_index) {
683     assert(argument);
684     if (!argument) return 0;
685     if (property) *property = argument->property;
686     if (length) *length = argument->length;
687     if (value_index) *value_index = argument->value_index;
688     return 1;
689 }
690 
ply_get_argument_user_data(p_ply_argument argument,void ** pdata,long * idata)691 int ply_get_argument_user_data(p_ply_argument argument, void **pdata,
692         long *idata) {
693     assert(argument);
694     if (!argument) return 0;
695     if (pdata) *pdata = argument->pdata;
696     if (idata) *idata = argument->idata;
697     return 1;
698 }
699 
ply_get_argument_value(p_ply_argument argument)700 double ply_get_argument_value(p_ply_argument argument) {
701     assert(argument);
702     if (!argument) return 0.0;
703     return argument->value;
704 }
705 
706 /* ----------------------------------------------------------------------
707  * Internal functions
708  * ---------------------------------------------------------------------- */
ply_read_list_property(p_ply ply,p_ply_element element,p_ply_property property,p_ply_argument argument)709 static int ply_read_list_property(p_ply ply, p_ply_element element,
710         p_ply_property property, p_ply_argument argument) {
711     int l;
712     p_ply_read_cb read_cb = property->read_cb;
713     p_ply_ihandler *driver = ply->idriver->ihandler;
714     /* get list length */
715     p_ply_ihandler handler = driver[property->length_type];
716     double length;
717     if (!handler(ply, &length)) {
718         ply_error(ply, "Error reading '%s' of '%s' number %d",
719                 property->name, element->name, argument->instance_index);
720         return 0;
721     }
722     /* invoke callback to pass length in value field */
723     argument->length = (long) length;
724     argument->value_index = -1;
725     argument->value = length;
726     if (read_cb && !read_cb(argument)) {
727         ply_error(ply, "Aborted by user");
728         return 0;
729     }
730     /* read list values */
731     handler = driver[property->value_type];
732     /* for each value in list */
733     for (l = 0; l < (long) length; l++) {
734         /* read value from file */
735         argument->value_index = l;
736         if (!handler(ply, &argument->value)) {
737             ply_error(ply, "Error reading value number %d of '%s' of "
738                     "'%s' number %d", l+1, property->name,
739                     element->name, argument->instance_index);
740             return 0;
741         }
742         /* invoke callback to pass value */
743         if (read_cb && !read_cb(argument)) {
744             ply_error(ply, "Aborted by user");
745             return 0;
746         }
747     }
748     return 1;
749 }
750 
ply_read_scalar_property(p_ply ply,p_ply_element element,p_ply_property property,p_ply_argument argument)751 static int ply_read_scalar_property(p_ply ply, p_ply_element element,
752         p_ply_property property, p_ply_argument argument) {
753     p_ply_read_cb read_cb = property->read_cb;
754     p_ply_ihandler *driver = ply->idriver->ihandler;
755     p_ply_ihandler handler = driver[property->type];
756     argument->length = 1;
757     argument->value_index = 0;
758     if (!handler(ply, &argument->value)) {
759         ply_error(ply, "Error reading '%s' of '%s' number %d",
760                 property->name, element->name, argument->instance_index);
761         return 0;
762     }
763     if (read_cb && !read_cb(argument)) {
764         ply_error(ply, "Aborted by user");
765         return 0;
766     }
767     return 1;
768 }
769 
ply_read_property(p_ply ply,p_ply_element element,p_ply_property property,p_ply_argument argument)770 static int ply_read_property(p_ply ply, p_ply_element element,
771         p_ply_property property, p_ply_argument argument) {
772     if (property->type == PLY_LIST)
773         return ply_read_list_property(ply, element, property, argument);
774     else
775         return ply_read_scalar_property(ply, element, property, argument);
776 }
777 
ply_read_element(p_ply ply,p_ply_element element,p_ply_argument argument)778 static int ply_read_element(p_ply ply, p_ply_element element,
779         p_ply_argument argument) {
780     long j, k;
781     /* for each element of this type */
782     for (j = 0; j < element->ninstances; j++) {
783         argument->instance_index = j;
784         /* for each property */
785         for (k = 0; k < element->nproperties; k++) {
786             p_ply_property property = &element->property[k];
787             argument->property = property;
788             argument->pdata = property->pdata;
789             argument->idata = property->idata;
790             if (!ply_read_property(ply, element, property, argument))
791                 return 0;
792         }
793     }
794     return 1;
795 }
796 
ply_find_string(const char * item,const char * const list[])797 static int ply_find_string(const char *item, const char* const list[]) {
798     int i;
799     assert(item && list);
800     for (i = 0; list[i]; i++)
801         if (!strcmp(list[i], item)) return i;
802     return -1;
803 }
804 
ply_find_element(p_ply ply,const char * name)805 static p_ply_element ply_find_element(p_ply ply, const char *name) {
806     p_ply_element element;
807     int i, nelements;
808     assert(ply && name);
809     element = ply->element;
810     nelements = ply->nelements;
811     assert(element || nelements == 0);
812     assert(!element || nelements > 0);
813     for (i = 0; i < nelements; i++)
814         if (!strcmp(element[i].name, name)) return &element[i];
815     return NULL;
816 }
817 
ply_find_property(p_ply_element element,const char * name)818 static p_ply_property ply_find_property(p_ply_element element,
819         const char *name) {
820     p_ply_property property;
821     int i, nproperties;
822     assert(element && name);
823     property = element->property;
824     nproperties = element->nproperties;
825     assert(property || nproperties == 0);
826     assert(!property || nproperties > 0);
827     for (i = 0; i < nproperties; i++)
828         if (!strcmp(property[i].name, name)) return &property[i];
829     return NULL;
830 }
831 
ply_check_word(p_ply ply)832 static int ply_check_word(p_ply ply) {
833     if (strlen(BLINE(ply)) >= WORDSIZE) {
834         ply_error(ply, "Word too long");
835         return 0;
836     }
837     return 1;
838 }
839 
ply_read_word(p_ply ply)840 static int ply_read_word(p_ply ply) {
841     size_t t = 0;
842     assert(ply && ply->fp && ply->io_mode == PLY_READ);
843     /* skip leading blanks */
844     while (1) {
845         t = strspn(BFIRST(ply), " \n\r\t");
846         /* check if all buffer was made of blanks */
847         if (t >= BSIZE(ply)) {
848             if (!BREFILL(ply)) {
849                 ply_error(ply, "Unexpected end of file");
850                 return 0;
851             }
852         } else break;
853     }
854     BSKIP(ply, t);
855     /* look for a space after the current word */
856     t = strcspn(BFIRST(ply), " \n\r\t");
857     /* if we didn't reach the end of the buffer, we are done */
858     if (t < BSIZE(ply)) {
859         ply->buffer_token = ply->buffer_first;
860         BSKIP(ply, t);
861         *BFIRST(ply) = '\0';
862         BSKIP(ply, 1);
863         return ply_check_word(ply);
864     }
865     /* otherwise, try to refill buffer */
866     if (!BREFILL(ply)) {
867         ply_error(ply, "Unexpected end of file");
868         return 0;
869     }
870     /* keep looking from where we left */
871     t += strcspn(BFIRST(ply) + t, " \n\r\t");
872     /* check if the token is too large for our buffer */
873     if (t >= BSIZE(ply)) {
874         ply_error(ply, "Token too large");
875         return 0;
876     }
877     /* we are done */
878     ply->buffer_token = ply->buffer_first;
879     BSKIP(ply, t);
880     *BFIRST(ply) = '\0';
881     BSKIP(ply, 1);
882     return ply_check_word(ply);
883 }
884 
ply_check_line(p_ply ply)885 static int ply_check_line(p_ply ply) {
886     if (strlen(BLINE(ply)) >= LINESIZE) {
887         ply_error(ply, "Line too long");
888         return 0;
889     }
890     return 1;
891 }
892 
ply_read_line(p_ply ply)893 static int ply_read_line(p_ply ply) {
894     const char *end = NULL;
895     assert(ply && ply->fp && ply->io_mode == PLY_READ);
896     /* look for a end of line */
897     end = strchr(BFIRST(ply), '\n');
898     /* if we didn't reach the end of the buffer, we are done */
899     if (end) {
900         ply->buffer_token = ply->buffer_first;
901         BSKIP(ply, end - BFIRST(ply));
902         *BFIRST(ply) = '\0';
903         BSKIP(ply, 1);
904         return ply_check_line(ply);
905     } else {
906         end = ply->buffer + BSIZE(ply);
907         /* otherwise, try to refill buffer */
908         if (!BREFILL(ply)) {
909             ply_error(ply, "Unexpected end of file");
910             return 0;
911         }
912     }
913     /* keep looking from where we left */
914     end = strchr(end, '\n');
915     /* check if the token is too large for our buffer */
916     if (!end) {
917         ply_error(ply, "Token too large");
918         return 0;
919     }
920     /* we are done */
921     ply->buffer_token = ply->buffer_first;
922     BSKIP(ply, end - BFIRST(ply));
923     *BFIRST(ply) = '\0';
924     BSKIP(ply, 1);
925     return ply_check_line(ply);
926 }
927 
ply_read_chunk(p_ply ply,void * anybuffer,size_t size)928 static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size) {
929     char *buffer = (char *) anybuffer;
930     size_t i = 0;
931     assert(ply && ply->fp && ply->io_mode == PLY_READ);
932     assert(ply->buffer_first <= ply->buffer_last);
933     while (i < size) {
934         if (ply->buffer_first < ply->buffer_last) {
935             buffer[i] = ply->buffer[ply->buffer_first];
936             ply->buffer_first++;
937             i++;
938         } else {
939             ply->buffer_first = 0;
940             ply->buffer_last = fread(ply->buffer, 1, BUFFERSIZE, ply->fp);
941             if (ply->buffer_last <= 0) return 0;
942         }
943     }
944     return 1;
945 }
946 
ply_write_chunk(p_ply ply,void * anybuffer,size_t size)947 static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size) {
948     char *buffer = (char *) anybuffer;
949     size_t i = 0;
950     assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
951     assert(ply->buffer_last <= BUFFERSIZE);
952     while (i < size) {
953         if (ply->buffer_last < BUFFERSIZE) {
954             ply->buffer[ply->buffer_last] = buffer[i];
955             ply->buffer_last++;
956             i++;
957         } else {
958             ply->buffer_last = 0;
959             if (fwrite(ply->buffer, 1, BUFFERSIZE, ply->fp) < BUFFERSIZE)
960                 return 0;
961         }
962     }
963     return 1;
964 }
965 
ply_write_chunk_reverse(p_ply ply,void * anybuffer,size_t size)966 static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size) {
967     int ret = 0;
968     ply_reverse(anybuffer, size);
969     ret = ply_write_chunk(ply, anybuffer, size);
970     ply_reverse(anybuffer, size);
971     return ret;
972 }
973 
ply_read_chunk_reverse(p_ply ply,void * anybuffer,size_t size)974 static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size) {
975     if (!ply_read_chunk(ply, anybuffer, size)) return 0;
976     ply_reverse(anybuffer, size);
977     return 1;
978 }
979 
ply_reverse(void * anydata,size_t size)980 static void ply_reverse(void *anydata, size_t size) {
981     char *data = (char *) anydata;
982     char temp;
983     size_t i;
984     for (i = 0; i < size/2; i++) {
985         temp = data[i];
986         data[i] = data[size-i-1];
987         data[size-i-1] = temp;
988     }
989 }
990 
ply_init(p_ply ply)991 static void ply_init(p_ply ply) {
992     ply->c = ' ';
993     ply->element = NULL;
994     ply->nelements = 0;
995     ply->comment = NULL;
996     ply->ncomments = 0;
997     ply->obj_info = NULL;
998     ply->nobj_infos = 0;
999     ply->idriver = NULL;
1000     ply->odriver = NULL;
1001     ply->buffer[0] = '\0';
1002     ply->buffer_first = ply->buffer_last = ply->buffer_token = 0;
1003     ply->welement = 0;
1004     ply->wproperty = 0;
1005     ply->winstance_index = 0;
1006     ply->wlength = 0;
1007     ply->wvalue_index = 0;
1008 }
1009 
ply_element_init(p_ply_element element)1010 static void ply_element_init(p_ply_element element) {
1011     element->name[0] = '\0';
1012     element->ninstances = 0;
1013     element->property = NULL;
1014     element->nproperties = 0;
1015 }
1016 
ply_property_init(p_ply_property property)1017 static void ply_property_init(p_ply_property property) {
1018     property->name[0] = '\0';
1019     property->type = -1;
1020     property->length_type = -1;
1021     property->value_type = -1;
1022     property->read_cb = (p_ply_read_cb) NULL;
1023     property->pdata = NULL;
1024     property->idata = 0;
1025 }
1026 
ply_alloc(void)1027 static p_ply ply_alloc(void) {
1028     p_ply ply = (p_ply) malloc(sizeof(t_ply));
1029     if (!ply) return NULL;
1030     ply_init(ply);
1031     return ply;
1032 }
1033 
ply_grow_array(p_ply ply,void ** pointer,long * nmemb,long size)1034 static void *ply_grow_array(p_ply ply, void **pointer,
1035         long *nmemb, long size) {
1036     void *temp = *pointer;
1037     long count = *nmemb + 1;
1038     if (!temp) temp = malloc(count*size);
1039     else temp = realloc(temp, count*size);
1040     if (!temp) {
1041         ply_error(ply, "Out of memory");
1042         return NULL;
1043     }
1044     *pointer = temp;
1045     *nmemb = count;
1046     return (char *) temp + (count-1) * size;
1047 }
1048 
ply_grow_element(p_ply ply)1049 static p_ply_element ply_grow_element(p_ply ply) {
1050     p_ply_element element = NULL;
1051     assert(ply);
1052     assert(ply->element || ply->nelements == 0);
1053     assert(!ply->element || ply->nelements > 0);
1054     element = (p_ply_element) ply_grow_array(ply, (void **) &ply->element,
1055             &ply->nelements, sizeof(t_ply_element));
1056     if (!element) return NULL;
1057     ply_element_init(element);
1058     return element;
1059 }
1060 
ply_grow_property(p_ply ply,p_ply_element element)1061 static p_ply_property ply_grow_property(p_ply ply, p_ply_element element) {
1062     p_ply_property property = NULL;
1063     assert(ply);
1064     assert(element);
1065     assert(element->property || element->nproperties == 0);
1066     assert(!element->property || element->nproperties > 0);
1067     property = (p_ply_property) ply_grow_array(ply,
1068             (void **) &element->property,
1069             &element->nproperties, sizeof(t_ply_property));
1070     if (!property) return NULL;
1071     ply_property_init(property);
1072     return property;
1073 }
1074 
ply_read_header_format(p_ply ply)1075 static int ply_read_header_format(p_ply ply) {
1076     assert(ply && ply->fp && ply->io_mode == PLY_READ);
1077     if (strcmp(BWORD(ply), "format")) return 0;
1078     if (!ply_read_word(ply)) return 0;
1079     ply->storage_mode = ply_find_string(BWORD(ply), ply_storage_mode_list);
1080     if (ply->storage_mode == (e_ply_storage_mode) (-1)) return 0;
1081     if (ply->storage_mode == PLY_ASCII) ply->idriver = &ply_idriver_ascii;
1082     else if (ply->storage_mode == ply_arch_endian())
1083         ply->idriver = &ply_idriver_binary;
1084     else ply->idriver = &ply_idriver_binary_reverse;
1085     if (!ply_read_word(ply)) return 0;
1086     if (strcmp(BWORD(ply), "1.0")) return 0;
1087     if (!ply_read_word(ply)) return 0;
1088     return 1;
1089 }
1090 
ply_read_header_comment(p_ply ply)1091 static int ply_read_header_comment(p_ply ply) {
1092     assert(ply && ply->fp && ply->io_mode == PLY_READ);
1093     if (strcmp(BWORD(ply), "comment")) return 0;
1094     if (!ply_read_line(ply)) return 0;
1095     if (!ply_add_comment(ply, BLINE(ply))) return 0;
1096     if (!ply_read_word(ply)) return 0;
1097     return 1;
1098 }
1099 
ply_read_header_obj_info(p_ply ply)1100 static int ply_read_header_obj_info(p_ply ply) {
1101     assert(ply && ply->fp && ply->io_mode == PLY_READ);
1102     if (strcmp(BWORD(ply), "obj_info")) return 0;
1103     if (!ply_read_line(ply)) return 0;
1104     if (!ply_add_obj_info(ply, BLINE(ply))) return 0;
1105     if (!ply_read_word(ply)) return 0;
1106     return 1;
1107 }
1108 
ply_read_header_property(p_ply ply)1109 static int ply_read_header_property(p_ply ply) {
1110     p_ply_element element = NULL;
1111     p_ply_property property = NULL;
1112     /* make sure it is a property */
1113     if (strcmp(BWORD(ply), "property")) return 0;
1114     element = &ply->element[ply->nelements-1];
1115     property = ply_grow_property(ply, element);
1116     if (!property) return 0;
1117     /* get property type */
1118     if (!ply_read_word(ply)) return 0;
1119     property->type = ply_find_string(BWORD(ply), ply_type_list);
1120     if (property->type == (e_ply_type) (-1)) return 0;
1121     if (property->type == PLY_LIST) {
1122         /* if it's a list, we need the base types */
1123         if (!ply_read_word(ply)) return 0;
1124         property->length_type = ply_find_string(BWORD(ply), ply_type_list);
1125         if (property->length_type == (e_ply_type) (-1)) return 0;
1126         if (!ply_read_word(ply)) return 0;
1127         property->value_type = ply_find_string(BWORD(ply), ply_type_list);
1128         if (property->value_type == (e_ply_type) (-1)) return 0;
1129     }
1130     /* get property name */
1131     if (!ply_read_word(ply)) return 0;
1132     strcpy(property->name, BWORD(ply));
1133     if (!ply_read_word(ply)) return 0;
1134     return 1;
1135 }
1136 
ply_read_header_element(p_ply ply)1137 static int ply_read_header_element(p_ply ply) {
1138     p_ply_element element = NULL;
1139     long dummy;
1140     assert(ply && ply->fp && ply->io_mode == PLY_READ);
1141     if (strcmp(BWORD(ply), "element")) return 0;
1142     /* allocate room for new element */
1143     element = ply_grow_element(ply);
1144     if (!element) return 0;
1145     /* get element name */
1146     if (!ply_read_word(ply)) return 0;
1147     strcpy(element->name, BWORD(ply));
1148     /* get number of elements of this type */
1149     if (!ply_read_word(ply)) return 0;
1150     if (sscanf(BWORD(ply), "%ld", &dummy) != 1) {
1151         ply_error(ply, "Expected number got '%s'", BWORD(ply));
1152         return 0;
1153     }
1154     element->ninstances = dummy;
1155     /* get all properties for this element */
1156     if (!ply_read_word(ply)) return 0;
1157     while (ply_read_header_property(ply) ||
1158         ply_read_header_comment(ply) || ply_read_header_obj_info(ply))
1159         /* do nothing */;
1160     return 1;
1161 }
1162 
ply_error_cb(const char * message)1163 static void ply_error_cb(const char *message) {
1164     fprintf(stderr, "RPly: %s\n", message);
1165 }
1166 
ply_error(p_ply ply,const char * fmt,...)1167 static void ply_error(p_ply ply, const char *fmt, ...) {
1168     char buffer[1024];
1169     va_list ap;
1170     va_start(ap, fmt);
1171     vsprintf(buffer, fmt, ap);
1172     va_end(ap);
1173     ply->error_cb(buffer);
1174 }
1175 
ply_arch_endian(void)1176 static e_ply_storage_mode ply_arch_endian(void) {
1177     unsigned long i = 1;
1178     unsigned char *s = (unsigned char *) &i;
1179     if (*s == 1) return PLY_LITTLE_ENDIAN;
1180     else return PLY_BIG_ENDIAN;
1181 }
1182 
ply_type_check(void)1183 static int ply_type_check(void) {
1184     assert(sizeof(char) == 1);
1185     assert(sizeof(unsigned char) == 1);
1186     assert(sizeof(short) == 2);
1187     assert(sizeof(unsigned short) == 2);
1188     assert(sizeof(int) == 4);
1189     assert(sizeof(unsigned int) == 4);
1190     assert(sizeof(float) == 4);
1191     assert(sizeof(double) == 8);
1192     if (sizeof(char) != 1) return 0;
1193     if (sizeof(unsigned char) != 1) return 0;
1194     if (sizeof(short) != 2) return 0;
1195     if (sizeof(unsigned short) != 2) return 0;
1196     if (sizeof(int) != 4) return 0;
1197     if (sizeof(unsigned int) != 4) return 0;
1198     if (sizeof(float) != 4) return 0;
1199     if (sizeof(double) != 8) return 0;
1200     return 1;
1201 }
1202 
1203 /* ----------------------------------------------------------------------
1204  * Output handlers
1205  * ---------------------------------------------------------------------- */
oascii_int8(p_ply ply,double value)1206 static int oascii_int8(p_ply ply, double value) {
1207     if (value > CHAR_MAX || value < CHAR_MIN) return 0;
1208     return fprintf(ply->fp, "%d ", (char) value) > 0;
1209 }
1210 
oascii_uint8(p_ply ply,double value)1211 static int oascii_uint8(p_ply ply, double value) {
1212     if (value > UCHAR_MAX || value < 0) return 0;
1213     return fprintf(ply->fp, "%d ", (unsigned char) value) > 0;
1214 }
1215 
oascii_int16(p_ply ply,double value)1216 static int oascii_int16(p_ply ply, double value) {
1217     if (value > SHRT_MAX || value < SHRT_MIN) return 0;
1218     return fprintf(ply->fp, "%d ", (short) value) > 0;
1219 }
1220 
oascii_uint16(p_ply ply,double value)1221 static int oascii_uint16(p_ply ply, double value) {
1222     if (value > USHRT_MAX || value < 0) return 0;
1223     return fprintf(ply->fp, "%d ", (unsigned short) value) > 0;
1224 }
1225 
oascii_int32(p_ply ply,double value)1226 static int oascii_int32(p_ply ply, double value) {
1227     if (value > INT_MAX || value < INT_MIN) return 0;
1228     return fprintf(ply->fp, "%d ", (int) value) > 0;
1229 }
1230 
oascii_uint32(p_ply ply,double value)1231 static int oascii_uint32(p_ply ply, double value) {
1232     if (value > UINT_MAX || value < 0) return 0;
1233     return fprintf(ply->fp, "%d ", (unsigned int) value) > 0;
1234 }
1235 
oascii_float32(p_ply ply,double value)1236 static int oascii_float32(p_ply ply, double value) {
1237     if (value < -FLT_MAX || value > FLT_MAX) return 0;
1238     return fprintf(ply->fp, "%g ", (float) value) > 0;
1239 }
1240 
oascii_float64(p_ply ply,double value)1241 static int oascii_float64(p_ply ply, double value) {
1242     if (value < -DBL_MAX || value > DBL_MAX) return 0;
1243     return fprintf(ply->fp, "%g ", value) > 0;
1244 }
1245 
obinary_int8(p_ply ply,double value)1246 static int obinary_int8(p_ply ply, double value) {
1247     char int8 = (char) value;
1248     if (value > CHAR_MAX || value < CHAR_MIN) return 0;
1249     return ply->odriver->ochunk(ply, &int8, sizeof(int8));
1250 }
1251 
obinary_uint8(p_ply ply,double value)1252 static int obinary_uint8(p_ply ply, double value) {
1253     unsigned char uint8 = (unsigned char) value;
1254     if (value > UCHAR_MAX || value < 0) return 0;
1255     return ply->odriver->ochunk(ply, &uint8, sizeof(uint8));
1256 }
1257 
obinary_int16(p_ply ply,double value)1258 static int obinary_int16(p_ply ply, double value) {
1259     short int16 = (short) value;
1260     if (value > SHRT_MAX || value < SHRT_MIN) return 0;
1261     return ply->odriver->ochunk(ply, &int16, sizeof(int16));
1262 }
1263 
obinary_uint16(p_ply ply,double value)1264 static int obinary_uint16(p_ply ply, double value) {
1265     unsigned short uint16 = (unsigned short) value;
1266     if (value > USHRT_MAX || value < 0) return 0;
1267     return ply->odriver->ochunk(ply, &uint16, sizeof(uint16));
1268 }
1269 
obinary_int32(p_ply ply,double value)1270 static int obinary_int32(p_ply ply, double value) {
1271     int int32 = (int) value;
1272     if (value > INT_MAX || value < INT_MIN) return 0;
1273     return ply->odriver->ochunk(ply, &int32, sizeof(int32));
1274 }
1275 
obinary_uint32(p_ply ply,double value)1276 static int obinary_uint32(p_ply ply, double value) {
1277     unsigned int uint32 = (unsigned int) value;
1278     if (value > UINT_MAX || value < 0) return 0;
1279     return ply->odriver->ochunk(ply, &uint32, sizeof(uint32));
1280 }
1281 
obinary_float32(p_ply ply,double value)1282 static int obinary_float32(p_ply ply, double value) {
1283     float float32 = (float) value;
1284     if (value > FLT_MAX || value < -FLT_MAX) return 0;
1285     return ply->odriver->ochunk(ply, &float32, sizeof(float32));
1286 }
1287 
obinary_float64(p_ply ply,double value)1288 static int obinary_float64(p_ply ply, double value) {
1289     return ply->odriver->ochunk(ply, &value, sizeof(value));
1290 }
1291 
1292 /* ----------------------------------------------------------------------
1293  * Input  handlers
1294  * ---------------------------------------------------------------------- */
iascii_int8(p_ply ply,double * value)1295 static int iascii_int8(p_ply ply, double *value) {
1296     char *end;
1297     if (!ply_read_word(ply)) return 0;
1298     *value = strtol(BWORD(ply), &end, 10);
1299     if (*end || *value > CHAR_MAX || *value < CHAR_MIN) return 0;
1300     return 1;
1301 }
1302 
iascii_uint8(p_ply ply,double * value)1303 static int iascii_uint8(p_ply ply, double *value) {
1304     char *end;
1305     if (!ply_read_word(ply)) return 0;
1306     *value = strtol(BWORD(ply), &end, 10);
1307     if (*end || *value > UCHAR_MAX || *value < 0) return 0;
1308     return 1;
1309 }
1310 
iascii_int16(p_ply ply,double * value)1311 static int iascii_int16(p_ply ply, double *value) {
1312     char *end;
1313     if (!ply_read_word(ply)) return 0;
1314     *value = strtol(BWORD(ply), &end, 10);
1315     if (*end || *value > SHRT_MAX || *value < SHRT_MIN) return 0;
1316     return 1;
1317 }
1318 
iascii_uint16(p_ply ply,double * value)1319 static int iascii_uint16(p_ply ply, double *value) {
1320     char *end;
1321     if (!ply_read_word(ply)) return 0;
1322     *value = strtol(BWORD(ply), &end, 10);
1323     if (*end || *value > USHRT_MAX || *value < 0) return 0;
1324     return 1;
1325 }
1326 
iascii_int32(p_ply ply,double * value)1327 static int iascii_int32(p_ply ply, double *value) {
1328     char *end;
1329     if (!ply_read_word(ply)) return 0;
1330     *value = strtol(BWORD(ply), &end, 10);
1331     if (*end || *value > INT_MAX || *value < INT_MIN) return 0;
1332     return 1;
1333 }
1334 
iascii_uint32(p_ply ply,double * value)1335 static int iascii_uint32(p_ply ply, double *value) {
1336     char *end;
1337     if (!ply_read_word(ply)) return 0;
1338     *value = strtol(BWORD(ply), &end, 10);
1339     if (*end || *value < 0) return 0;
1340     return 1;
1341 }
1342 
iascii_float32(p_ply ply,double * value)1343 static int iascii_float32(p_ply ply, double *value) {
1344     char *end;
1345     if (!ply_read_word(ply)) return 0;
1346     *value = strtod(BWORD(ply), &end);
1347     if (*end || *value < -FLT_MAX || *value > FLT_MAX) return 0;
1348     return 1;
1349 }
1350 
iascii_float64(p_ply ply,double * value)1351 static int iascii_float64(p_ply ply, double *value) {
1352     char *end;
1353     if (!ply_read_word(ply)) return 0;
1354     *value = strtod(BWORD(ply), &end);
1355     if (*end || *value < -DBL_MAX || *value > DBL_MAX) return 0;
1356     return 1;
1357 }
1358 
ibinary_int8(p_ply ply,double * value)1359 static int ibinary_int8(p_ply ply, double *value) {
1360     char int8;
1361     if (!ply->idriver->ichunk(ply, &int8, 1)) return 0;
1362     *value = int8;
1363     return 1;
1364 }
1365 
ibinary_uint8(p_ply ply,double * value)1366 static int ibinary_uint8(p_ply ply, double *value) {
1367     unsigned char uint8;
1368     if (!ply->idriver->ichunk(ply, &uint8, 1)) return 0;
1369     *value = uint8;
1370     return 1;
1371 }
1372 
ibinary_int16(p_ply ply,double * value)1373 static int ibinary_int16(p_ply ply, double *value) {
1374     short int16;
1375     if (!ply->idriver->ichunk(ply, &int16, sizeof(int16))) return 0;
1376     *value = int16;
1377     return 1;
1378 }
1379 
ibinary_uint16(p_ply ply,double * value)1380 static int ibinary_uint16(p_ply ply, double *value) {
1381     unsigned short uint16;
1382     if (!ply->idriver->ichunk(ply, &uint16, sizeof(uint16))) return 0;
1383     *value = uint16;
1384     return 1;
1385 }
1386 
ibinary_int32(p_ply ply,double * value)1387 static int ibinary_int32(p_ply ply, double *value) {
1388     int int32;
1389     if (!ply->idriver->ichunk(ply, &int32, sizeof(int32))) return 0;
1390     *value = int32;
1391     return 1;
1392 }
1393 
ibinary_uint32(p_ply ply,double * value)1394 static int ibinary_uint32(p_ply ply, double *value) {
1395     unsigned int uint32;
1396     if (!ply->idriver->ichunk(ply, &uint32, sizeof(uint32))) return 0;
1397     *value = uint32;
1398     return 1;
1399 }
1400 
ibinary_float32(p_ply ply,double * value)1401 static int ibinary_float32(p_ply ply, double *value) {
1402     float float32;
1403     if (!ply->idriver->ichunk(ply, &float32, sizeof(float32))) return 0;
1404     *value = float32;
1405     ply_reverse(&float32, sizeof(float32));
1406     return 1;
1407 }
1408 
ibinary_float64(p_ply ply,double * value)1409 static int ibinary_float64(p_ply ply, double *value) {
1410     return ply->idriver->ichunk(ply, value, sizeof(double));
1411 }
1412 
1413 /* ----------------------------------------------------------------------
1414  * Constants
1415  * ---------------------------------------------------------------------- */
1416 static t_ply_idriver ply_idriver_ascii = {
1417     {   iascii_int8, iascii_uint8, iascii_int16, iascii_uint16,
1418         iascii_int32, iascii_uint32, iascii_float32, iascii_float64,
1419         iascii_int8, iascii_uint8, iascii_int16, iascii_uint16,
1420         iascii_int32, iascii_uint32, iascii_float32, iascii_float64
1421     }, /* order matches e_ply_type enum */
1422     NULL,
1423     "ascii input"
1424 };
1425 
1426 static t_ply_idriver ply_idriver_binary = {
1427     {   ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
1428         ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64,
1429         ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
1430         ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64
1431     }, /* order matches e_ply_type enum */
1432     ply_read_chunk,
1433     "binary input"
1434 };
1435 
1436 static t_ply_idriver ply_idriver_binary_reverse = {
1437     {   ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
1438         ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64,
1439         ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
1440         ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64
1441     }, /* order matches e_ply_type enum */
1442     ply_read_chunk_reverse,
1443     "reverse binary input"
1444 };
1445 
1446 static t_ply_odriver ply_odriver_ascii = {
1447     {   oascii_int8, oascii_uint8, oascii_int16, oascii_uint16,
1448         oascii_int32, oascii_uint32, oascii_float32, oascii_float64,
1449         oascii_int8, oascii_uint8, oascii_int16, oascii_uint16,
1450         oascii_int32, oascii_uint32, oascii_float32, oascii_float64
1451     }, /* order matches e_ply_type enum */
1452     NULL,
1453     "ascii output"
1454 };
1455 
1456 static t_ply_odriver ply_odriver_binary = {
1457     {   obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
1458         obinary_int32, obinary_uint32, obinary_float32, obinary_float64,
1459         obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
1460         obinary_int32, obinary_uint32, obinary_float32, obinary_float64
1461     }, /* order matches e_ply_type enum */
1462     ply_write_chunk,
1463     "binary output"
1464 };
1465 
1466 static t_ply_odriver ply_odriver_binary_reverse = {
1467     {   obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
1468         obinary_int32, obinary_uint32, obinary_float32, obinary_float64,
1469         obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
1470         obinary_int32, obinary_uint32, obinary_float32, obinary_float64
1471     }, /* order matches e_ply_type enum */
1472     ply_write_chunk_reverse,
1473     "reverse binary output"
1474 };
1475 
1476 /* ----------------------------------------------------------------------
1477  * Copyright (C) 2003 Diego Nehab.  All rights reserved.
1478  *
1479  * Permission is hereby granted, free of charge, to any person obtaining
1480  * a copy of this software and associated documentation files (the
1481  * "Software"), to deal in the Software without restriction, including
1482  * without limitation the rights to use, copy, modify, merge, publish,
1483  * distribute, sublicense, and/or sell copies of the Software, and to
1484  * permit persons to whom the Software is furnished to do so, subject to
1485  * the following conditions:
1486  *
1487  * The above copyright notice and this permission notice shall be
1488  * included in all copies or substantial portions of the Software.
1489  *
1490  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1491  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1492  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1493  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
1494  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
1495  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1496  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1497  * ---------------------------------------------------------------------- */
1498