source: git/src/img.c @ dc1a546

RELEASE/1.0RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereostereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since dc1a546 was 4b156b3, checked in by Olly Betts <olly@…>, 24 years ago

Fixed off-by-one bug in reading code in img.c which was causing the odd
survey tree problems in aven which Andy Atkinson reported.

git-svn-id: file:///home/survex-svn/survex/trunk@1695 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 25.1 KB
Line 
1/* img.c
2 * Routines for reading and writing Survex ".3d" image files
3 * Copyright (C) 1993-2001 Olly Betts
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27#include <time.h>
28#include <ctype.h>
29
30#include "img.h"
31
32#ifdef IMG_HOSTED
33# include "debug.h"
34# include "filelist.h"
35# include "filename.h"
36# include "message.h"
37# include "useful.h"
38# define TIMENA msg(/*Date and time not available.*/108)
39# define TIMEFMT msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107)
40#else
41# define INT32_T long
42# define TIMENA "Time not available."
43# define TIMEFMT "%a,%Y.%m.%d %H:%M:%S %Z"
44# define EXT_SVX_3D "3d"
45# define xosmalloc(L) malloc((L))
46# define xosrealloc(L,S) realloc((L),(S))
47# define osfree(P) free((P))
48# define ossizeof(T) sizeof(T)
49/* in IMG_HOSTED mode, this tests if a filename refers to a directory */
50# define fDirectory(X) 0
51/* open file FNM with mode MODE, maybe using path PTH and/or extension EXT */
52/* path isn't used in img.c, but EXT is */
53# define fopenWithPthAndExt(PTH,FNM,EXT,MODE,X) fopen(FNM,MODE)
54# define fFalse 0
55# define fTrue 1
56# define bool int
57/* dummy do {...} while(0) hack to permit stuff like
58 * if (s) fputsnl(s,fh); else exit(1);
59 * to work as intended
60 */
61# define fputsnl(S, FH) do {fputs((S), (FH)); putc('\n', (FH));} while(0)
62# define ASSERT(X)
63
64static long
65get32(FILE *fh)
66{
67   long w = getc(fh);
68   w |= (long)getc(fh) << 8l;
69   w |= (long)getc(fh) << 16l;
70   w |= (long)getc(fh) << 24l;
71   return w;
72}
73
74static void
75put32(long w, FILE *fh)
76{
77   putc((char)(w), fh);
78   putc((char)(w >> 8l), fh);
79   putc((char)(w >> 16l), fh);
80   putc((char)(w >> 24l), fh);
81}
82
83#endif
84
85#ifdef HAVE_ROUND
86# include <math.h>
87extern double round(double); /* prototype is often missing... */
88# define my_round round
89#else
90static double
91my_round(double x) {
92   if (x >= 0.0) return floor(x + 0.5);
93   return ceil(x - 0.5);
94}
95#endif
96
97unsigned int img_output_version = 3;
98
99#ifdef IMG_HOSTED
100static enum {
101   IMG_NONE = 0,
102   IMG_FILENOTFOUND = /*Couldn't open data file `%s'*/24,
103   IMG_OUTOFMEMORY  = /*Out of memory %.0s*/38,
104   IMG_DIRECTORY    = /*Filename `%s' refers to directory*/44,
105   IMG_CANTOPENOUT  = /*Failed to open output file `%s'*/47,
106   IMG_BADFORMAT    = /*Bad 3d image file `%s'*/106,
107   IMG_READERROR    = /*Error reading from file `%s'*/109,
108   IMG_WRITEERROR   = /*Error writing to file `%s'*/110,
109   IMG_TOONEW       = /*File `%s' has a newer format than this program can understand*/114
110} img_errno = IMG_NONE;
111#else
112static img_errcode img_errno = IMG_NONE;
113#endif
114
115#define FILEID "Survex 3D Image File"
116
117/* Attempt to string paste to ensure we are passed a literal string */
118#define LITLEN(S) (sizeof(S"") - 1)
119
120static char *
121my_strdup(const char *str)
122{
123   char *p;
124   size_t len = strlen(str) + 1;
125   p = xosmalloc(len);
126   if (p) memcpy(p, str, len);
127   return p;
128}
129
130static char *
131getline_alloc(FILE *fh)
132{
133   int ch;
134   size_t i = 0;
135   size_t len = 16;
136   char *buf = xosmalloc(len);
137   if (!buf) return NULL;
138
139   ch = getc(fh);
140   while (ch != '\n' && ch != '\r' && ch != EOF) {
141      buf[i++] = ch;
142      if (i == len - 1) {
143         char *p;
144         len += len;
145         p = xosrealloc(buf, len);
146         if (!p) {
147            osfree(buf);
148            return NULL;
149         }
150         buf = p;
151      }
152      ch = getc(fh);
153   }
154   if (ch == '\n' || ch == '\r') {
155      /* remove any further eol chars (for DOS text files) */
156      do {
157         ch = getc(fh);
158      } while (ch == '\n' || ch == '\r');
159      ungetc(ch, fh); /* we don't want it, so put it back */
160   }
161   buf[i++] = '\0';
162   return buf;
163}
164
165#ifndef IMG_HOSTED
166img_errcode
167img_error(void)
168{
169   return img_errno;
170}
171#else
172int
173img_error(void)
174{
175   return (int)img_errno;
176}
177#endif
178
179img *
180img_open_survey(const char *fnm, const char *survey)
181{
182   img *pimg;
183   size_t len;
184   char buf[LITLEN(FILEID) + 1];
185   int ch;
186
187   if (fDirectory(fnm)) {
188      img_errno = IMG_DIRECTORY;
189      return NULL;
190   }
191
192   pimg = (img *)xosmalloc(ossizeof(img));
193   if (pimg == NULL) {
194      img_errno = IMG_OUTOFMEMORY;
195      return NULL;
196   }
197
198   pimg->buf_len = 257;
199   pimg->label_buf = xosmalloc(pimg->buf_len);
200   if (!pimg->label_buf) {
201      osfree(pimg);
202      img_errno = IMG_OUTOFMEMORY;
203      return NULL;
204   }
205
206   pimg->fh = fopenWithPthAndExt("", fnm, EXT_SVX_3D, "rb", NULL);
207   if (pimg->fh == NULL) {
208      osfree(pimg);
209      img_errno = IMG_FILENOTFOUND;
210      return NULL;
211   }
212
213   pimg->fRead = fTrue; /* reading from this file */
214   img_errno = IMG_NONE;
215
216   pimg->flags = 0;
217
218   /* for version 3 we use label_buf to store the prefix for reuse */
219   pimg->label_len = 0;
220
221   pimg->survey = NULL;
222   pimg->survey_len = 0;
223
224   pimg->title = NULL;
225   if (survey) {
226      len = strlen(survey);
227      if (len) {
228         if (survey[len - 1] == '.') len--;
229         if (len) {
230            char *p;
231            pimg->survey = osmalloc(len + 2);
232            memcpy(pimg->survey, survey, len);
233            /* Set title to leaf survey name */
234            pimg->survey[len] = '\0';
235            p = strchr(pimg->survey, '.');
236            if (p) p++; else p = pimg->survey;
237            pimg->title = my_strdup(p);
238            if (!pimg->title) {
239               img_errno = IMG_OUTOFMEMORY;
240               goto error;
241            }
242            pimg->survey[len] = '.';
243            pimg->survey[len + 1] = '\0';
244         }
245      }
246      pimg->survey_len = len;
247   }
248
249   /* [version -1] already skipped heading line, or there wasn't one
250    * [version 0] not in the middle of a 'LINE' command
251    * [version 3] not in the middle of turning a LINE into a MOVE
252    */
253   pimg->pending = 0;
254
255   len = strlen(fnm);
256   if (len > LITLEN(EXT_SVX_POS) + 1 &&
257       fnm[len - LITLEN(EXT_SVX_POS) - 1] == FNM_SEP_EXT &&
258       strcmp(fnm + len - LITLEN(EXT_SVX_POS), EXT_SVX_POS) == 0) {
259      pimg->version = -1;
260      if (!survey) pimg->title = baseleaf_from_fnm(fnm);
261      pimg->datestamp = my_strdup(TIMENA);
262      if (!pimg->datestamp) {
263         img_errno = IMG_OUTOFMEMORY;
264         goto error;
265      }
266      pimg->start = 0;
267      return pimg;
268   }
269
270   if (fread(buf, LITLEN(FILEID) + 1, 1, pimg->fh) != 1 ||
271       memcmp(buf, FILEID"\n", LITLEN(FILEID)) != 0) {
272      img_errno = IMG_BADFORMAT;
273      goto error;
274   }
275
276   /* check file format version */
277   ch = getc(pimg->fh);
278   pimg->version = 0;
279   if (tolower(ch) == 'b') {
280      /* binary file iff B/b prefix */
281      pimg->version = 1;
282      ch = getc(pimg->fh);
283   }
284   if (ch != 'v') {
285      img_errno = IMG_BADFORMAT;
286      goto error;
287   }
288   ch = getc(pimg->fh);
289   if (ch == '0') {
290      if (fread(buf, 4, 1, pimg->fh) != 1 || memcmp(buf, ".01\n", 4) != 0) {
291         img_errno = IMG_BADFORMAT;
292         goto error;
293      }
294      /* nothing special to do */
295   } else if (pimg->version == 0) {
296      if (ch < '2' || ch > '3' || getc(pimg->fh) != '\n') {
297         img_errno = IMG_TOONEW;
298         goto error;
299      }
300      pimg->version = ch - '0';
301   } else {
302      img_errno = IMG_BADFORMAT;
303      goto error;
304   }
305
306   if (!pimg->title) pimg->title = getline_alloc(pimg->fh);
307   pimg->datestamp = getline_alloc(pimg->fh);
308   if (!pimg->title || !pimg->datestamp) {
309      img_errno = IMG_OUTOFMEMORY;
310      error:
311      osfree(pimg->title);
312      fclose(pimg->fh);
313      osfree(pimg);
314      return NULL;
315   }
316
317   pimg->start = ftell(pimg->fh);
318
319   return pimg;
320}
321
322void
323img_rewind(img *pimg)
324{
325   fseek(pimg->fh, pimg->start, SEEK_SET);
326   clearerr(pimg->fh);
327   /* [version -1] already skipped heading line, or there wasn't one
328    * [version 0] not in the middle of a 'LINE' command
329    * [version 3] not in the middle of turning a LINE into a MOVE */
330   pimg->pending = 0;
331
332   img_errno = IMG_NONE;
333
334   /* for version 3 we use label_buf to store the prefix for reuse */
335   pimg->label_len = 0;
336}
337
338img *
339img_open_write(const char *fnm, char *title_buf, bool fBinary)
340{
341   time_t tm;
342   img *pimg;
343
344   fBinary = fBinary;
345
346   if (fDirectory(fnm)) {
347      img_errno = IMG_DIRECTORY;
348      return NULL;
349   }
350
351   pimg = (img *)xosmalloc(ossizeof(img));
352   if (pimg == NULL) {
353      img_errno = IMG_OUTOFMEMORY;
354      return NULL;
355   }
356
357   pimg->buf_len = 257;
358   pimg->label_buf = xosmalloc(pimg->buf_len);
359   if (!pimg->label_buf) {
360      osfree(pimg);
361      img_errno = IMG_OUTOFMEMORY;
362      return NULL;
363   }
364
365   pimg->fh = fopen(fnm, "wb");
366   if (!pimg->fh) {
367      osfree(pimg);
368      img_errno = IMG_CANTOPENOUT;
369      return NULL;
370   }
371
372   /* Output image file header */
373   fputs("Survex 3D Image File\n", pimg->fh); /* file identifier string */
374   if (img_output_version < 2) {
375      pimg->version = 1;
376      fputs("Bv0.01\n", pimg->fh); /* binary file format version number */
377   } else {
378      pimg->version = (img_output_version > 2) ? 3 : 2;
379      fprintf(pimg->fh, "v%d\n", pimg->version); /* file format version no. */
380   }
381   fputsnl(title_buf, pimg->fh);
382   tm = time(NULL);
383   if (tm == (time_t)-1) {
384      fputsnl(TIMENA, pimg->fh);
385   } else {
386      char date[256];
387      /* output current date and time in format specified */
388      strftime(date, 256, TIMEFMT, localtime(&tm));
389      fputsnl(date, pimg->fh);
390   }
391   pimg->fRead = fFalse; /* writing to this file */
392   img_errno = IMG_NONE;
393
394   /* for version 3 we use label_buf to store the prefix for reuse */
395   pimg->label_buf[0] = '\0';
396   pimg->label_len = 0;
397
398   /* Don't check for write errors now - let img_close() report them... */
399   return pimg;
400}
401
402static int
403read_coord(FILE *fh, img_point *pt)
404{
405   ASSERT(fh);
406   ASSERT(pt);
407   pt->x = get32(fh) / 100.0;
408   pt->y = get32(fh) / 100.0;
409   pt->z = get32(fh) / 100.0;
410   if (ferror(fh) || feof(fh)) {
411      img_errno = feof(fh) ? IMG_BADFORMAT : IMG_READERROR;
412      return 0;
413   }
414   return 1;
415}
416
417int
418img_read_item(img *pimg, img_point *p)
419{
420   int result;
421   pimg->flags = 0;
422   pimg->label = pimg->label_buf;
423
424   if (pimg->version == 3) {
425      int opt;
426      if (pimg->pending >= 0x80) {
427         *p = pimg->mv;
428         pimg->flags = (int)(pimg->pending) & 0x3f;
429         pimg->pending = 0;
430         return img_LINE;
431      }
432      again3: /* label to goto if we get a prefix */
433      opt = getc(pimg->fh);
434      if (opt == EOF) {
435         img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
436         return img_BAD;
437      }
438      switch (opt >> 6) {
439       case 0:
440         if (opt == 0) {
441            if (!pimg->label_len) return img_STOP; /* end of data marker */
442            pimg->label_len = 0;
443            goto again3;
444         }
445         if (opt < 15) {
446            /* 1-14 mean trim that many levels from current prefix */
447            int c;
448            /* zero prefix using "0" */
449            if (pimg->label_len <= 16) {
450               img_errno = IMG_BADFORMAT;
451               return img_BAD;
452            }
453            c = pimg->label_len - 16 - 1;
454            opt &= 0x07;
455            while (pimg->label_buf[c] != '.' || --opt > 0) {
456               if (--c < 0) {
457                  /* zero prefix using "0" */
458                  img_errno = IMG_BADFORMAT;
459                  return img_BAD;
460               }
461            }
462            c++;
463            pimg->label_len = c;
464            goto again3;
465         }
466         if (opt == 15) {
467            result = img_MOVE;
468            break;
469         }
470         /* 16-31 mean remove (n - 15) characters from the prefix */
471         /* zero prefix using 0 */
472         if (pimg->label_len <= opt - 15) {
473            img_errno = IMG_BADFORMAT;
474            return img_BAD;
475         }
476         pimg->label_len -= (opt - 15);
477         goto again3;
478       case 1: case 2: {
479         char *q;
480         long len = getc(pimg->fh);
481         if (len == EOF) {
482            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
483            return img_BAD;
484         }
485         if (len == 0xfe) {
486            len += get16(pimg->fh);
487            if (feof(pimg->fh)) {
488               img_errno = IMG_BADFORMAT;
489               return img_BAD;
490            }
491            if (ferror(pimg->fh)) {
492               img_errno = IMG_READERROR;
493               return img_BAD;
494            }
495         } else if (len == 0xff) {
496            len = get32(pimg->fh);
497            if (ferror(pimg->fh)) {
498               img_errno = IMG_READERROR;
499               return img_BAD;
500            }
501            if (feof(pimg->fh) || len < 0xfe + 0xffff) {
502               img_errno = IMG_BADFORMAT;
503               return img_BAD;
504            }
505         }
506
507         q = pimg->label_buf + pimg->label_len;
508         pimg->label_len += len;
509         if (pimg->label_len >= pimg->buf_len) {
510            char *b = xosrealloc(pimg->label_buf, pimg->label_len + 1);
511            if (!b) {
512               img_errno = IMG_OUTOFMEMORY;
513               return img_BAD;
514            }
515            q = (q - pimg->label_buf) + b;
516            pimg->label = pimg->label_buf = b;
517            pimg->buf_len = pimg->label_len + 1;
518         }
519         if (len && fread(q, len, 1, pimg->fh) != 1) {
520            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
521            return img_BAD;
522         }
523         q[len] = '\0';
524
525         result = opt & 0x40 ? img_LABEL : img_LINE;
526
527         if (pimg->survey_len) {
528            size_t l = pimg->survey_len;
529            const char *s = pimg->label_buf;
530            if (result == img_LINE) {
531               if (strncmp(pimg->survey, s, l) != 0 ||
532                   !(s[l] == '.' || s[l] == '\0')) {
533                  if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
534                  pimg->pending = 15;
535                  goto again3;
536               }
537            } else {
538               if (strncmp(pimg->survey, s, l + 1) != 0) {
539                  fseek(pimg->fh, 12, SEEK_CUR);
540                  goto again3;
541               }
542            }
543            pimg->label += l;
544            /* skip the dot if there */
545            if (*pimg->label) pimg->label++;
546         }
547
548         if (result == img_LINE && pimg->pending) {
549            *p = pimg->mv;
550            if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
551            pimg->pending = opt;
552            return img_MOVE;
553         }
554         pimg->flags = (int)opt & 0x3f;
555         break;
556       }
557       default:
558         img_errno = IMG_BADFORMAT;
559         return img_BAD;
560      }
561      if (!read_coord(pimg->fh, p)) return img_BAD;
562      pimg->pending = 0;
563      return result;
564   } else if (pimg->version > 0) {
565      static long opt_lookahead = 0;
566      static img_point pt = { 0.0, 0.0, 0.0 };
567      long opt;
568      again: /* label to goto if we get a cross */
569      pimg->label[0] = '\0';
570
571      if (pimg->version == 1) {
572         if (opt_lookahead) {
573            opt = opt_lookahead;
574            opt_lookahead = 0;
575         } else {
576            opt = get32(pimg->fh);
577         }
578      } else {
579         opt = getc(pimg->fh);
580      }
581
582      if (feof(pimg->fh)) {
583         img_errno = IMG_BADFORMAT;
584         return img_BAD;
585      }
586      if (ferror(pimg->fh)) {
587         img_errno = IMG_READERROR;
588         return img_BAD;
589      }
590
591      switch (opt) {
592       case -1: case 0:
593         return img_STOP; /* end of data marker */
594       case 1:
595         /* skip coordinates */
596         if (fseek(pimg->fh, 12, SEEK_CUR) == -1) {
597            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
598            return img_BAD;
599         }
600         goto again;
601       case 2: case 3: {
602         char *q;
603         int ch;
604         result = img_LABEL;
605         ch = getc(pimg->fh);
606         if (ch == EOF) {
607            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
608            return img_BAD;
609         }
610         if (ch != '\\') ungetc(ch, pimg->fh);
611         fgets(pimg->label_buf, 257, pimg->fh);
612         if (feof(pimg->fh)) {
613            img_errno = IMG_BADFORMAT;
614            return img_BAD;
615         }
616         if (ferror(pimg->fh)) {
617            img_errno = IMG_READERROR;
618            return img_BAD;
619         }
620         q = pimg->label_buf + strlen(pimg->label_buf) - 1;
621         if (*q != '\n') {
622            img_errno = IMG_BADFORMAT;
623            return img_BAD;
624         }
625         /* Ignore empty labels in some .3d files (caused by a bug) */
626         if (q == pimg->label_buf) goto again;
627         *q = '\0';
628         pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
629         if (opt == 2) goto done;
630         break;
631       }
632       case 6: case 7: {
633         long len;
634         result = img_LABEL;
635
636         if (opt == 7)
637            pimg->flags = getc(pimg->fh);
638         else
639            pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
640
641         len = get32(pimg->fh);
642
643         if (feof(pimg->fh)) {
644            img_errno = IMG_BADFORMAT;
645            return img_BAD;
646         }
647         if (ferror(pimg->fh)) {
648            img_errno = IMG_READERROR;
649            return img_BAD;
650         }
651
652         /* Ignore empty labels in some .3d files (caused by a bug) */
653         if (len == 0) goto again;
654         if (len >= (long)pimg->buf_len) {
655            char *b = xosrealloc(pimg->label_buf, len + 1);
656            if (!b) {
657               img_errno = IMG_OUTOFMEMORY;
658               return img_BAD;
659            }
660            pimg->label = pimg->label_buf = b;
661            pimg->buf_len = len + 1;
662         }
663         if (fread(pimg->label_buf, len, 1, pimg->fh) != 1) {
664            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
665            return img_BAD;
666         }
667         break;
668       }
669       case 4:
670         result = img_MOVE;
671         break;
672       case 5:
673         result = img_LINE;
674         break;
675       default:
676         switch ((int)opt & 0xc0) {
677          case 0x80:
678            pimg->flags = (int)opt & 0x3f;
679            result = img_LINE;
680            break;
681          case 0x40: {
682            char *q;
683            pimg->flags = (int)opt & 0x3f;
684            result = img_LABEL;
685            if (!fgets(pimg->label_buf, 257, pimg->fh)) {
686               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
687               return img_BAD;
688            }
689            q = pimg->label_buf + strlen(pimg->label_buf) - 1;
690            /* Ignore empty-labels in some .3d files (caused by a bug) */
691            if (q == pimg->label_buf) goto again;
692            if (*q != '\n') {
693               img_errno = IMG_BADFORMAT;
694               return img_BAD;
695            }
696            *q = '\0';
697            break;
698          }
699          case 0xc0:
700            /* use this for an extra leg or station flag if we need it */
701          default:
702            img_errno = IMG_BADFORMAT;
703            return img_BAD;
704         }
705         break;
706      }
707
708      if (!read_coord(pimg->fh, &pt)) return img_BAD;
709
710      if (result == img_LABEL && pimg->survey_len) {
711         if (strncmp(pimg->label_buf, pimg->survey, pimg->survey_len + 1) != 0)
712            goto again;
713         pimg->label += pimg->survey_len + 1;
714      }
715
716      done:
717      *p = pt;
718
719      if (result == img_MOVE && pimg->version == 1) {
720         /* peek at next code and see if it's an old-style label */
721         opt_lookahead = get32(pimg->fh);
722
723         if (feof(pimg->fh)) {
724            img_errno = IMG_BADFORMAT;
725            return img_BAD;
726         }
727         if (ferror(pimg->fh)) {
728            img_errno = IMG_READERROR;
729            return img_BAD;
730         }
731
732         if (opt_lookahead == 2) return img_read_item(pimg, p);
733      }
734
735      return result;
736   } else if (pimg->version == 0) {
737      ascii_again:
738      pimg->label[0] = '\0';
739      if (feof(pimg->fh)) return img_STOP;
740      if (pimg->pending) {
741         pimg->pending = 0;
742         result = img_LINE;
743      } else {
744         char cmd[7];
745         /* Stop if nothing found */
746         if (fscanf(pimg->fh, "%6s", cmd) < 1) return img_STOP;
747         if (strcmp(cmd, "move") == 0)
748            result = img_MOVE;
749         else if (strcmp(cmd, "draw") == 0)
750            result = img_LINE;
751         else if (strcmp(cmd, "line") == 0) {
752            /* set flag to indicate to process second triplet as LINE */
753            pimg->pending = 1;
754            result = img_MOVE;
755         } else if (strcmp(cmd, "cross") == 0) {
756            if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
757               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
758               return img_BAD;
759            }
760            goto ascii_again;
761         } else if (strcmp(cmd, "name") == 0) {
762            size_t off = 0;
763            int ch = getc(pimg->fh);
764            if (ch == ' ') ch = getc(pimg->fh);
765            while (ch != ' ') {
766               if (ch == '\n' || ch == EOF) {
767                  img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
768                  return img_BAD;
769               }
770               if (off == pimg->buf_len) {
771                  char *b;
772                  b = xosrealloc(pimg->label_buf, pimg->buf_len * 2);
773                  if (!b) {
774                     img_errno = IMG_OUTOFMEMORY;
775                     return img_BAD;
776                  }
777                  pimg->label_buf = b;
778                  pimg->buf_len *= 2;
779               }
780               pimg->label_buf[off++] = ch;
781               ch = getc(pimg->fh);
782            }
783            pimg->label_buf[off] = '\0';
784
785            pimg->label = pimg->label_buf;
786            if (pimg->label[0] == '\\') pimg->label++;
787
788            result = img_LABEL;
789         } else {
790            img_errno = IMG_BADFORMAT;
791            return img_BAD; /* unknown keyword */
792         }
793      }
794
795      if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
796         img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
797         return img_BAD;
798      }
799
800      if (result == img_LABEL && pimg->survey_len) {
801         if (strncmp(pimg->label, pimg->survey, pimg->survey_len + 1) != 0)
802            goto ascii_again;
803         pimg->label += pimg->survey_len + 1;
804      }
805
806      return result;
807   } else {
808      /* version -1: .pos file */
809      size_t off;
810      pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
811      againpos:
812      off = 0;
813      while (fscanf(pimg->fh, "(%lf,%lf,%lf ) ", &p->x, &p->y, &p->z) != 3) {
814         int ch;
815         if (ferror(pimg->fh)) {
816            img_errno = IMG_READERROR;
817            return img_BAD;
818         }
819         if (feof(pimg->fh)) return img_STOP;
820         if (pimg->pending) {
821            img_errno = IMG_BADFORMAT;
822            return img_BAD;
823         }
824         pimg->pending = 1;
825         /* ignore rest of line */
826         do {
827            ch = getc(pimg->fh);
828         } while (ch != '\n' && ch != '\r' && ch != EOF);
829      }
830
831      pimg->label_buf[0] = '\0';
832      while (!feof(pimg->fh)) {
833         char *b;
834         if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
835            img_errno = IMG_READERROR;
836            return img_BAD;
837         }
838
839         off += strlen(pimg->label_buf + off);
840         if (off && pimg->label_buf[off - 1] == '\n') {
841            pimg->label_buf[off - 1] = '\0';
842            break;
843         }
844         b = xosrealloc(pimg->label_buf, pimg->buf_len * 2);
845         if (!b) {
846            img_errno = IMG_OUTOFMEMORY;
847            return img_BAD;
848         }
849         pimg->label_buf = b;
850         pimg->buf_len *= 2;
851      }
852
853      pimg->label = pimg->label_buf;
854
855      if (pimg->label[0] == '\\') pimg->label++;
856
857      if (pimg->survey_len) {
858         size_t l = pimg->survey_len + 1;
859         if (strncmp(pimg->survey, pimg->label, l) != 0) goto againpos;
860         pimg->label += l;
861      }
862
863      return img_LABEL;
864   }
865}
866
867static int
868write_v3label(img *pimg, int opt, const char *s)
869{
870   size_t len, n, dot;
871
872   /* find length of common prefix */
873   dot = 0;
874   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
875      if (s[len] == '.') dot = len + 1;
876   }
877
878   ASSERT(len <= pimg->label_len);
879   n = pimg->label_len - len;
880   if (len == 0) {
881      if (pimg->label_len) putc(0, pimg->fh);
882   } else if (n <= 16) {
883      if (n) putc(n + 15, pimg->fh);
884   } else if (dot == 0) {
885      if (pimg->label_len) putc(0, pimg->fh);
886      len = 0;
887   } else {
888      const char *p = pimg->label_buf + dot;
889      n = 1;
890      for (len = pimg->label_len - dot - 17; len; len--) {
891         if (*p++ == '.') n++;
892      }
893      if (n <= 14) {
894         putc(n, pimg->fh);
895         len = dot;
896      } else {
897         if (pimg->label_len) putc(0, pimg->fh);
898         len = 0;
899      }
900   }
901
902   n = strlen(s + len);
903   putc(opt, pimg->fh);
904   if (n < 0xfe) {
905      putc(n, pimg->fh);
906   } else if (n < 0xffff + 0xfe) {
907      putc(0xfe, pimg->fh);
908      put16(n - 0xfe, pimg->fh);
909   } else {
910      putc(0xff, pimg->fh);
911      put32(n, pimg->fh);
912   }
913   fwrite(s + len, n, 1, pimg->fh);
914
915   n += len;
916   pimg->label_len = n;
917   if (n >= pimg->buf_len) {
918      char *p = xosrealloc(pimg->label_buf, n + 1);
919      if (!p) return 0; /* FIXME: distinguish out of memory... */
920      pimg->label_buf = p;
921      pimg->buf_len = n + 1;
922   }
923   memcpy(pimg->label_buf + len, s + len, n - len + 1);
924
925   return !ferror(pimg->fh);
926}
927
928void
929img_write_item(img *pimg, int code, int flags, const char *s,
930               double x, double y, double z)
931{
932   if (!pimg) return;
933   if (pimg->version == 3) {
934      int opt = 0;
935      switch (code) {
936       case img_LABEL:
937         write_v3label(pimg, 0x40 | flags, s);
938         opt = 0;
939         break;
940       case img_MOVE:
941         opt = 15;
942         break;
943       case img_LINE:
944         write_v3label(pimg, 0x80 | flags, s ? s : "");
945         opt = 0;
946         break;
947       default: /* ignore for now */
948         return;
949      }
950      if (opt) putc(opt, pimg->fh);
951      /* Output in cm */
952      put32((INT32_T)my_round(x * 100.0), pimg->fh);
953      put32((INT32_T)my_round(y * 100.0), pimg->fh);
954      put32((INT32_T)my_round(z * 100.0), pimg->fh);
955   } else {
956      size_t len;
957      INT32_T opt = 0;
958      ASSERT(pimg->version > 0);
959      switch (code) {
960       case img_LABEL:
961         if (pimg->version == 1) {
962            /* put a move before each label */
963            img_write_item(pimg, img_MOVE, 0, NULL, x, y, z);
964            put32(2, pimg->fh);
965            fputsnl(s, pimg->fh);
966            return;
967         }
968         len = strlen(s);
969         if (len > 255 || strchr(s, '\n')) {
970            /* long label - not in early incarnations of v2 format, but few
971             * 3d files will need these, so better not to force incompatibility
972             * with a new version I think... */
973            putc(7, pimg->fh);
974            putc(flags, pimg->fh);
975            put32(len, pimg->fh);
976            fputs(s, pimg->fh);
977         } else {
978            putc(0x40 | (flags & 0x3f), pimg->fh);
979            fputsnl(s, pimg->fh);
980         }
981         opt = 0;
982         break;
983       case img_MOVE:
984         opt = 4;
985         break;
986       case img_LINE:
987         if (pimg->version > 1) {
988            opt = 0x80 | (flags & 0x3f);
989            break;
990         }
991         opt = 5;
992         break;
993       default: /* ignore for now */
994         return;
995      }
996      if (pimg->version == 1) {
997         put32(opt, pimg->fh);
998      } else {
999         if (opt) putc(opt, pimg->fh);
1000      }
1001      /* Output in cm */
1002      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1003      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1004      put32((INT32_T)my_round(z * 100.0), pimg->fh);
1005   }
1006}
1007
1008int
1009img_close(img *pimg)
1010{
1011   int result = 1;
1012   if (pimg) {
1013      if (pimg->fh) {
1014         if (pimg->fRead) {
1015            osfree(pimg->survey);
1016            osfree(pimg->title);
1017            osfree(pimg->datestamp);
1018         } else {
1019            /* write end of data marker */
1020            switch (pimg->version) {
1021             case 1:
1022               put32((INT32_T)-1, pimg->fh);
1023               break;
1024             case 2:
1025               putc(0, pimg->fh);
1026               break;
1027             case 3:
1028               if (pimg->label_len) putc(0, pimg->fh);
1029               putc(0, pimg->fh);
1030               break;
1031            }
1032         }
1033         if (ferror(pimg->fh)) result = 0;
1034         if (fclose(pimg->fh)) result = 0;
1035         img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
1036      }
1037      osfree(pimg->label_buf);
1038      osfree(pimg);
1039   }
1040   return result;
1041}
Note: See TracBrowser for help on using the repository browser.