source: git/src/img.c @ cd70c00

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

img can now read Compass plt files

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

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