source: git/src/img.c @ 0d922fc

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 0d922fc was 0d922fc, checked in by Olly Betts <olly@…>, 23 years ago

A few tweaks to get .plt file reading fully working

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

  • Property mode set to 100644
File size: 30.5 KB
Line 
1/* img.c
2 * Routines for reading and writing Survex ".3d" image files
3 * Copyright (C) 1993-2002 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
97/* portable case insensitive string compare */
98#if defined(strcasecmp) || defined(HAVE_STRCASECMP)
99# define my_strcasecmp strcasecmp
100#else
101/* What about top bit set chars? */
102int my_strcasecmp(const char *s1, const char *s2) {
103   register c1, c2;
104   do {
105      c1 = *s1++;
106      c2 = *s2++;
107   } while (c1 && toupper(c1) == toupper(c2));
108   /* now calculate real difference */
109   return c1 - c2;
110}
111#endif
112
113unsigned int img_output_version = 3;
114
115#ifdef IMG_HOSTED
116static enum {
117   IMG_NONE = 0,
118   IMG_FILENOTFOUND = /*Couldn't open data file `%s'*/24,
119   IMG_OUTOFMEMORY  = /*Out of memory %.0s*/38,
120   IMG_DIRECTORY    = /*Filename `%s' refers to directory*/44,
121   IMG_CANTOPENOUT  = /*Failed to open output file `%s'*/47,
122   IMG_BADFORMAT    = /*Bad 3d image file `%s'*/106,
123   IMG_READERROR    = /*Error reading from file `%s'*/109,
124   IMG_WRITEERROR   = /*Error writing to file `%s'*/110,
125   IMG_TOONEW       = /*File `%s' has a newer format than this program can understand*/114
126} img_errno = IMG_NONE;
127#else
128static img_errcode img_errno = IMG_NONE;
129#endif
130
131#define FILEID "Survex 3D Image File"
132
133#define EXT_PLT "plt"
134
135/* Attempt to string paste to ensure we are passed a literal string */
136#define LITLEN(S) (sizeof(S"") - 1)
137
138static char *
139my_strdup(const char *str)
140{
141   char *p;
142   size_t len = strlen(str) + 1;
143   p = xosmalloc(len);
144   if (p) memcpy(p, str, len);
145   return p;
146}
147
148static char *
149getline_alloc(FILE *fh)
150{
151   int ch;
152   size_t i = 0;
153   size_t len = 16;
154   char *buf = xosmalloc(len);
155   if (!buf) return NULL;
156
157   ch = getc(fh);
158   while (ch != '\n' && ch != '\r' && ch != EOF) {
159      buf[i++] = ch;
160      if (i == len - 1) {
161         char *p;
162         len += len;
163         p = xosrealloc(buf, len);
164         if (!p) {
165            osfree(buf);
166            return NULL;
167         }
168         buf = p;
169      }
170      ch = getc(fh);
171   }
172   if (ch == '\n' || ch == '\r') {
173      int otherone = ch ^ ('\n' ^ '\r');
174      ch = getc(fh);
175      /* if it's not the other eol character, put it back */
176      if (ch != otherone) ungetc(ch, fh);
177   }
178   buf[i++] = '\0';
179   return buf;
180}
181
182#ifndef IMG_HOSTED
183img_errcode
184img_error(void)
185{
186   return img_errno;
187}
188#else
189int
190img_error(void)
191{
192   return (int)img_errno;
193}
194#endif
195
196static bool
197check_label_space(img *pimg, size_t len)
198{
199   if (len > pimg->buf_len) {
200      char *b = xosrealloc(pimg->label_buf, len);
201      if (!b) return fFalse;
202      pimg->label = (pimg->label - pimg->label_buf) + b;
203      pimg->label_buf = b;
204      pimg->buf_len = len;
205   }
206   return fTrue;
207}
208
209img *
210img_open_survey(const char *fnm, const char *survey)
211{
212   img *pimg;
213   size_t len;
214   char buf[LITLEN(FILEID) + 1];
215   int ch;
216
217   if (fDirectory(fnm)) {
218      img_errno = IMG_DIRECTORY;
219      return NULL;
220   }
221
222   pimg = (img *)xosmalloc(ossizeof(img));
223   if (pimg == NULL) {
224      img_errno = IMG_OUTOFMEMORY;
225      return NULL;
226   }
227
228   pimg->buf_len = 257;
229   pimg->label_buf = xosmalloc(pimg->buf_len);
230   if (!pimg->label_buf) {
231      osfree(pimg);
232      img_errno = IMG_OUTOFMEMORY;
233      return NULL;
234   }
235
236   pimg->fh = fopenWithPthAndExt("", fnm, EXT_SVX_3D, "rb", NULL);
237   if (pimg->fh == NULL) {
238      osfree(pimg);
239      img_errno = IMG_FILENOTFOUND;
240      return NULL;
241   }
242
243   pimg->fRead = fTrue; /* reading from this file */
244   img_errno = IMG_NONE;
245
246   pimg->flags = 0;
247
248   /* for version 3 we use label_buf to store the prefix for reuse */
249   /* for version -2, 0 value indicates we haven't entered a survey yet */
250   pimg->label_len = 0;
251
252   pimg->survey = NULL;
253   pimg->survey_len = 0;
254
255   pimg->title = NULL;
256   if (survey) {
257      len = strlen(survey);
258      if (len) {
259         if (survey[len - 1] == '.') len--;
260         if (len) {
261            char *p;
262            pimg->survey = osmalloc(len + 2);
263            memcpy(pimg->survey, survey, len);
264            /* Set title to leaf survey name */
265            pimg->survey[len] = '\0';
266            p = strchr(pimg->survey, '.');
267            if (p) p++; else p = pimg->survey;
268            pimg->title = my_strdup(p);
269            if (!pimg->title) {
270               img_errno = IMG_OUTOFMEMORY;
271               goto error;
272            }
273            pimg->survey[len] = '.';
274            pimg->survey[len + 1] = '\0';
275         }
276      }
277      pimg->survey_len = len;
278   }
279
280   /* [version -2] pending IMG_LINE ('D') or IMG_MOVE ('M')
281    * [version -1] already skipped heading line, or there wasn't one
282    * [version 0] not in the middle of a 'LINE' command
283    * [version 3] not in the middle of turning a LINE into a MOVE
284    */
285   pimg->pending = 0;
286
287   len = strlen(fnm);
288   if (len > LITLEN(EXT_SVX_POS) + 1 &&
289       fnm[len - LITLEN(EXT_SVX_POS) - 1] == FNM_SEP_EXT &&
290       my_strcasecmp(fnm + len - LITLEN(EXT_SVX_POS), EXT_SVX_POS) == 0) {
291      pimg->version = -1;
292      if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
293      pimg->datestamp = my_strdup(TIMENA);
294      if (!pimg->datestamp) {
295         img_errno = IMG_OUTOFMEMORY;
296         goto error;
297      }
298      pimg->start = 0;
299      return pimg;
300   }
301
302   if (len > LITLEN(EXT_PLT) + 1 &&
303       fnm[len - LITLEN(EXT_PLT) - 1] == FNM_SEP_EXT &&
304       my_strcasecmp(fnm + len - LITLEN(EXT_PLT), EXT_PLT) == 0) {
305      long fpos;
306
307      pimg->version = -2;
308      pimg->start = 0;
309      if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
310      pimg->datestamp = my_strdup(TIMENA);
311      if (!pimg->datestamp) {
312         img_errno = IMG_OUTOFMEMORY;
313         goto error;
314      }
315      while (1) {
316         int ch = getc(pimg->fh);
317         switch (ch) {
318          case '\x1a':
319            fseek(pimg->fh, -1, SEEK_CUR);
320            /* FALL THRU */
321          case EOF:
322            pimg->start = ftell(pimg->fh);
323            return pimg;
324          case 'N': {
325            char *line, *q;
326            fpos = ftell(pimg->fh) - 1;
327            if (!pimg->survey) {
328               /* FIXME : if there's only one survey in the file, it'd be nice
329                * to use its description as the title here...
330                */
331               ungetc('N', pimg->fh);
332               pimg->start = fpos;
333               return pimg;
334            }
335            line = getline_alloc(pimg->fh);
336            len = 0;
337            while (line[len] > 32) ++len;
338            if (pimg->survey_len != len ||
339                memcmp(line, pimg->survey, len) != 0) {
340               osfree(line);
341               continue;
342            }
343            q = strchr(line + len, 'C');
344            if (q && q[1]) {
345                osfree(pimg->title);
346                pimg->title = osstrdup(q + 1);
347            } else if (!pimg->title) {
348                pimg->title = osstrdup(pimg->label);
349            }
350            osfree(line);
351            if (!pimg->title) {
352                img_errno = IMG_OUTOFMEMORY;
353                goto error;
354            }
355            if (!pimg->start) pimg->start = fpos;
356            return pimg;
357          }
358          case 'M': case 'D':
359            pimg->start = ftell(pimg->fh) - 1;
360            break;
361         }
362         while (ch != '\n' && ch != '\r') {
363            ch = getc(pimg->fh);
364         }
365      }
366   }
367
368   if (fread(buf, LITLEN(FILEID) + 1, 1, pimg->fh) != 1 ||
369       memcmp(buf, FILEID"\n", LITLEN(FILEID)) != 0) {
370      img_errno = IMG_BADFORMAT;
371      goto error;
372   }
373
374   /* check file format version */
375   ch = getc(pimg->fh);
376   pimg->version = 0;
377   if (tolower(ch) == 'b') {
378      /* binary file iff B/b prefix */
379      pimg->version = 1;
380      ch = getc(pimg->fh);
381   }
382   if (ch != 'v') {
383      img_errno = IMG_BADFORMAT;
384      goto error;
385   }
386   ch = getc(pimg->fh);
387   if (ch == '0') {
388      if (fread(buf, 4, 1, pimg->fh) != 1 || memcmp(buf, ".01\n", 4) != 0) {
389         img_errno = IMG_BADFORMAT;
390         goto error;
391      }
392      /* nothing special to do */
393   } else if (pimg->version == 0) {
394      if (ch < '2' || ch > '3' || getc(pimg->fh) != '\n') {
395         img_errno = IMG_TOONEW;
396         goto error;
397      }
398      pimg->version = ch - '0';
399   } else {
400      img_errno = IMG_BADFORMAT;
401      goto error;
402   }
403
404   if (!pimg->title)
405       pimg->title = getline_alloc(pimg->fh);
406   else
407       osfree(getline_alloc(pimg->fh));
408   pimg->datestamp = getline_alloc(pimg->fh);
409   if (!pimg->title || !pimg->datestamp) {
410      img_errno = IMG_OUTOFMEMORY;
411      error:
412      osfree(pimg->title);
413      osfree(pimg->datestamp);
414      fclose(pimg->fh);
415      osfree(pimg);
416      return NULL;
417   }
418
419   pimg->start = ftell(pimg->fh);
420
421   return pimg;
422}
423
424int
425img_rewind(img *pimg)
426{
427   if (!pimg->fRead) {
428      img_errno = IMG_WRITEERROR;
429      return 0;
430   }
431   if (fseek(pimg->fh, pimg->start, SEEK_SET) != 0) {
432      img_errno = IMG_READERROR;
433      return 0;
434   }
435   clearerr(pimg->fh);
436   /* [version -1] already skipped heading line, or there wasn't one
437    * [version 0] not in the middle of a 'LINE' command
438    * [version 3] not in the middle of turning a LINE into a MOVE */
439   pimg->pending = 0;
440
441   img_errno = IMG_NONE;
442
443   /* for version 3 we use label_buf to store the prefix for reuse */
444   /* for version -2, 0 value indicates we haven't entered a survey yet */
445   pimg->label_len = 0;
446   return 1;
447}
448
449img *
450img_open_write(const char *fnm, char *title_buf, bool fBinary)
451{
452   time_t tm;
453   img *pimg;
454
455   fBinary = fBinary;
456
457   if (fDirectory(fnm)) {
458      img_errno = IMG_DIRECTORY;
459      return NULL;
460   }
461
462   pimg = (img *)xosmalloc(ossizeof(img));
463   if (pimg == NULL) {
464      img_errno = IMG_OUTOFMEMORY;
465      return NULL;
466   }
467
468   pimg->buf_len = 257;
469   pimg->label_buf = xosmalloc(pimg->buf_len);
470   if (!pimg->label_buf) {
471      osfree(pimg);
472      img_errno = IMG_OUTOFMEMORY;
473      return NULL;
474   }
475
476   pimg->fh = fopen(fnm, "wb");
477   if (!pimg->fh) {
478      osfree(pimg);
479      img_errno = IMG_CANTOPENOUT;
480      return NULL;
481   }
482
483   /* Output image file header */
484   fputs("Survex 3D Image File\n", pimg->fh); /* file identifier string */
485   if (img_output_version < 2) {
486      pimg->version = 1;
487      fputs("Bv0.01\n", pimg->fh); /* binary file format version number */
488   } else {
489      pimg->version = (img_output_version > 2) ? 3 : 2;
490      fprintf(pimg->fh, "v%d\n", pimg->version); /* file format version no. */
491   }
492   fputsnl(title_buf, pimg->fh);
493   tm = time(NULL);
494   if (tm == (time_t)-1) {
495      fputsnl(TIMENA, pimg->fh);
496   } else {
497      char date[256];
498      /* output current date and time in format specified */
499      strftime(date, 256, TIMEFMT, localtime(&tm));
500      fputsnl(date, pimg->fh);
501   }
502   pimg->fRead = fFalse; /* writing to this file */
503   img_errno = IMG_NONE;
504
505   /* for version 3 we use label_buf to store the prefix for reuse */
506   pimg->label_buf[0] = '\0';
507   pimg->label_len = 0;
508
509   /* Don't check for write errors now - let img_close() report them... */
510   return pimg;
511}
512
513static int
514read_coord(FILE *fh, img_point *pt)
515{
516   ASSERT(fh);
517   ASSERT(pt);
518   pt->x = get32(fh) / 100.0;
519   pt->y = get32(fh) / 100.0;
520   pt->z = get32(fh) / 100.0;
521   if (ferror(fh) || feof(fh)) {
522      img_errno = feof(fh) ? IMG_BADFORMAT : IMG_READERROR;
523      return 0;
524   }
525   return 1;
526}
527
528static int
529skip_coord(FILE *fh)
530{
531    return (fseek(fh, 12, SEEK_CUR) == 0);
532}
533
534int
535img_read_item(img *pimg, img_point *p)
536{
537   int result;
538   pimg->flags = 0;
539
540   if (pimg->version == 3) {
541      int opt;
542      if (pimg->pending >= 0x80) {
543         *p = pimg->mv;
544         pimg->flags = (int)(pimg->pending) & 0x3f;
545         pimg->pending = 0;
546         return img_LINE;
547      }
548      again3: /* label to goto if we get a prefix */
549      pimg->label = pimg->label_buf;
550      opt = getc(pimg->fh);
551      if (opt == EOF) {
552         img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
553         return img_BAD;
554      }
555      switch (opt >> 6) {
556       case 0:
557         if (opt == 0) {
558            if (!pimg->label_len) return img_STOP; /* end of data marker */
559            pimg->label_len = 0;
560            goto again3;
561         }
562         if (opt < 15) {
563            /* 1-14 mean trim that many levels from current prefix */
564            int c;
565            if (pimg->label_len <= 16) {
566               /* zero prefix using "0" */
567               img_errno = IMG_BADFORMAT;
568               return img_BAD;
569            }
570            c = pimg->label_len - 16 - 1;
571            while (pimg->label_buf[c] != '.' || --opt > 0) {
572               if (--c < 0) {
573                  /* zero prefix using "0" */
574                  img_errno = IMG_BADFORMAT;
575                  return img_BAD;
576               }
577            }
578            c++;
579            pimg->label_len = c;
580            goto again3;
581         }
582         if (opt == 15) {
583            result = img_MOVE;
584            break;
585         }
586         /* 16-31 mean remove (n - 15) characters from the prefix */
587         /* zero prefix using 0 */
588         if (pimg->label_len <= (size_t)(opt - 15)) {
589            img_errno = IMG_BADFORMAT;
590            return img_BAD;
591         }
592         pimg->label_len -= (opt - 15);
593         goto again3;
594       case 1: case 2: {
595         char *q;
596         long len = getc(pimg->fh);
597         if (len == EOF) {
598            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
599            return img_BAD;
600         }
601         if (len == 0xfe) {
602            len += get16(pimg->fh);
603            if (feof(pimg->fh)) {
604               img_errno = IMG_BADFORMAT;
605               return img_BAD;
606            }
607            if (ferror(pimg->fh)) {
608               img_errno = IMG_READERROR;
609               return img_BAD;
610            }
611         } else if (len == 0xff) {
612            len = get32(pimg->fh);
613            if (ferror(pimg->fh)) {
614               img_errno = IMG_READERROR;
615               return img_BAD;
616            }
617            if (feof(pimg->fh) || len < 0xfe + 0xffff) {
618               img_errno = IMG_BADFORMAT;
619               return img_BAD;
620            }
621         }
622
623         if (!check_label_space(pimg, pimg->label_len + len + 1)) {
624            img_errno = IMG_OUTOFMEMORY;
625            return img_BAD;
626         }
627         q = pimg->label_buf + pimg->label_len;
628         pimg->label_len += len;
629         if (len && fread(q, len, 1, pimg->fh) != 1) {
630            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
631            return img_BAD;
632         }
633         q[len] = '\0';
634
635         result = opt & 0x40 ? img_LABEL : img_LINE;
636
637         if (pimg->survey_len) {
638            size_t l = pimg->survey_len;
639            const char *s = pimg->label_buf;
640            if (result == img_LINE) {
641               if (strncmp(pimg->survey, s, l) != 0 ||
642                   !(s[l] == '.' || s[l] == '\0')) {
643                  if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
644                  pimg->pending = 15;
645                  goto again3;
646               }
647            } else {
648               if (strncmp(pimg->survey, s, l + 1) != 0) {
649                  if (!skip_coord(pimg->fh)) return img_BAD;
650                  pimg->pending = 0;
651                  goto again3;
652               }
653            }
654            pimg->label += l;
655            /* skip the dot if there */
656            if (*pimg->label) pimg->label++;
657         }
658
659         if (result == img_LINE && pimg->pending) {
660            *p = pimg->mv;
661            if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
662            pimg->pending = opt;
663            return img_MOVE;
664         }
665         pimg->flags = (int)opt & 0x3f;
666         break;
667       }
668       default:
669         img_errno = IMG_BADFORMAT;
670         return img_BAD;
671      }
672      if (!read_coord(pimg->fh, p)) return img_BAD;
673      pimg->pending = 0;
674      return result;
675   }
676
677   pimg->label = pimg->label_buf;
678
679   if (pimg->version > 0) {
680      static long opt_lookahead = 0;
681      static img_point pt = { 0.0, 0.0, 0.0 };
682      long opt;
683      again: /* label to goto if we get a cross */
684      pimg->label[0] = '\0';
685
686      if (pimg->version == 1) {
687         if (opt_lookahead) {
688            opt = opt_lookahead;
689            opt_lookahead = 0;
690         } else {
691            opt = get32(pimg->fh);
692         }
693      } else {
694         opt = getc(pimg->fh);
695      }
696
697      if (feof(pimg->fh)) {
698         img_errno = IMG_BADFORMAT;
699         return img_BAD;
700      }
701      if (ferror(pimg->fh)) {
702         img_errno = IMG_READERROR;
703         return img_BAD;
704      }
705
706      switch (opt) {
707       case -1: case 0:
708         return img_STOP; /* end of data marker */
709       case 1:
710         /* skip coordinates */
711         if (!skip_coord(pimg->fh)) {
712            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
713            return img_BAD;
714         }
715         goto again;
716       case 2: case 3: {
717         char *q;
718         int ch;
719         result = img_LABEL;
720         ch = getc(pimg->fh);
721         if (ch == EOF) {
722            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
723            return img_BAD;
724         }
725         if (ch != '\\') ungetc(ch, pimg->fh);
726         fgets(pimg->label_buf, 257, pimg->fh);
727         if (feof(pimg->fh)) {
728            img_errno = IMG_BADFORMAT;
729            return img_BAD;
730         }
731         if (ferror(pimg->fh)) {
732            img_errno = IMG_READERROR;
733            return img_BAD;
734         }
735         q = pimg->label_buf + strlen(pimg->label_buf) - 1;
736         if (*q != '\n') {
737            img_errno = IMG_BADFORMAT;
738            return img_BAD;
739         }
740         /* Ignore empty labels in some .3d files (caused by a bug) */
741         if (q == pimg->label_buf) goto again;
742         *q = '\0';
743         pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
744         if (opt == 2) goto done;
745         break;
746       }
747       case 6: case 7: {
748         long len;
749         result = img_LABEL;
750
751         if (opt == 7)
752            pimg->flags = getc(pimg->fh);
753         else
754            pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
755
756         len = get32(pimg->fh);
757
758         if (feof(pimg->fh)) {
759            img_errno = IMG_BADFORMAT;
760            return img_BAD;
761         }
762         if (ferror(pimg->fh)) {
763            img_errno = IMG_READERROR;
764            return img_BAD;
765         }
766
767         /* Ignore empty labels in some .3d files (caused by a bug) */
768         if (len == 0) goto again;
769         if (!check_label_space(pimg, len + 1)) {
770            img_errno = IMG_OUTOFMEMORY;
771            return img_BAD;
772         }
773         if (fread(pimg->label_buf, len, 1, pimg->fh) != 1) {
774            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
775            return img_BAD;
776         }
777         break;
778       }
779       case 4:
780         result = img_MOVE;
781         break;
782       case 5:
783         result = img_LINE;
784         break;
785       default:
786         switch ((int)opt & 0xc0) {
787          case 0x80:
788            pimg->flags = (int)opt & 0x3f;
789            result = img_LINE;
790            break;
791          case 0x40: {
792            char *q;
793            pimg->flags = (int)opt & 0x3f;
794            result = img_LABEL;
795            if (!fgets(pimg->label_buf, 257, pimg->fh)) {
796               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
797               return img_BAD;
798            }
799            q = pimg->label_buf + strlen(pimg->label_buf) - 1;
800            /* Ignore empty-labels in some .3d files (caused by a bug) */
801            if (q == pimg->label_buf) goto again;
802            if (*q != '\n') {
803               img_errno = IMG_BADFORMAT;
804               return img_BAD;
805            }
806            *q = '\0';
807            break;
808          }
809          case 0xc0:
810            /* use this for an extra leg or station flag if we need it */
811          default:
812            img_errno = IMG_BADFORMAT;
813            return img_BAD;
814         }
815         break;
816      }
817
818      if (!read_coord(pimg->fh, &pt)) return img_BAD;
819
820      if (result == img_LABEL && pimg->survey_len) {
821         if (strncmp(pimg->label_buf, pimg->survey, pimg->survey_len + 1) != 0)
822            goto again;
823         pimg->label += pimg->survey_len + 1;
824      }
825
826      done:
827      *p = pt;
828
829      if (result == img_MOVE && pimg->version == 1) {
830         /* peek at next code and see if it's an old-style label */
831         opt_lookahead = get32(pimg->fh);
832
833         if (feof(pimg->fh)) {
834            img_errno = IMG_BADFORMAT;
835            return img_BAD;
836         }
837         if (ferror(pimg->fh)) {
838            img_errno = IMG_READERROR;
839            return img_BAD;
840         }
841
842         if (opt_lookahead == 2) return img_read_item(pimg, p);
843      }
844
845      return result;
846   } else if (pimg->version == 0) {
847      ascii_again:
848      pimg->label[0] = '\0';
849      if (feof(pimg->fh)) return img_STOP;
850      if (pimg->pending) {
851         pimg->pending = 0;
852         result = img_LINE;
853      } else {
854         char cmd[7];
855         /* Stop if nothing found */
856         if (fscanf(pimg->fh, "%6s", cmd) < 1) return img_STOP;
857         if (strcmp(cmd, "move") == 0)
858            result = img_MOVE;
859         else if (strcmp(cmd, "draw") == 0)
860            result = img_LINE;
861         else if (strcmp(cmd, "line") == 0) {
862            /* set flag to indicate to process second triplet as LINE */
863            pimg->pending = 1;
864            result = img_MOVE;
865         } else if (strcmp(cmd, "cross") == 0) {
866            if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
867               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
868               return img_BAD;
869            }
870            goto ascii_again;
871         } else if (strcmp(cmd, "name") == 0) {
872            size_t off = 0;
873            int ch = getc(pimg->fh);
874            if (ch == ' ') ch = getc(pimg->fh);
875            while (ch != ' ') {
876               if (ch == '\n' || ch == EOF) {
877                  img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
878                  return img_BAD;
879               }
880               if (off == pimg->buf_len) {
881                  if (!check_label_space(pimg, pimg->buf_len * 2)) {
882                     img_errno = IMG_OUTOFMEMORY;
883                     return img_BAD;
884                  }
885               }
886               pimg->label_buf[off++] = ch;
887               ch = getc(pimg->fh);
888            }
889            pimg->label_buf[off] = '\0';
890
891            pimg->label = pimg->label_buf;
892            if (pimg->label[0] == '\\') pimg->label++;
893
894            result = img_LABEL;
895         } else {
896            img_errno = IMG_BADFORMAT;
897            return img_BAD; /* unknown keyword */
898         }
899      }
900
901      if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
902         img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
903         return img_BAD;
904      }
905
906      if (result == img_LABEL && pimg->survey_len) {
907         if (strncmp(pimg->label, pimg->survey, pimg->survey_len + 1) != 0)
908            goto ascii_again;
909         pimg->label += pimg->survey_len + 1;
910      }
911
912      return result;
913   } else if (pimg->version == -1) {
914      /* version -1: .pos file */
915      size_t off;
916      pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
917      againpos:
918      off = 0;
919      while (fscanf(pimg->fh, "(%lf,%lf,%lf ) ", &p->x, &p->y, &p->z) != 3) {
920         int ch;
921         if (ferror(pimg->fh)) {
922            img_errno = IMG_READERROR;
923            return img_BAD;
924         }
925         if (feof(pimg->fh)) return img_STOP;
926         if (pimg->pending) {
927            img_errno = IMG_BADFORMAT;
928            return img_BAD;
929         }
930         pimg->pending = 1;
931         /* ignore rest of line */
932         do {
933            ch = getc(pimg->fh);
934         } while (ch != '\n' && ch != '\r' && ch != EOF);
935      }
936
937      pimg->label_buf[0] = '\0';
938      while (!feof(pimg->fh)) {
939         char *b;
940         if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
941            img_errno = IMG_READERROR;
942            return img_BAD;
943         }
944
945         off += strlen(pimg->label_buf + off);
946         if (off && pimg->label_buf[off - 1] == '\n') {
947            pimg->label_buf[off - 1] = '\0';
948            break;
949         }
950         if (!check_label_space(pimg, pimg->buf_len * 2)) {
951            img_errno = IMG_OUTOFMEMORY;
952            return img_BAD;
953         }
954      }
955
956      pimg->label = pimg->label_buf;
957
958      if (pimg->label[0] == '\\') pimg->label++;
959
960      if (pimg->survey_len) {
961         size_t l = pimg->survey_len + 1;
962         if (strncmp(pimg->survey, pimg->label, l) != 0) goto againpos;
963         pimg->label += l;
964      }
965
966      return img_LABEL;
967   } else {
968      /* version -2: Compass .plt file */
969      if (pimg->pending > 0) {
970         /* -1 signals we've entered the first survey we want to
971          * read, and need to fudge lots if the first action is 'D'...
972          */
973         /* pending MOVE or LINE */
974         int r = pimg->pending - 4;
975         pimg->pending = 0;
976         pimg->flags = 0;
977         pimg->label[pimg->label_len] = '\0';
978         return r;
979      }
980
981      while (1) {
982         char *line;
983         char *q;
984         size_t len = 0;
985         int ch = getc(pimg->fh);
986
987         switch (ch) {
988            case '\x1a': case EOF: /* Don't insist on ^Z at end of file */
989               return img_STOP;
990            case 'N':
991               line = getline_alloc(pimg->fh);
992               while (line[len] > 32) ++len;
993               if (pimg->survey && pimg->label_len == 0)
994                  pimg->pending = -1;
995               if (!check_label_space(pimg, len + 1)) {
996                  osfree(line);
997                  img_errno = IMG_OUTOFMEMORY;
998                  return img_BAD;
999               }
1000               pimg->label_len = len;
1001               pimg->label = pimg->label_buf;
1002               memcpy(pimg->label, line, len);
1003               pimg->label[len] = '\0';
1004               osfree(line);
1005               break;
1006            case 'M': case 'D': {
1007               /* Move or Draw */
1008               long fpos = -1;
1009               if (!pimg->survey && pimg->label_len == 0) {
1010                  /* We're only holding onto this line in case the first line
1011                   * of the 'N' is a 'D', so reprocess this line as an 'X'
1012                   * to skip it for now...
1013                   */
1014                  ungetc('X', pimg->fh);
1015                  break;
1016               }
1017               if (ch == 'D' && pimg->pending == -1) {
1018                  fpos = ftell(pimg->fh) - 1;
1019                  fseek(pimg->fh, pimg->start, SEEK_SET);
1020                  ch = getc(pimg->fh);
1021                  pimg->pending = 0;
1022               }
1023               line = getline_alloc(pimg->fh);
1024               if (sscanf(line, "%lf %lf %lf", &p->x, &p->y, &p->z) != 3) {
1025                  osfree(line);
1026                  if (ferror(pimg->fh)) {
1027                     img_errno = IMG_READERROR;
1028                  } else {
1029                     img_errno = IMG_BADFORMAT;
1030                  }
1031                  return img_BAD;
1032               }
1033               q = strchr(line, 'S');
1034               if (!q) {
1035                  osfree(line);
1036                  img_errno = IMG_BADFORMAT;
1037                  return img_BAD;
1038               }
1039               ++q;
1040               len = 0;
1041               while (q[len] > ' ') ++len;
1042               q[len] = '\0';
1043               len += 2; /* '.' and '\0' */
1044               if (!check_label_space(pimg, pimg->label_len + len)) {
1045                  img_errno = IMG_OUTOFMEMORY;
1046                  return img_BAD;
1047               }
1048               pimg->label = pimg->label_buf;
1049               pimg->label[pimg->label_len] = '.';
1050               memcpy(pimg->label + pimg->label_len + 1, q, len - 1);
1051               osfree(line);
1052               pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1053               if (fpos != -1) {
1054                  fseek(pimg->fh, fpos, SEEK_SET);
1055               } else {
1056                  pimg->pending = (ch == 'M' ? img_MOVE : img_LINE) + 4;
1057               }
1058               return img_LABEL;
1059            }
1060            case 'X': case 'F': case 'S':
1061               /* bounding boX (marks end of survey), Feature survey, or
1062                * new Section - skip to next survey */
1063               if (pimg->survey) return img_STOP;
1064               while (1) {
1065                  do {
1066                     ch = getc(pimg->fh);
1067                  } while (ch != '\n' && ch != '\r' && ch != EOF);
1068                  while (ch == '\n' || ch == '\r') ch = getc(pimg->fh);
1069                  if (ch == 'N') break;
1070                  if (ch == '\x1a' || ch == EOF) return img_STOP;
1071               }
1072               ungetc('N', pimg->fh);
1073               break;
1074            default:
1075               img_errno = IMG_BADFORMAT;
1076               return img_BAD;
1077         }
1078      }
1079   }
1080}
1081
1082static int
1083write_v3label(img *pimg, int opt, const char *s)
1084{
1085   size_t len, n, dot;
1086
1087   /* find length of common prefix */
1088   dot = 0;
1089   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
1090      if (s[len] == '.') dot = len + 1;
1091   }
1092
1093   ASSERT(len <= pimg->label_len);
1094   n = pimg->label_len - len;
1095   if (len == 0) {
1096      if (pimg->label_len) putc(0, pimg->fh);
1097   } else if (n <= 16) {
1098      if (n) putc(n + 15, pimg->fh);
1099   } else if (dot == 0) {
1100      if (pimg->label_len) putc(0, pimg->fh);
1101      len = 0;
1102   } else {
1103      const char *p = pimg->label_buf + dot;
1104      n = 1;
1105      for (len = pimg->label_len - dot - 17; len; len--) {
1106         if (*p++ == '.') n++;
1107      }
1108      if (n <= 14) {
1109         putc(n, pimg->fh);
1110         len = dot;
1111      } else {
1112         if (pimg->label_len) putc(0, pimg->fh);
1113         len = 0;
1114      }
1115   }
1116
1117   n = strlen(s + len);
1118   putc(opt, pimg->fh);
1119   if (n < 0xfe) {
1120      putc(n, pimg->fh);
1121   } else if (n < 0xffff + 0xfe) {
1122      putc(0xfe, pimg->fh);
1123      put16(n - 0xfe, pimg->fh);
1124   } else {
1125      putc(0xff, pimg->fh);
1126      put32(n, pimg->fh);
1127   }
1128   fwrite(s + len, n, 1, pimg->fh);
1129
1130   n += len;
1131   pimg->label_len = n;
1132   if (!check_label_space(pimg, n + 1))
1133      return 0; /* FIXME: distinguish out of memory... */
1134   memcpy(pimg->label_buf + len, s + len, n - len + 1);
1135
1136   return !ferror(pimg->fh);
1137}
1138
1139void
1140img_write_item(img *pimg, int code, int flags, const char *s,
1141               double x, double y, double z)
1142{
1143   if (!pimg) return;
1144   if (pimg->version == 3) {
1145      int opt = 0;
1146      switch (code) {
1147       case img_LABEL:
1148         write_v3label(pimg, 0x40 | flags, s);
1149         opt = 0;
1150         break;
1151       case img_MOVE:
1152         opt = 15;
1153         break;
1154       case img_LINE:
1155         write_v3label(pimg, 0x80 | flags, s ? s : "");
1156         opt = 0;
1157         break;
1158       default: /* ignore for now */
1159         return;
1160      }
1161      if (opt) putc(opt, pimg->fh);
1162      /* Output in cm */
1163      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1164      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1165      put32((INT32_T)my_round(z * 100.0), pimg->fh);
1166   } else {
1167      size_t len;
1168      INT32_T opt = 0;
1169      ASSERT(pimg->version > 0);
1170      switch (code) {
1171       case img_LABEL:
1172         if (pimg->version == 1) {
1173            /* put a move before each label */
1174            img_write_item(pimg, img_MOVE, 0, NULL, x, y, z);
1175            put32(2, pimg->fh);
1176            fputsnl(s, pimg->fh);
1177            return;
1178         }
1179         len = strlen(s);
1180         if (len > 255 || strchr(s, '\n')) {
1181            /* long label - not in early incarnations of v2 format, but few
1182             * 3d files will need these, so better not to force incompatibility
1183             * with a new version I think... */
1184            putc(7, pimg->fh);
1185            putc(flags, pimg->fh);
1186            put32(len, pimg->fh);
1187            fputs(s, pimg->fh);
1188         } else {
1189            putc(0x40 | (flags & 0x3f), pimg->fh);
1190            fputsnl(s, pimg->fh);
1191         }
1192         opt = 0;
1193         break;
1194       case img_MOVE:
1195         opt = 4;
1196         break;
1197       case img_LINE:
1198         if (pimg->version > 1) {
1199            opt = 0x80 | (flags & 0x3f);
1200            break;
1201         }
1202         opt = 5;
1203         break;
1204       default: /* ignore for now */
1205         return;
1206      }
1207      if (pimg->version == 1) {
1208         put32(opt, pimg->fh);
1209      } else {
1210         if (opt) putc(opt, pimg->fh);
1211      }
1212      /* Output in cm */
1213      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1214      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1215      put32((INT32_T)my_round(z * 100.0), pimg->fh);
1216   }
1217}
1218
1219int
1220img_close(img *pimg)
1221{
1222   int result = 1;
1223   if (pimg) {
1224      if (pimg->fh) {
1225         if (pimg->fRead) {
1226            osfree(pimg->survey);
1227            osfree(pimg->title);
1228            osfree(pimg->datestamp);
1229         } else {
1230            /* write end of data marker */
1231            switch (pimg->version) {
1232             case 1:
1233               put32((INT32_T)-1, pimg->fh);
1234               break;
1235             case 2:
1236               putc(0, pimg->fh);
1237               break;
1238             case 3:
1239               if (pimg->label_len) putc(0, pimg->fh);
1240               putc(0, pimg->fh);
1241               break;
1242            }
1243         }
1244         if (ferror(pimg->fh)) result = 0;
1245         if (fclose(pimg->fh)) result = 0;
1246         if (!result) img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
1247      }
1248      osfree(pimg->label_buf);
1249      osfree(pimg);
1250   }
1251   return result;
1252}
Note: See TracBrowser for help on using the repository browser.