source: git/src/img.c @ 38193a5

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

Detect .pos files even with a different extension.

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

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