source: git/src/img.c @ 8ec15cf

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since 8ec15cf was 8ec15cf, checked in by Olly Betts <olly@…>, 10 years ago

src/img.c: In non-hosted mode, don't define GETC and PUTC if they're
already defined, to allow easy overriding with getc_unlocked() and
putc_unlocked().

  • Property mode set to 100644
File size: 62.8 KB
Line 
1/* img.c
2 * Routines for reading and writing Survex ".3d" image files
3 * Copyright (C) 1993-2004,2005,2006,2010,2011,2013,2014 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 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#include <locale.h>
30
31#include "img.h"
32
33#define TIMENA "?"
34#ifdef IMG_HOSTED
35# define INT32_T int32_t
36# include "debug.h"
37# include "filelist.h"
38# include "filename.h"
39# include "message.h"
40# include "useful.h"
41# define TIMEFMT msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107)
42#else
43# ifdef HAVE_STDINT_H
44#  include <stdint.h>
45#  define INT32_T int32_t
46# else
47#  include <limits.h>
48#  if INT_MAX >= 2147483647
49#   define INT32_T int
50#  else
51#   define INT32_T long
52#  endif
53# endif
54# define TIMEFMT "%a,%Y.%m.%d %H:%M:%S %Z"
55# define EXT_SVX_3D "3d"
56# define EXT_SVX_POS "pos"
57# define FNM_SEP_EXT '.'
58# define METRES_PER_FOOT 0.3048 /* exact value */
59# define xosmalloc(L) malloc((L))
60# define xosrealloc(L,S) realloc((L),(S))
61# define osfree(P) free((P))
62# define osnew(T) (T*)malloc(sizeof(T))
63
64/* in IMG_HOSTED mode, this tests if a filename refers to a directory */
65# define fDirectory(X) 0
66/* open file FNM with mode MODE, maybe using path PTH and/or extension EXT */
67/* path isn't used in img.c, but EXT is */
68# define fopenWithPthAndExt(PTH,FNM,EXT,MODE,X) \
69    ((*(X) = NULL), fopen(FNM,MODE))
70# ifndef PUTC
71#  define PUTC(C, FH) putc(C, FH)
72# endif
73# ifndef GETC
74#  define GETC(FH) getc(FH)
75# endif
76# define fputsnl(S, FH) (fputs((S), (FH)) == EOF ? EOF : putc('\n', (FH)))
77# define SVX_ASSERT(X)
78
79#ifdef __cplusplus
80# include <algorithm>
81using std::max;
82using std::min;
83#else
84/* Return max/min of two numbers. */
85/* May be defined already (e.g. by Borland C in stdlib.h) */
86/* NB Bad news if X or Y has side-effects... */
87# ifndef max
88#  define max(X, Y) ((X) > (Y) ? (X) : (Y))
89# endif
90# ifndef min
91#  define min(X, Y) ((X) < (Y) ? (X) : (Y))
92# endif
93#endif
94
95static INT32_T
96get32(FILE *fh)
97{
98   INT32_T w = GETC(fh);
99   w |= (INT32_T)GETC(fh) << 8l;
100   w |= (INT32_T)GETC(fh) << 16l;
101   w |= (INT32_T)GETC(fh) << 24l;
102   return w;
103}
104
105static void
106put32(long w, FILE *fh)
107{
108   PUTC((char)(w), fh);
109   PUTC((char)(w >> 8l), fh);
110   PUTC((char)(w >> 16l), fh);
111   PUTC((char)(w >> 24l), fh);
112}
113
114static short
115get16(FILE *fh)
116{
117   short w = GETC(fh);
118   w |= (short)GETC(fh) << 8l;
119   return w;
120}
121
122static void
123put16(short w, FILE *fh)
124{
125   PUTC((char)(w), fh);
126   PUTC((char)(w >> 8l), fh);
127}
128
129static char *
130baseleaf_from_fnm(const char *fnm)
131{
132   const char *p;
133   const char *q;
134   char * res;
135   size_t len;
136
137   p = fnm;
138   q = strrchr(p, '/');
139   if (q) p = q + 1;
140   q = strrchr(p, '\\');
141   if (q) p = q + 1;
142
143   q = strrchr(p, FNM_SEP_EXT);
144   if (q) len = (const char *)q - p; else len = strlen(p);
145
146   res = (char *)xosmalloc(len + 1);
147   if (!res) return NULL;
148   memcpy(res, p, len);
149   res[len] = '\0';
150   return res;
151}
152#endif
153
154static unsigned short
155getu16(FILE *fh)
156{
157   return (unsigned short)get16(fh);
158}
159
160#include <math.h>
161
162#if !defined HAVE_LROUND && !defined HAVE_DECL_LROUND
163/* The autoconf tests are not in use, but C99 and C++11 both added lround(),
164 * so set HAVE_LROUND and HAVE_DECL_LROUND conservatively based on the language
165 * standard version the compiler claims to support. */
166# if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \
167     (defined __cplusplus && __cplusplus >= 201103L)
168#  define HAVE_LROUND 1
169#  define HAVE_DECL_LROUND 1
170# endif
171#endif
172
173#ifdef HAVE_LROUND
174# if defined HAVE_DECL_LROUND && !HAVE_DECL_LROUND
175/* On older systems, the prototype may be missing. */
176extern long lround(double);
177# endif
178# define my_lround lround
179#else
180static long
181my_lround(double x) {
182   return (x >= 0.0) ? (long)(x + 0.5) : -(long)(0.5 - x);
183}
184#endif
185
186/* portable case insensitive string compare */
187#if defined(strcasecmp) || defined(HAVE_STRCASECMP)
188# define my_strcasecmp strcasecmp
189#else
190/* What about top bit set chars? */
191int my_strcasecmp(const char *s1, const char *s2) {
192   register int c1, c2;
193   do {
194      c1 = *s1++;
195      c2 = *s2++;
196   } while (c1 && toupper(c1) == toupper(c2));
197   /* now calculate real difference */
198   return c1 - c2;
199}
200#endif
201
202unsigned int img_output_version = IMG_VERSION_MAX;
203
204static img_errcode img_errno = IMG_NONE;
205
206#define FILEID "Survex 3D Image File"
207
208#define EXT_PLT "plt"
209#define EXT_PLF "plf"
210#define EXT_XYZ "xyz"
211
212/* Attempt to string paste to ensure we are passed a literal string */
213#define LITLEN(S) (sizeof(S"") - 1)
214
215/* Fake "version numbers" for non-3d formats we can read. */
216#define VERSION_CMAP_SHOT       -4
217#define VERSION_CMAP_STATION    -3
218#define VERSION_COMPASS_PLT     -2
219#define VERSION_SURVEX_POS      -1
220
221static char *
222my_strdup(const char *str)
223{
224   char *p;
225   size_t len = strlen(str) + 1;
226   p = (char *)xosmalloc(len);
227   if (p) memcpy(p, str, len);
228   return p;
229}
230
231static char *
232getline_alloc(FILE *fh)
233{
234   int ch;
235   size_t i = 0;
236   size_t len = 16;
237   char *buf = (char *)xosmalloc(len);
238   if (!buf) return NULL;
239
240   ch = GETC(fh);
241   while (ch != '\n' && ch != '\r' && ch != EOF) {
242      buf[i++] = ch;
243      if (i == len - 1) {
244         char *p;
245         len += len;
246         p = (char *)xosrealloc(buf, len);
247         if (!p) {
248            osfree(buf);
249            return NULL;
250         }
251         buf = p;
252      }
253      ch = GETC(fh);
254   }
255   if (ch == '\n' || ch == '\r') {
256      int otherone = ch ^ ('\n' ^ '\r');
257      ch = GETC(fh);
258      /* if it's not the other eol character, put it back */
259      if (ch != otherone) ungetc(ch, fh);
260   }
261   buf[i++] = '\0';
262   return buf;
263}
264
265img_errcode
266img_error(void)
267{
268   return img_errno;
269}
270
271static int
272check_label_space(img *pimg, size_t len)
273{
274   if (len > pimg->buf_len) {
275      char *b = (char *)xosrealloc(pimg->label_buf, len);
276      if (!b) return 0;
277      pimg->label = (pimg->label - pimg->label_buf) + b;
278      pimg->label_buf = b;
279      pimg->buf_len = len;
280   }
281   return 1;
282}
283
284#define has_ext(F,L,E) ((L) > LITLEN(E) + 1 &&\
285                        (F)[(L) - LITLEN(E) - 1] == FNM_SEP_EXT &&\
286                        my_strcasecmp((F) + (L) - LITLEN(E), E) == 0)
287
288img *
289img_open_survey(const char *fnm, const char *survey)
290{
291   img *pimg;
292   size_t len;
293   char buf[LITLEN(FILEID) + 9];
294   int ch;
295
296   if (fDirectory(fnm)) {
297      img_errno = IMG_DIRECTORY;
298      return NULL;
299   }
300
301   pimg = osnew(img);
302   if (pimg == NULL) {
303      img_errno = IMG_OUTOFMEMORY;
304      return NULL;
305   }
306
307   pimg->buf_len = 257;
308   pimg->label_buf = (char *)xosmalloc(pimg->buf_len);
309   if (!pimg->label_buf) {
310      osfree(pimg);
311      img_errno = IMG_OUTOFMEMORY;
312      return NULL;
313   }
314
315   pimg->fh = fopenWithPthAndExt("", fnm, EXT_SVX_3D, "rb", &(pimg->filename_opened));
316   if (pimg->fh == NULL) {
317      osfree(pimg->label_buf);
318      osfree(pimg);
319      img_errno = IMG_FILENOTFOUND;
320      return NULL;
321   }
322
323   pimg->fRead = 1; /* reading from this file */
324   img_errno = IMG_NONE;
325
326   pimg->flags = 0;
327
328   /* for version >= 3 we use label_buf to store the prefix for reuse */
329   /* for VERSION_COMPASS_PLT, 0 value indicates we haven't
330    * entered a survey yet */
331   /* for VERSION_CMAP_SHOT, we store the last station here
332    * to detect whether we MOVE or LINE */
333   pimg->label_len = 0;
334   pimg->label_buf[0] = '\0';
335
336   pimg->survey = NULL;
337   pimg->survey_len = 0;
338   pimg->separator = '.';
339#if IMG_API_VERSION == 0
340   pimg->date1 = pimg->date2 = 0;
341#else /* IMG_API_VERSION == 1 */
342   pimg->days1 = pimg->days2 = -1;
343#endif
344   pimg->is_extended_elevation = 0;
345
346   pimg->style = pimg->oldstyle = img_STYLE_UNKNOWN;
347
348   pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
349
350   pimg->title = pimg->datestamp = NULL;
351   if (survey) {
352      len = strlen(survey);
353      if (len) {
354         if (survey[len - 1] == '.') len--;
355         if (len) {
356            char *p;
357            pimg->survey = (char *)xosmalloc(len + 2);
358            if (!pimg->survey) {
359               img_errno = IMG_OUTOFMEMORY;
360               goto error;
361            }
362            memcpy(pimg->survey, survey, len);
363            /* Set title to leaf survey name */
364            pimg->survey[len] = '\0';
365            p = strchr(pimg->survey, '.');
366            if (p) p++; else p = pimg->survey;
367            pimg->title = my_strdup(p);
368            if (!pimg->title) {
369               img_errno = IMG_OUTOFMEMORY;
370               goto error;
371            }
372            pimg->survey[len] = '.';
373            pimg->survey[len + 1] = '\0';
374         }
375      }
376      pimg->survey_len = len;
377   }
378
379   /* [VERSION_COMPASS_PLT, VERSION_CMAP_STATION, VERSION_CMAP_SHOT] pending
380    * IMG_LINE or IMG_MOVE - both have 4 added.
381    * [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
382    * [version 0] not in the middle of a 'LINE' command
383    * [version >= 3] not in the middle of turning a LINE into a MOVE
384    */
385   pimg->pending = 0;
386
387   len = strlen(fnm);
388   if (has_ext(fnm, len, EXT_SVX_POS)) {
389pos_file:
390      pimg->version = VERSION_SURVEX_POS;
391      if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
392      pimg->datestamp = my_strdup(TIMENA);
393      if (!pimg->datestamp) {
394         img_errno = IMG_OUTOFMEMORY;
395         goto error;
396      }
397      pimg->start = 0;
398      return pimg;
399   }
400
401   if (has_ext(fnm, len, EXT_PLT) || has_ext(fnm, len, EXT_PLF)) {
402      long fpos;
403plt_file:
404      pimg->version = VERSION_COMPASS_PLT;
405      /* Spaces aren't legal in Compass station names, but dots are, so
406       * use space as the level separator */
407      pimg->separator = ' ';
408      pimg->start = 0;
409      if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
410      pimg->datestamp = my_strdup(TIMENA);
411      if (!pimg->datestamp) {
412         img_errno = IMG_OUTOFMEMORY;
413         goto error;
414      }
415      while (1) {
416         ch = GETC(pimg->fh);
417         switch (ch) {
418          case '\x1a':
419            fseek(pimg->fh, -1, SEEK_CUR);
420            /* FALL THRU */
421          case EOF:
422            pimg->start = ftell(pimg->fh);
423            return pimg;
424          case 'N': {
425            char *line, *q;
426            fpos = ftell(pimg->fh) - 1;
427            if (!pimg->survey) {
428               /* FIXME : if there's only one survey in the file, it'd be nice
429                * to use its description as the title here...
430                */
431               ungetc('N', pimg->fh);
432               pimg->start = fpos;
433               return pimg;
434            }
435            line = getline_alloc(pimg->fh);
436            if (!line) {
437               img_errno = IMG_OUTOFMEMORY;
438               goto error;
439            }
440            len = 0;
441            while (line[len] > 32) ++len;
442            if (pimg->survey_len != len ||
443                memcmp(line, pimg->survey, len) != 0) {
444               osfree(line);
445               continue;
446            }
447            q = strchr(line + len, 'C');
448            if (q && q[1]) {
449                osfree(pimg->title);
450                pimg->title = my_strdup(q + 1);
451            } else if (!pimg->title) {
452                pimg->title = my_strdup(pimg->label);
453            }
454            osfree(line);
455            if (!pimg->title) {
456                img_errno = IMG_OUTOFMEMORY;
457                goto error;
458            }
459            if (!pimg->start) pimg->start = fpos;
460            fseek(pimg->fh, pimg->start, SEEK_SET);
461            return pimg;
462          }
463          case 'M': case 'D':
464            pimg->start = ftell(pimg->fh) - 1;
465            break;
466         }
467         while (ch != '\n' && ch != '\r') {
468            ch = GETC(pimg->fh);
469         }
470      }
471   }
472
473   if (has_ext(fnm, len, EXT_XYZ)) {
474      char *line;
475xyz_file:
476      /* Spaces aren't legal in CMAP station names, but dots are, so
477       * use space as the level separator. */
478      pimg->separator = ' ';
479      line = getline_alloc(pimg->fh);
480      if (!line) {
481         img_errno = IMG_OUTOFMEMORY;
482         goto error;
483      }
484      /* FIXME: reparse date? */
485      len = strlen(line);
486      if (len > 59) line[59] = '\0';
487      if (len > 45) {
488         pimg->datestamp = my_strdup(line + 45);
489      } else {
490         pimg->datestamp = my_strdup(TIMENA);
491      }
492      if (strncmp(line, "  Cave Survey Data Processed by CMAP ",
493                  LITLEN("  Cave Survey Data Processed by CMAP ")) == 0) {
494         len = 0;
495      } else {
496         if (len > 45) {
497            line[45] = '\0';
498            len = 45;
499         }
500         while (len > 2 && line[len - 1] == ' ') --len;
501         if (len > 2) {
502            line[len] = '\0';
503            pimg->title = my_strdup(line + 2);
504         }
505      }
506      if (len <= 2) pimg->title = baseleaf_from_fnm(fnm);
507      osfree(line);
508      if (!pimg->datestamp || !pimg->title) {
509         img_errno = IMG_OUTOFMEMORY;
510         goto error;
511      }
512      line = getline_alloc(pimg->fh);
513      if (!line) {
514         img_errno = IMG_OUTOFMEMORY;
515         goto error;
516      }
517      if (line[0] != ' ' || (line[1] != 'S' && line[1] != 'O')) {
518         img_errno = IMG_BADFORMAT;
519         goto error;
520      }
521      if (line[1] == 'S') {
522         pimg->version = VERSION_CMAP_STATION;
523      } else {
524         pimg->version = VERSION_CMAP_SHOT;
525      }
526      osfree(line);
527      line = getline_alloc(pimg->fh);
528      if (!line) {
529         img_errno = IMG_OUTOFMEMORY;
530         goto error;
531      }
532      if (line[0] != ' ' || line[1] != '-') {
533         img_errno = IMG_BADFORMAT;
534         goto error;
535      }
536      osfree(line);
537      pimg->start = ftell(pimg->fh);
538      return pimg;
539   }
540
541   if (fread(buf, LITLEN(FILEID) + 1, 1, pimg->fh) != 1 ||
542       memcmp(buf, FILEID"\n", LITLEN(FILEID) + 1) != 0) {
543      if (fread(buf + LITLEN(FILEID) + 1, 8, 1, pimg->fh) == 1 &&
544          memcmp(buf, FILEID"\r\nv0.01\r\n", LITLEN(FILEID) + 9) == 0) {
545         /* v0 3d file with DOS EOLs */
546         pimg->version = 0;
547         goto v03d;
548      }
549      rewind(pimg->fh);
550      if (buf[1] == ' ') {
551         if (buf[0] == ' ') {
552            /* Looks like a CMAP .xyz file ... */
553            goto xyz_file;
554         } else if (strchr("ZSNF", buf[0])) {
555            /* Looks like a Compass .plt file ... */
556            /* Almost certainly it'll start "Z " */
557            goto plt_file;
558         }
559      }
560      if (buf[0] == '(') {
561         /* Looks like a Survex .pos file ... */
562         goto pos_file;
563      }
564      img_errno = IMG_BADFORMAT;
565      goto error;
566   }
567
568   /* check file format version */
569   ch = GETC(pimg->fh);
570   pimg->version = 0;
571   if (tolower(ch) == 'b') {
572      /* binary file iff B/b prefix */
573      pimg->version = 1;
574      ch = GETC(pimg->fh);
575   }
576   if (ch != 'v') {
577      img_errno = IMG_BADFORMAT;
578      goto error;
579   }
580   ch = GETC(pimg->fh);
581   if (ch == '0') {
582      if (fread(buf, 4, 1, pimg->fh) != 1 || memcmp(buf, ".01\n", 4) != 0) {
583         img_errno = IMG_BADFORMAT;
584         goto error;
585      }
586      /* nothing special to do */
587   } else if (pimg->version == 0) {
588      if (ch < '2' || ch > '0' + IMG_VERSION_MAX || GETC(pimg->fh) != '\n') {
589         img_errno = IMG_TOONEW;
590         goto error;
591      }
592      pimg->version = ch - '0';
593   } else {
594      img_errno = IMG_BADFORMAT;
595      goto error;
596   }
597
598v03d:
599   if (!pimg->title)
600       pimg->title = getline_alloc(pimg->fh);
601   else
602       osfree(getline_alloc(pimg->fh));
603   pimg->datestamp = getline_alloc(pimg->fh);
604   if (!pimg->title || !pimg->datestamp) {
605      img_errno = IMG_OUTOFMEMORY;
606      error:
607      osfree(pimg->title);
608      osfree(pimg->datestamp);
609      osfree(pimg->filename_opened);
610      fclose(pimg->fh);
611      osfree(pimg);
612      return NULL;
613   }
614
615   if (pimg->version >= 8) {
616      int flags = GETC(pimg->fh);
617      if (flags & img_FFLAG_EXTENDED) pimg->is_extended_elevation = 1;
618   } else {
619      len = strlen(pimg->title);
620      if (len > 11 && strcmp(pimg->title + len - 11, " (extended)") == 0) {
621          pimg->title[len - 11] = '\0';
622          pimg->is_extended_elevation = 1;
623      }
624   }
625
626   pimg->start = ftell(pimg->fh);
627
628   return pimg;
629}
630
631int
632img_rewind(img *pimg)
633{
634   if (!pimg->fRead) {
635      img_errno = IMG_WRITEERROR;
636      return 0;
637   }
638   if (fseek(pimg->fh, pimg->start, SEEK_SET) != 0) {
639      img_errno = IMG_READERROR;
640      return 0;
641   }
642   clearerr(pimg->fh);
643   /* [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
644    * [version 0] not in the middle of a 'LINE' command
645    * [version >= 3] not in the middle of turning a LINE into a MOVE */
646   pimg->pending = 0;
647
648   img_errno = IMG_NONE;
649
650   /* for version >= 3 we use label_buf to store the prefix for reuse */
651   /* for VERSION_COMPASS_PLT, 0 value indicates we haven't entered a survey
652    * yet */
653   /* for VERSION_CMAP_SHOT, we store the last station here to detect whether
654    * we MOVE or LINE */
655   pimg->label_len = 0;
656   pimg->style = img_STYLE_UNKNOWN;
657   return 1;
658}
659
660img *
661img_open_write(const char *fnm, char *title, int flags)
662{
663   time_t tm;
664   img *pimg;
665
666   if (fDirectory(fnm)) {
667      img_errno = IMG_DIRECTORY;
668      return NULL;
669   }
670
671   pimg = osnew(img);
672   if (pimg == NULL) {
673      img_errno = IMG_OUTOFMEMORY;
674      return NULL;
675   }
676
677   pimg->buf_len = 257;
678   pimg->label_buf = (char *)xosmalloc(pimg->buf_len);
679   if (!pimg->label_buf) {
680      osfree(pimg);
681      img_errno = IMG_OUTOFMEMORY;
682      return NULL;
683   }
684
685   pimg->fh = fopen(fnm, "wb");
686   if (!pimg->fh) {
687      osfree(pimg->label_buf);
688      osfree(pimg);
689      img_errno = IMG_CANTOPENOUT;
690      return NULL;
691   }
692
693   pimg->filename_opened = NULL;
694
695   /* Output image file header */
696   fputs("Survex 3D Image File\n", pimg->fh); /* file identifier string */
697   if (img_output_version < 2) {
698      pimg->version = 1;
699      fputs("Bv0.01\n", pimg->fh); /* binary file format version number */
700   } else {
701      pimg->version = (img_output_version > IMG_VERSION_MAX) ? IMG_VERSION_MAX : img_output_version;
702      fprintf(pimg->fh, "v%d\n", pimg->version); /* file format version no. */
703   }
704
705   fputs(title, pimg->fh);
706   if (pimg->version < 8 && (flags & img_FFLAG_EXTENDED)) {
707      /* Older format versions append " (extended)" to the title to mark
708       * extended elevations. */
709      size_t len = strlen(title);
710      if (len < 11 || strcmp(title + len - 11, " (extended)") != 0)
711         fputs(" (extended)", pimg->fh);
712   }
713   PUTC('\n', pimg->fh);
714
715   tm = time(NULL);
716   if (tm == (time_t)-1) {
717      fputsnl(TIMENA, pimg->fh);
718   } else if (pimg->version <= 7) {
719      char date[256];
720      /* output current date and time in format specified */
721      strftime(date, 256, TIMEFMT, localtime(&tm));
722      fputsnl(date, pimg->fh);
723   } else {
724      fprintf(pimg->fh, "@%ld\n", (long)tm);
725   }
726
727   if (pimg->version >= 8) {
728      /* Clear bit one in case anyone has been passing true for fBinary. */
729      flags &=~ 1;
730      PUTC(flags, pimg->fh);
731   }
732
733#if 0
734   if (img_output_version >= 5) {
735       static const unsigned char codelengths[32] = {
736           4,  8,  8,  16, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
737           0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
738       };
739       fwrite(codelengths, 32, 1, pimg->fh);
740   }
741#endif
742   pimg->fRead = 0; /* writing to this file */
743   img_errno = IMG_NONE;
744
745   /* for version >= 3 we use label_buf to store the prefix for reuse */
746   pimg->label_buf[0] = '\0';
747   pimg->label_len = 0;
748
749#if IMG_API_VERSION == 0
750   pimg->date1 = pimg->date2 = 0;
751   pimg->olddate1 = pimg->olddate2 = 0;
752#else /* IMG_API_VERSION == 1 */
753   pimg->days1 = pimg->days2 = -1;
754   pimg->olddays1 = pimg->olddays2 = -1;
755#endif
756   pimg->style = pimg->oldstyle = img_STYLE_UNKNOWN;
757
758   pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
759
760   pimg->n_legs = 0;
761   pimg->length = 0.0;
762   pimg->E = pimg->H = pimg->V = 0.0;
763
764   /* Don't check for write errors now - let img_close() report them... */
765   return pimg;
766}
767
768static void
769read_xyz_station_coords(img_point *pt, const char *line)
770{
771   char num[12];
772   memcpy(num, line + 6, 9);
773   num[9] = '\0';
774   pt->x = atof(num) / METRES_PER_FOOT;
775   memcpy(num, line + 15, 9);
776   pt->y = atof(num) / METRES_PER_FOOT;
777   memcpy(num, line + 24, 8);
778   num[8] = '\0';
779   pt->z = atof(num) / METRES_PER_FOOT;
780}
781
782static void
783read_xyz_shot_coords(img_point *pt, const char *line)
784{
785   char num[12];
786   memcpy(num, line + 40, 10);
787   num[10] = '\0';
788   pt->x = atof(num) / METRES_PER_FOOT;
789   memcpy(num, line + 50, 10);
790   pt->y = atof(num) / METRES_PER_FOOT;
791   memcpy(num, line + 60, 9);
792   num[9] = '\0';
793   pt->z = atof(num) / METRES_PER_FOOT;
794}
795
796static void
797subtract_xyz_shot_deltas(img_point *pt, const char *line)
798{
799   char num[12];
800   memcpy(num, line + 15, 9);
801   num[9] = '\0';
802   pt->x -= atof(num) / METRES_PER_FOOT;
803   memcpy(num, line + 24, 8);
804   num[8] = '\0';
805   pt->y -= atof(num) / METRES_PER_FOOT;
806   memcpy(num, line + 32, 8);
807   pt->z -= atof(num) / METRES_PER_FOOT;
808}
809
810static int
811read_coord(FILE *fh, img_point *pt)
812{
813   SVX_ASSERT(fh);
814   SVX_ASSERT(pt);
815   pt->x = get32(fh) / 100.0;
816   pt->y = get32(fh) / 100.0;
817   pt->z = get32(fh) / 100.0;
818   if (ferror(fh) || feof(fh)) {
819      img_errno = feof(fh) ? IMG_BADFORMAT : IMG_READERROR;
820      return 0;
821   }
822   return 1;
823}
824
825static int
826skip_coord(FILE *fh)
827{
828    return (fseek(fh, 12, SEEK_CUR) == 0);
829}
830
831static int
832read_v3label(img *pimg)
833{
834   char *q;
835   long len = GETC(pimg->fh);
836   if (len == EOF) {
837      img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
838      return img_BAD;
839   }
840   if (len == 0xfe) {
841      len += get16(pimg->fh);
842      if (feof(pimg->fh)) {
843         img_errno = IMG_BADFORMAT;
844         return img_BAD;
845      }
846      if (ferror(pimg->fh)) {
847         img_errno = IMG_READERROR;
848         return img_BAD;
849      }
850   } else if (len == 0xff) {
851      len = get32(pimg->fh);
852      if (ferror(pimg->fh)) {
853         img_errno = IMG_READERROR;
854         return img_BAD;
855      }
856      if (feof(pimg->fh) || len < 0xfe + 0xffff) {
857         img_errno = IMG_BADFORMAT;
858         return img_BAD;
859      }
860   }
861
862   if (!check_label_space(pimg, pimg->label_len + len + 1)) {
863      img_errno = IMG_OUTOFMEMORY;
864      return img_BAD;
865   }
866   q = pimg->label_buf + pimg->label_len;
867   pimg->label_len += len;
868   if (len && fread(q, len, 1, pimg->fh) != 1) {
869      img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
870      return img_BAD;
871   }
872   q[len] = '\0';
873   return 0;
874}
875
876static int
877read_v8label(img *pimg, int common_flag, size_t common_val)
878{
879   char *q;
880   size_t del, add;
881   if (common_flag) {
882      if (common_val == 0) return 0;
883      add = del = common_val;
884   } else {
885      int ch = GETC(pimg->fh);
886      if (ch == EOF) {
887         img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
888         return img_BAD;
889      }
890      if (ch != 0x00) {
891         del = ch >> 4;
892         add = ch & 0x0f;
893      } else {
894         ch = GETC(pimg->fh);
895         if (ch == EOF) {
896            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
897            return img_BAD;
898         }
899         if (ch != 0xff) {
900            del = ch;
901         } else {
902            del = get32(pimg->fh);
903            if (ferror(pimg->fh)) {
904               img_errno = IMG_READERROR;
905               return img_BAD;
906            }
907         }
908         ch = GETC(pimg->fh);
909         if (ch == EOF) {
910            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
911            return img_BAD;
912         }
913         if (ch != 0xff) {
914            add = ch;
915         } else {
916            add = get32(pimg->fh);
917            if (ferror(pimg->fh)) {
918               img_errno = IMG_READERROR;
919               return img_BAD;
920            }
921         }
922      }
923
924      if (add > del && !check_label_space(pimg, pimg->label_len + add - del + 1)) {
925         img_errno = IMG_OUTOFMEMORY;
926         return img_BAD;
927      }
928   }
929   if (del > pimg->label_len) {
930      img_errno = IMG_BADFORMAT;
931      return img_BAD;
932   }
933   pimg->label_len -= del;
934   q = pimg->label_buf + pimg->label_len;
935   pimg->label_len += add;
936   if (add && fread(q, add, 1, pimg->fh) != 1) {
937      img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
938      return img_BAD;
939   }
940   q[add] = '\0';
941   return 0;
942}
943
944static int img_read_item_new(img *pimg, img_point *p);
945static int img_read_item_v3to7(img *pimg, img_point *p);
946static int img_read_item_ancient(img *pimg, img_point *p);
947static int img_read_item_ascii_wrapper(img *pimg, img_point *p);
948static int img_read_item_ascii(img *pimg, img_point *p);
949
950int
951img_read_item(img *pimg, img_point *p)
952{
953   pimg->flags = 0;
954
955   if (pimg->version >= 8) {
956      return img_read_item_new(pimg, p);
957   } else if (pimg->version >= 3) {
958      return img_read_item_v3to7(pimg, p);
959   } else if (pimg->version >= 1) {
960      return img_read_item_ancient(pimg, p);
961   } else {
962      return img_read_item_ascii_wrapper(pimg, p);
963   }
964}
965
966int
967img_read_item_new(img *pimg, img_point *p)
968{
969   int result;
970   int opt;
971   pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
972   if (pimg->pending >= 0x40) {
973      if (pimg->pending == 256) {
974         pimg->pending = 0;
975         return img_XSECT_END;
976      }
977      *p = pimg->mv;
978      pimg->flags = (int)(pimg->pending) & 0x3f;
979      pimg->pending = 0;
980      return img_LINE;
981   }
982   again3: /* label to goto if we get a prefix, date, or lrud */
983   pimg->label = pimg->label_buf;
984   opt = GETC(pimg->fh);
985   if (opt == EOF) {
986      img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
987      return img_BAD;
988   }
989   if (opt >> 6 == 0) {
990      if (opt <= 4) {
991         if (opt == 0 && pimg->style == 0)
992            return img_STOP; /* end of data marker */
993         /* STYLE */
994         pimg->style = opt;
995         goto again3;
996      }
997      if (opt >= 0x10) {
998          switch (opt) {
999              case 0x10: { /* No date info */
1000#if IMG_API_VERSION == 0
1001                  pimg->date1 = pimg->date2 = 0;
1002#else /* IMG_API_VERSION == 1 */
1003                  pimg->days1 = pimg->days2 = -1;
1004#endif
1005                  break;
1006              }
1007              case 0x11: { /* Single date */
1008                  int days1 = (int)getu16(pimg->fh);
1009#if IMG_API_VERSION == 0
1010                  pimg->date2 = pimg->date1 = (days1 - 25567) * 86400;
1011#else /* IMG_API_VERSION == 1 */
1012                  pimg->days2 = pimg->days1 = days1;
1013#endif
1014                  break;
1015              }
1016              case 0x12: { /* Date range (short) */
1017                  int days1 = (int)getu16(pimg->fh);
1018                  int days2 = days1 + GETC(pimg->fh) + 1;
1019#if IMG_API_VERSION == 0
1020                  pimg->date1 = (days1 - 25567) * 86400;
1021                  pimg->date2 = (days2 - 25567) * 86400;
1022#else /* IMG_API_VERSION == 1 */
1023                  pimg->days1 = days1;
1024                  pimg->days2 = days2;
1025#endif
1026                  break;
1027              }
1028              case 0x13: { /* Date range (long) */
1029                  int days1 = (int)getu16(pimg->fh);
1030                  int days2 = (int)getu16(pimg->fh);
1031#if IMG_API_VERSION == 0
1032                  pimg->date1 = (days1 - 25567) * 86400;
1033                  pimg->date2 = (days2 - 25567) * 86400;
1034#else /* IMG_API_VERSION == 1 */
1035                  pimg->days1 = days1;
1036                  pimg->days2 = days2;
1037#endif
1038                  break;
1039              }
1040              case 0x1f: /* Error info */
1041                  pimg->n_legs = get32(pimg->fh);
1042                  pimg->length = get32(pimg->fh) / 100.0;
1043                  pimg->E = get32(pimg->fh) / 100.0;
1044                  pimg->H = get32(pimg->fh) / 100.0;
1045                  pimg->V = get32(pimg->fh) / 100.0;
1046                  return img_ERROR_INFO;
1047              case 0x30: case 0x31: /* LRUD */
1048              case 0x32: case 0x33: /* Big LRUD! */
1049                  if (read_v8label(pimg, 0, 0) == img_BAD) return img_BAD;
1050                  pimg->flags = (int)opt & 0x01;
1051                  if (opt < 0x32) {
1052                      pimg->l = get16(pimg->fh) / 100.0;
1053                      pimg->r = get16(pimg->fh) / 100.0;
1054                      pimg->u = get16(pimg->fh) / 100.0;
1055                      pimg->d = get16(pimg->fh) / 100.0;
1056                  } else {
1057                      pimg->l = get32(pimg->fh) / 100.0;
1058                      pimg->r = get32(pimg->fh) / 100.0;
1059                      pimg->u = get32(pimg->fh) / 100.0;
1060                      pimg->d = get32(pimg->fh) / 100.0;
1061                  }
1062                  if (pimg->survey_len) {
1063                      size_t l = pimg->survey_len;
1064                      const char *s = pimg->label_buf;
1065                      if (strncmp(pimg->survey, s, l + 1) != 0) {
1066                          return img_XSECT_END;
1067                      }
1068                      pimg->label += l;
1069                      /* skip the dot if there */
1070                      if (*pimg->label) pimg->label++;
1071                  }
1072                  /* If this is the last cross-section in this passage, set
1073                   * pending so we return img_XSECT_END next time. */
1074                  if (pimg->flags & 0x01) {
1075                      pimg->pending = 256;
1076                      pimg->flags &= ~0x01;
1077                  }
1078                  return img_XSECT;
1079              default: /* 0x25 - 0x2f and 0x34 - 0x3f are currently unallocated. */
1080                  img_errno = IMG_BADFORMAT;
1081                  return img_BAD;
1082          }
1083          goto again3;
1084      }
1085      if (opt != 15) {
1086         /* 1-14 and 16-31 reserved */
1087         img_errno = IMG_BADFORMAT;
1088         return img_BAD;
1089      }
1090      result = img_MOVE;
1091   } else if (opt >= 0x80) {
1092      if (read_v8label(pimg, 0, 0) == img_BAD) return img_BAD;
1093
1094      result = img_LABEL;
1095
1096      if (pimg->survey_len) {
1097         size_t l = pimg->survey_len;
1098         const char *s = pimg->label_buf;
1099         if (strncmp(pimg->survey, s, l + 1) != 0) {
1100            if (!skip_coord(pimg->fh)) return img_BAD;
1101            pimg->pending = 0;
1102            goto again3;
1103         }
1104         pimg->label += l;
1105         /* skip the dot if there */
1106         if (*pimg->label) pimg->label++;
1107      }
1108
1109      pimg->flags = (int)opt & 0x7f;
1110   } else if ((opt >> 6) == 1) {
1111      if (read_v8label(pimg, opt & 0x20, 0) == img_BAD) return img_BAD;
1112
1113      result = img_LINE;
1114
1115      if (pimg->survey_len) {
1116         size_t l = pimg->survey_len;
1117         const char *s = pimg->label_buf;
1118         if (strncmp(pimg->survey, s, l) != 0 ||
1119             !(s[l] == '.' || s[l] == '\0')) {
1120            if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
1121            pimg->pending = 15;
1122            goto again3;
1123         }
1124         pimg->label += l;
1125         /* skip the dot if there */
1126         if (*pimg->label) pimg->label++;
1127      }
1128
1129      if (pimg->pending) {
1130         *p = pimg->mv;
1131         if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
1132         pimg->pending = opt;
1133         return img_MOVE;
1134      }
1135      pimg->flags = (int)opt & 0x1f;
1136   } else {
1137      img_errno = IMG_BADFORMAT;
1138      return img_BAD;
1139   }
1140   if (!read_coord(pimg->fh, p)) return img_BAD;
1141   pimg->pending = 0;
1142   return result;
1143}
1144
1145int
1146img_read_item_v3to7(img *pimg, img_point *p)
1147{
1148   int result;
1149   int opt;
1150   pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
1151   if (pimg->pending == 256) {
1152      pimg->pending = 0;
1153      return img_XSECT_END;
1154   }
1155   if (pimg->pending >= 0x80) {
1156      *p = pimg->mv;
1157      pimg->flags = (int)(pimg->pending) & 0x3f;
1158      pimg->pending = 0;
1159      return img_LINE;
1160   }
1161   again3: /* label to goto if we get a prefix, date, or lrud */
1162   pimg->label = pimg->label_buf;
1163   opt = GETC(pimg->fh);
1164   if (opt == EOF) {
1165      img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1166      return img_BAD;
1167   }
1168   switch (opt >> 6) {
1169    case 0:
1170      if (opt == 0) {
1171         if (!pimg->label_len) return img_STOP; /* end of data marker */
1172         pimg->label_len = 0;
1173         goto again3;
1174      }
1175      if (opt < 15) {
1176         /* 1-14 mean trim that many levels from current prefix */
1177         int c;
1178         if (pimg->label_len <= 17) {
1179            /* zero prefix using "0" */
1180            img_errno = IMG_BADFORMAT;
1181            return img_BAD;
1182         }
1183         /* extra - 1 because label_len points to one past the end */
1184         c = pimg->label_len - 17 - 1;
1185         while (pimg->label_buf[c] != '.' || --opt > 0) {
1186            if (--c < 0) {
1187               /* zero prefix using "0" */
1188               img_errno = IMG_BADFORMAT;
1189               return img_BAD;
1190            }
1191         }
1192         c++;
1193         pimg->label_len = c;
1194         goto again3;
1195      }
1196      if (opt == 15) {
1197         result = img_MOVE;
1198         break;
1199      }
1200      if (opt >= 0x20) {
1201          switch (opt) {
1202              case 0x20: /* Single date */
1203                  if (pimg->version < 7) {
1204                      int date1 = get32(pimg->fh);
1205#if IMG_API_VERSION == 0
1206                      pimg->date2 = pimg->date1 = date1;
1207#else /* IMG_API_VERSION == 1 */
1208                      if (date1 != 0) {
1209                          pimg->days2 = pimg->days1 = (date1 / 86400) + 25567;
1210                      } else {
1211                          pimg->days2 = pimg->days1 = -1;
1212                      }
1213#endif
1214                  } else {
1215                      int days1 = (int)getu16(pimg->fh);
1216#if IMG_API_VERSION == 0
1217                      pimg->date2 = pimg->date1 = (days1 - 25567) * 86400;
1218#else /* IMG_API_VERSION == 1 */
1219                      pimg->days2 = pimg->days1 = days1;
1220#endif
1221                  }
1222                  break;
1223              case 0x21: /* Date range (short for v7+) */
1224                  if (pimg->version < 7) {
1225                      INT32_T date1 = get32(pimg->fh);
1226                      INT32_T date2 = get32(pimg->fh);
1227#if IMG_API_VERSION == 0
1228                      pimg->date1 = date1;
1229                      pimg->date2 = date2;
1230#else /* IMG_API_VERSION == 1 */
1231                      pimg->days1 = (date1 / 86400) + 25567;
1232                      pimg->days2 = (date2 / 86400) + 25567;
1233#endif
1234                  } else {
1235                      int days1 = (int)getu16(pimg->fh);
1236                      int days2 = days1 + GETC(pimg->fh) + 1;
1237#if IMG_API_VERSION == 0
1238                      pimg->date1 = (days1 - 25567) * 86400;
1239                      pimg->date2 = (days2 - 25567) * 86400;
1240#else /* IMG_API_VERSION == 1 */
1241                      pimg->days1 = days1;
1242                      pimg->days2 = days2;
1243#endif
1244                  }
1245                  break;
1246              case 0x22: /* Error info */
1247                  pimg->n_legs = get32(pimg->fh);
1248                  pimg->length = get32(pimg->fh) / 100.0;
1249                  pimg->E = get32(pimg->fh) / 100.0;
1250                  pimg->H = get32(pimg->fh) / 100.0;
1251                  pimg->V = get32(pimg->fh) / 100.0;
1252                  return img_ERROR_INFO;
1253              case 0x23: { /* v7+: Date range (long) */
1254                  if (pimg->version < 7) {
1255                      img_errno = IMG_BADFORMAT;
1256                      return img_BAD;
1257                  }
1258                  int days1 = (int)getu16(pimg->fh);
1259                  int days2 = (int)getu16(pimg->fh);
1260#if IMG_API_VERSION == 0
1261                  pimg->date1 = (days1 - 25567) * 86400;
1262                  pimg->date2 = (days2 - 25567) * 86400;
1263#else /* IMG_API_VERSION == 1 */
1264                  pimg->days1 = days1;
1265                  pimg->days2 = days2;
1266#endif
1267                  break;
1268              }
1269              case 0x24: { /* v7+: No date info */
1270#if IMG_API_VERSION == 0
1271                  pimg->date1 = pimg->date2 = 0;
1272#else /* IMG_API_VERSION == 1 */
1273                  pimg->days1 = pimg->days2 = -1;
1274#endif
1275                  break;
1276              }
1277              case 0x30: case 0x31: /* LRUD */
1278              case 0x32: case 0x33: /* Big LRUD! */
1279                  if (read_v3label(pimg) == img_BAD) return img_BAD;
1280                  pimg->flags = (int)opt & 0x01;
1281                  if (opt < 0x32) {
1282                      pimg->l = get16(pimg->fh) / 100.0;
1283                      pimg->r = get16(pimg->fh) / 100.0;
1284                      pimg->u = get16(pimg->fh) / 100.0;
1285                      pimg->d = get16(pimg->fh) / 100.0;
1286                  } else {
1287                      pimg->l = get32(pimg->fh) / 100.0;
1288                      pimg->r = get32(pimg->fh) / 100.0;
1289                      pimg->u = get32(pimg->fh) / 100.0;
1290                      pimg->d = get32(pimg->fh) / 100.0;
1291                  }
1292                  if (pimg->survey_len) {
1293                      size_t l = pimg->survey_len;
1294                      const char *s = pimg->label_buf;
1295                      if (strncmp(pimg->survey, s, l + 1) != 0) {
1296                          return img_XSECT_END;
1297                      }
1298                      pimg->label += l;
1299                      /* skip the dot if there */
1300                      if (*pimg->label) pimg->label++;
1301                  }
1302                  /* If this is the last cross-section in this passage, set
1303                   * pending so we return img_XSECT_END next time. */
1304                  if (pimg->flags & 0x01) {
1305                      pimg->pending = 256;
1306                      pimg->flags &= ~0x01;
1307                  }
1308                  return img_XSECT;
1309              default: /* 0x25 - 0x2f and 0x34 - 0x3f are currently unallocated. */
1310                  img_errno = IMG_BADFORMAT;
1311                  return img_BAD;
1312          }
1313          goto again3;
1314      }
1315      /* 16-31 mean remove (n - 15) characters from the prefix */
1316      /* zero prefix using 0 */
1317      if (pimg->label_len <= (size_t)(opt - 15)) {
1318         img_errno = IMG_BADFORMAT;
1319         return img_BAD;
1320      }
1321      pimg->label_len -= (opt - 15);
1322      goto again3;
1323    case 1:
1324      if (read_v3label(pimg) == img_BAD) return img_BAD;
1325
1326      result = img_LABEL;
1327
1328      if (pimg->survey_len) {
1329         size_t l = pimg->survey_len;
1330         const char *s = pimg->label_buf;
1331         if (strncmp(pimg->survey, s, l + 1) != 0) {
1332            if (!skip_coord(pimg->fh)) return img_BAD;
1333            pimg->pending = 0;
1334            goto again3;
1335         }
1336         pimg->label += l;
1337         /* skip the dot if there */
1338         if (*pimg->label) pimg->label++;
1339      }
1340
1341      pimg->flags = (int)opt & 0x3f;
1342      break;
1343    case 2:
1344      if (read_v3label(pimg) == img_BAD) return img_BAD;
1345
1346      result = img_LINE;
1347
1348      if (pimg->survey_len) {
1349         size_t l = pimg->survey_len;
1350         const char *s = pimg->label_buf;
1351         if (strncmp(pimg->survey, s, l) != 0 ||
1352             !(s[l] == '.' || s[l] == '\0')) {
1353            if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
1354            pimg->pending = 15;
1355            goto again3;
1356         }
1357         pimg->label += l;
1358         /* skip the dot if there */
1359         if (*pimg->label) pimg->label++;
1360      }
1361
1362      if (pimg->pending) {
1363         *p = pimg->mv;
1364         if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
1365         pimg->pending = opt;
1366         return img_MOVE;
1367      }
1368      pimg->flags = (int)opt & 0x3f;
1369      break;
1370    default:
1371      img_errno = IMG_BADFORMAT;
1372      return img_BAD;
1373   }
1374   if (!read_coord(pimg->fh, p)) return img_BAD;
1375   pimg->pending = 0;
1376   return result;
1377}
1378
1379int
1380img_read_item_ancient(img *pimg, img_point *p)
1381{
1382   int result;
1383   static long opt_lookahead = 0;
1384   static img_point pt = { 0.0, 0.0, 0.0 };
1385   long opt;
1386
1387   pimg->label = pimg->label_buf;
1388
1389   again: /* label to goto if we get a cross */
1390   pimg->label[0] = '\0';
1391
1392   if (pimg->version == 1) {
1393      if (opt_lookahead) {
1394         opt = opt_lookahead;
1395         opt_lookahead = 0;
1396      } else {
1397         opt = get32(pimg->fh);
1398      }
1399   } else {
1400      opt = GETC(pimg->fh);
1401   }
1402
1403   if (feof(pimg->fh)) {
1404      img_errno = IMG_BADFORMAT;
1405      return img_BAD;
1406   }
1407   if (ferror(pimg->fh)) {
1408      img_errno = IMG_READERROR;
1409      return img_BAD;
1410   }
1411
1412   switch (opt) {
1413    case -1: case 0:
1414      return img_STOP; /* end of data marker */
1415    case 1:
1416      /* skip coordinates */
1417      if (!skip_coord(pimg->fh)) {
1418         img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1419         return img_BAD;
1420      }
1421      goto again;
1422    case 2: case 3: {
1423      char *q;
1424      int ch;
1425      result = img_LABEL;
1426      ch = GETC(pimg->fh);
1427      if (ch == EOF) {
1428         img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1429         return img_BAD;
1430      }
1431      if (ch != '\\') ungetc(ch, pimg->fh);
1432      fgets(pimg->label_buf, 257, pimg->fh);
1433      if (feof(pimg->fh)) {
1434         img_errno = IMG_BADFORMAT;
1435         return img_BAD;
1436      }
1437      if (ferror(pimg->fh)) {
1438         img_errno = IMG_READERROR;
1439         return img_BAD;
1440      }
1441      q = pimg->label_buf + strlen(pimg->label_buf) - 1;
1442      if (*q != '\n') {
1443         img_errno = IMG_BADFORMAT;
1444         return img_BAD;
1445      }
1446      /* Ignore empty labels in some .3d files (caused by a bug) */
1447      if (q == pimg->label_buf) goto again;
1448      *q = '\0';
1449      pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
1450      if (opt == 2) goto done;
1451      break;
1452    }
1453    case 6: case 7: {
1454      long len;
1455      result = img_LABEL;
1456
1457      if (opt == 7)
1458         pimg->flags = GETC(pimg->fh);
1459      else
1460         pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
1461
1462      len = get32(pimg->fh);
1463
1464      if (feof(pimg->fh)) {
1465         img_errno = IMG_BADFORMAT;
1466         return img_BAD;
1467      }
1468      if (ferror(pimg->fh)) {
1469         img_errno = IMG_READERROR;
1470         return img_BAD;
1471      }
1472
1473      /* Ignore empty labels in some .3d files (caused by a bug) */
1474      if (len == 0) goto again;
1475      if (!check_label_space(pimg, len + 1)) {
1476         img_errno = IMG_OUTOFMEMORY;
1477         return img_BAD;
1478      }
1479      if (fread(pimg->label_buf, len, 1, pimg->fh) != 1) {
1480         img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1481         return img_BAD;
1482      }
1483      pimg->label_buf[len] = '\0';
1484      break;
1485    }
1486    case 4:
1487      result = img_MOVE;
1488      break;
1489    case 5:
1490      result = img_LINE;
1491      break;
1492    default:
1493      switch ((int)opt & 0xc0) {
1494       case 0x80:
1495         pimg->flags = (int)opt & 0x3f;
1496         result = img_LINE;
1497         break;
1498       case 0x40: {
1499         char *q;
1500         pimg->flags = (int)opt & 0x3f;
1501         result = img_LABEL;
1502         if (!fgets(pimg->label_buf, 257, pimg->fh)) {
1503            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1504            return img_BAD;
1505         }
1506         q = pimg->label_buf + strlen(pimg->label_buf) - 1;
1507         /* Ignore empty-labels in some .3d files (caused by a bug) */
1508         if (q == pimg->label_buf) goto again;
1509         if (*q != '\n') {
1510            img_errno = IMG_BADFORMAT;
1511            return img_BAD;
1512         }
1513         *q = '\0';
1514         break;
1515       }
1516       case 0xc0:
1517         /* use this for an extra leg or station flag if we need it */
1518       default:
1519         img_errno = IMG_BADFORMAT;
1520         return img_BAD;
1521      }
1522      break;
1523   }
1524
1525   if (!read_coord(pimg->fh, &pt)) return img_BAD;
1526
1527   if (result == img_LABEL && pimg->survey_len) {
1528      if (strncmp(pimg->label_buf, pimg->survey, pimg->survey_len + 1) != 0)
1529         goto again;
1530      pimg->label += pimg->survey_len + 1;
1531   }
1532
1533   done:
1534   *p = pt;
1535
1536   if (result == img_MOVE && pimg->version == 1) {
1537      /* peek at next code and see if it's an old-style label */
1538      opt_lookahead = get32(pimg->fh);
1539
1540      if (feof(pimg->fh)) {
1541         img_errno = IMG_BADFORMAT;
1542         return img_BAD;
1543      }
1544      if (ferror(pimg->fh)) {
1545         img_errno = IMG_READERROR;
1546         return img_BAD;
1547      }
1548
1549      if (opt_lookahead == 2) return img_read_item(pimg, p);
1550   }
1551
1552   return result;
1553}
1554
1555static int
1556img_read_item_ascii_wrapper(img *pimg, img_point *p)
1557{
1558   /* We need to set the default locale for fscanf() to work on
1559    * numbers with "." as decimal point. */
1560   int result;
1561   char * current_locale = my_strdup(setlocale(LC_NUMERIC, NULL));
1562   setlocale(LC_NUMERIC, "C");
1563   result = img_read_item_ascii(pimg, p);
1564   setlocale(LC_NUMERIC, current_locale);
1565   free(current_locale);
1566   return result;
1567}
1568
1569/* Handle all ASCII formats. */
1570static int
1571img_read_item_ascii(img *pimg, img_point *p)
1572{
1573   int result;
1574   pimg->label = pimg->label_buf;
1575   if (pimg->version == 0) {
1576      ascii_again:
1577      pimg->label[0] = '\0';
1578      if (feof(pimg->fh)) return img_STOP;
1579      if (pimg->pending) {
1580         pimg->pending = 0;
1581         result = img_LINE;
1582      } else {
1583         char cmd[7];
1584         /* Stop if nothing found */
1585         if (fscanf(pimg->fh, "%6s", cmd) < 1) return img_STOP;
1586         if (strcmp(cmd, "move") == 0)
1587            result = img_MOVE;
1588         else if (strcmp(cmd, "draw") == 0)
1589            result = img_LINE;
1590         else if (strcmp(cmd, "line") == 0) {
1591            /* set flag to indicate to process second triplet as LINE */
1592            pimg->pending = 1;
1593            result = img_MOVE;
1594         } else if (strcmp(cmd, "cross") == 0) {
1595            if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
1596               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1597               return img_BAD;
1598            }
1599            goto ascii_again;
1600         } else if (strcmp(cmd, "name") == 0) {
1601            size_t off = 0;
1602            int ch = GETC(pimg->fh);
1603            if (ch == ' ') ch = GETC(pimg->fh);
1604            while (ch != ' ') {
1605               if (ch == '\n' || ch == EOF) {
1606                  img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
1607                  return img_BAD;
1608               }
1609               if (off == pimg->buf_len) {
1610                  if (!check_label_space(pimg, pimg->buf_len * 2)) {
1611                     img_errno = IMG_OUTOFMEMORY;
1612                     return img_BAD;
1613                  }
1614               }
1615               pimg->label_buf[off++] = ch;
1616               ch = GETC(pimg->fh);
1617            }
1618            pimg->label_buf[off] = '\0';
1619
1620            pimg->label = pimg->label_buf;
1621            if (pimg->label[0] == '\\') pimg->label++;
1622
1623            pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1624
1625            result = img_LABEL;
1626         } else {
1627            img_errno = IMG_BADFORMAT;
1628            return img_BAD; /* unknown keyword */
1629         }
1630      }
1631
1632      if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
1633         img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
1634         return img_BAD;
1635      }
1636
1637      if (result == img_LABEL && pimg->survey_len) {
1638         if (strncmp(pimg->label, pimg->survey, pimg->survey_len + 1) != 0)
1639            goto ascii_again;
1640         pimg->label += pimg->survey_len + 1;
1641      }
1642
1643      return result;
1644   } else if (pimg->version == VERSION_SURVEX_POS) {
1645      /* Survex .pos file */
1646      int ch;
1647      size_t off;
1648      pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1649      againpos:
1650      off = 0;
1651      while (fscanf(pimg->fh, "(%lf,%lf,%lf )", &p->x, &p->y, &p->z) != 3) {
1652         if (ferror(pimg->fh)) {
1653            img_errno = IMG_READERROR;
1654            return img_BAD;
1655         }
1656         if (feof(pimg->fh)) return img_STOP;
1657         if (pimg->pending) {
1658            img_errno = IMG_BADFORMAT;
1659            return img_BAD;
1660         }
1661         pimg->pending = 1;
1662         /* ignore rest of line */
1663         do {
1664            ch = GETC(pimg->fh);
1665         } while (ch != '\n' && ch != '\r' && ch != EOF);
1666      }
1667
1668      pimg->label_buf[0] = '\0';
1669      do {
1670          ch = GETC(pimg->fh);
1671      } while (ch == ' ' || ch == '\t');
1672      if (ch == '\n' || ch == EOF) {
1673          /* If there's no label, set img_SFLAG_ANON. */
1674          pimg->flags |= img_SFLAG_ANON;
1675          return img_LABEL;
1676      }
1677      pimg->label_buf[0] = ch;
1678      off = 1;
1679      while (!feof(pimg->fh)) {
1680         if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
1681            img_errno = IMG_READERROR;
1682            return img_BAD;
1683         }
1684
1685         off += strlen(pimg->label_buf + off);
1686         if (off && pimg->label_buf[off - 1] == '\n') {
1687            pimg->label_buf[off - 1] = '\0';
1688            break;
1689         }
1690         if (!check_label_space(pimg, pimg->buf_len * 2)) {
1691            img_errno = IMG_OUTOFMEMORY;
1692            return img_BAD;
1693         }
1694      }
1695
1696      pimg->label = pimg->label_buf;
1697
1698      if (pimg->label[0] == '\\') pimg->label++;
1699
1700      if (pimg->survey_len) {
1701         size_t l = pimg->survey_len + 1;
1702         if (strncmp(pimg->survey, pimg->label, l) != 0) goto againpos;
1703         pimg->label += l;
1704      }
1705
1706      return img_LABEL;
1707   } else if (pimg->version == VERSION_COMPASS_PLT) {
1708      /* Compass .plt file */
1709      if (pimg->pending > 0) {
1710         /* -1 signals we've entered the first survey we want to
1711          * read, and need to fudge lots if the first action is 'D'...
1712          */
1713         /* pending MOVE or LINE */
1714         int r = pimg->pending - 4;
1715         pimg->pending = 0;
1716         pimg->flags = 0;
1717         pimg->label[pimg->label_len] = '\0';
1718         return r;
1719      }
1720
1721      while (1) {
1722         char *line;
1723         char *q;
1724         size_t len = 0;
1725         int ch = GETC(pimg->fh);
1726
1727         switch (ch) {
1728            case '\x1a': case EOF: /* Don't insist on ^Z at end of file */
1729               return img_STOP;
1730            case 'X': case 'F': case 'S':
1731               /* bounding boX (marks end of survey), Feature survey, or
1732                * new Section - skip to next survey */
1733               if (pimg->survey) return img_STOP;
1734skip_to_N:
1735               while (1) {
1736                  do {
1737                     ch = GETC(pimg->fh);
1738                  } while (ch != '\n' && ch != '\r' && ch != EOF);
1739                  while (ch == '\n' || ch == '\r') ch = GETC(pimg->fh);
1740                  if (ch == 'N') break;
1741                  if (ch == '\x1a' || ch == EOF) return img_STOP;
1742               }
1743               /* FALLTHRU */
1744            case 'N':
1745               line = getline_alloc(pimg->fh);
1746               if (!line) {
1747                  img_errno = IMG_OUTOFMEMORY;
1748                  return img_BAD;
1749               }
1750               while (line[len] > 32) ++len;
1751               if (pimg->label_len == 0) pimg->pending = -1;
1752               if (!check_label_space(pimg, len + 1)) {
1753                  osfree(line);
1754                  img_errno = IMG_OUTOFMEMORY;
1755                  return img_BAD;
1756               }
1757               pimg->label_len = len;
1758               pimg->label = pimg->label_buf;
1759               memcpy(pimg->label, line, len);
1760               pimg->label[len] = '\0';
1761               osfree(line);
1762               break;
1763            case 'M': case 'D': {
1764               /* Move or Draw */
1765               long fpos = -1;
1766               if (pimg->survey && pimg->label_len == 0) {
1767                  /* We're only holding onto this line in case the first line
1768                   * of the 'N' is a 'D', so skip it for now...
1769                   */
1770                  goto skip_to_N;
1771               }
1772               if (ch == 'D' && pimg->pending == -1) {
1773                  if (pimg->survey) {
1774                     fpos = ftell(pimg->fh) - 1;
1775                     fseek(pimg->fh, pimg->start, SEEK_SET);
1776                     ch = GETC(pimg->fh);
1777                     pimg->pending = 0;
1778                  } else {
1779                     /* If a file actually has a 'D' before any 'M', then
1780                      * pretend the 'D' is an 'M' - one of the examples
1781                      * in the docs was like this! */
1782                     ch = 'M';
1783                  }
1784               }
1785               line = getline_alloc(pimg->fh);
1786               if (!line) {
1787                  img_errno = IMG_OUTOFMEMORY;
1788                  return img_BAD;
1789               }
1790               /* Compass stores coordinates as North, East, Up = (y,x,z)! */
1791               if (sscanf(line, "%lf%lf%lf", &p->y, &p->x, &p->z) != 3) {
1792                  osfree(line);
1793                  if (ferror(pimg->fh)) {
1794                     img_errno = IMG_READERROR;
1795                  } else {
1796                     img_errno = IMG_BADFORMAT;
1797                  }
1798                  return img_BAD;
1799               }
1800               p->x *= METRES_PER_FOOT;
1801               p->y *= METRES_PER_FOOT;
1802               p->z *= METRES_PER_FOOT;
1803               q = strchr(line, 'S');
1804               if (!q) {
1805                  osfree(line);
1806                  img_errno = IMG_BADFORMAT;
1807                  return img_BAD;
1808               }
1809               ++q;
1810               len = 0;
1811               while (q[len] > ' ') ++len;
1812               q[len] = '\0';
1813               len += 2; /* ' ' and '\0' */
1814               if (!check_label_space(pimg, pimg->label_len + len)) {
1815                  img_errno = IMG_OUTOFMEMORY;
1816                  return img_BAD;
1817               }
1818               pimg->label = pimg->label_buf;
1819               if (pimg->label_len) {
1820                   pimg->label[pimg->label_len] = ' ';
1821                   memcpy(pimg->label + pimg->label_len + 1, q, len - 1);
1822               } else {
1823                   memcpy(pimg->label, q, len - 1);
1824               }
1825               q += len - 1;
1826               /* Now read LRUD.  Technically, this is optional but virtually
1827                * all PLT files have it (with dummy negative values if no LRUD
1828                * was measured) and some versions of Compass can't read PLT
1829                * files without it!
1830                */
1831               while (*q && *q <= ' ') q++;
1832               if (*q == 'P') {
1833                   if (sscanf(q + 1, "%lf%lf%lf%lf",
1834                              &pimg->l, &pimg->r, &pimg->u, &pimg->d) != 4) {
1835                       osfree(line);
1836                       if (ferror(pimg->fh)) {
1837                           img_errno = IMG_READERROR;
1838                       } else {
1839                           img_errno = IMG_BADFORMAT;
1840                       }
1841                       return img_BAD;
1842                   }
1843                   pimg->l *= METRES_PER_FOOT;
1844                   pimg->r *= METRES_PER_FOOT;
1845                   pimg->u *= METRES_PER_FOOT;
1846                   pimg->d *= METRES_PER_FOOT;
1847               } else {
1848                   pimg->l = pimg->r = pimg->u = pimg->d = -1;
1849               }
1850               osfree(line);
1851               pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1852               if (fpos != -1) {
1853                  fseek(pimg->fh, fpos, SEEK_SET);
1854               } else {
1855                  pimg->pending = (ch == 'M' ? img_MOVE : img_LINE) + 4;
1856               }
1857               return img_LABEL;
1858            }
1859            default:
1860               img_errno = IMG_BADFORMAT;
1861               return img_BAD;
1862         }
1863      }
1864   } else {
1865      /* CMAP .xyz file */
1866      char *line = NULL;
1867      char *q;
1868      size_t len;
1869
1870      if (pimg->pending) {
1871         /* pending MOVE or LINE or LABEL or STOP */
1872         int r = pimg->pending - 4;
1873         /* Set label to empty - don't use "" as we adjust label relative
1874          * to label_buf when label_buf is reallocated. */
1875         pimg->label = pimg->label_buf + strlen(pimg->label_buf);
1876         pimg->flags = 0;
1877         if (r == img_LABEL) {
1878            /* nasty magic */
1879            read_xyz_shot_coords(p, pimg->label_buf + 16);
1880            subtract_xyz_shot_deltas(p, pimg->label_buf + 16);
1881            pimg->pending = img_STOP + 4;
1882            return img_MOVE;
1883         }
1884
1885         pimg->pending = 0;
1886
1887         if (r == img_STOP) {
1888            /* nasty magic */
1889            read_xyz_shot_coords(p, pimg->label_buf + 16);
1890            return img_LINE;
1891         }
1892
1893         return r;
1894      }
1895
1896      pimg->label = pimg->label_buf;
1897      do {
1898         osfree(line);
1899         if (feof(pimg->fh)) return img_STOP;
1900         line = getline_alloc(pimg->fh);
1901         if (!line) {
1902            img_errno = IMG_OUTOFMEMORY;
1903            return img_BAD;
1904         }
1905      } while (line[0] == ' ' || line[0] == '\0');
1906      if (line[0] == '\x1a') return img_STOP;
1907
1908      len = strlen(line);
1909      if (pimg->version == VERSION_CMAP_STATION) {
1910         /* station variant */
1911         if (len < 37) {
1912            osfree(line);
1913            img_errno = IMG_BADFORMAT;
1914            return img_BAD;
1915         }
1916         memcpy(pimg->label, line, 6);
1917         q = (char *)memchr(pimg->label, ' ', 6);
1918         if (!q) q = pimg->label + 6;
1919         *q = '\0';
1920
1921         read_xyz_station_coords(p, line);
1922
1923         /* FIXME: look at prev for lines (line + 32, 5) */
1924         /* FIXME: duplicate stations... */
1925         return img_LABEL;
1926      } else {
1927         /* Shot variant */
1928         char old[8], new_[8];
1929         if (len < 61) {
1930            osfree(line);
1931            img_errno = IMG_BADFORMAT;
1932            return img_BAD;
1933         }
1934
1935         memcpy(old, line, 7);
1936         q = (char *)memchr(old, ' ', 7);
1937         if (!q) q = old + 7;
1938         *q = '\0';
1939
1940         memcpy(new_, line + 7, 7);
1941         q = (char *)memchr(new_, ' ', 7);
1942         if (!q) q = new_ + 7;
1943         *q = '\0';
1944
1945         pimg->flags = img_SFLAG_UNDERGROUND;
1946
1947         if (strcmp(old, new_) == 0) {
1948            pimg->pending = img_MOVE + 4;
1949            read_xyz_shot_coords(p, line);
1950            strcpy(pimg->label, new_);
1951            osfree(line);
1952            return img_LABEL;
1953         }
1954
1955         if (strcmp(old, pimg->label) == 0) {
1956            pimg->pending = img_LINE + 4;
1957            read_xyz_shot_coords(p, line);
1958            strcpy(pimg->label, new_);
1959            osfree(line);
1960            return img_LABEL;
1961         }
1962
1963         pimg->pending = img_LABEL + 4;
1964         read_xyz_shot_coords(p, line);
1965         strcpy(pimg->label, new_);
1966         memcpy(pimg->label + 16, line, 70);
1967
1968         osfree(line);
1969         return img_LABEL;
1970      }
1971   }
1972}
1973
1974static void
1975write_coord(FILE *fh, double x, double y, double z)
1976{
1977   SVX_ASSERT(fh);
1978   /* Output in cm */
1979   static INT32_T X_, Y_, Z_;
1980   INT32_T X = my_lround(x * 100.0);
1981   INT32_T Y = my_lround(y * 100.0);
1982   INT32_T Z = my_lround(z * 100.0);
1983
1984   X_ -= X;
1985   Y_ -= Y;
1986   Z_ -= Z;
1987   put32(X, fh);
1988   put32(Y, fh);
1989   put32(Z, fh);
1990   X_ = X; Y_ = Y; Z_ = Z;
1991}
1992
1993static int
1994write_v3label(img *pimg, int opt, const char *s)
1995{
1996   size_t len, n, dot;
1997
1998   /* find length of common prefix */
1999   dot = 0;
2000   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
2001      if (s[len] == '.') dot = len + 1;
2002   }
2003
2004   SVX_ASSERT(len <= pimg->label_len);
2005   n = pimg->label_len - len;
2006   if (len == 0) {
2007      if (pimg->label_len) PUTC(0, pimg->fh);
2008   } else if (n <= 16) {
2009      if (n) PUTC(n + 15, pimg->fh);
2010   } else if (dot == 0) {
2011      if (pimg->label_len) PUTC(0, pimg->fh);
2012      len = 0;
2013   } else {
2014      const char *p = pimg->label_buf + dot;
2015      n = 1;
2016      for (len = pimg->label_len - dot - 17; len; len--) {
2017         if (*p++ == '.') n++;
2018      }
2019      if (n <= 14) {
2020         PUTC(n, pimg->fh);
2021         len = dot;
2022      } else {
2023         if (pimg->label_len) PUTC(0, pimg->fh);
2024         len = 0;
2025      }
2026   }
2027
2028   n = strlen(s + len);
2029   PUTC(opt, pimg->fh);
2030   if (n < 0xfe) {
2031      PUTC(n, pimg->fh);
2032   } else if (n < 0xffff + 0xfe) {
2033      PUTC(0xfe, pimg->fh);
2034      put16((short)(n - 0xfe), pimg->fh);
2035   } else {
2036      PUTC(0xff, pimg->fh);
2037      put32(n, pimg->fh);
2038   }
2039   fwrite(s + len, n, 1, pimg->fh);
2040
2041   n += len;
2042   pimg->label_len = n;
2043   if (!check_label_space(pimg, n + 1))
2044      return 0; /* FIXME: distinguish out of memory... */
2045   memcpy(pimg->label_buf + len, s + len, n - len + 1);
2046
2047   return !ferror(pimg->fh);
2048}
2049
2050static int
2051write_v8label(img *pimg, int opt, int common_flag, size_t common_val,
2052              const char *s)
2053{
2054   size_t len, del, add;
2055
2056   /* find length of common prefix */
2057   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
2058   }
2059
2060   SVX_ASSERT(len <= pimg->label_len);
2061   del = pimg->label_len - len;
2062   add = strlen(s + len);
2063
2064   if (add == common_val && del == common_val) {
2065      PUTC(opt | common_flag, pimg->fh);
2066   } else {
2067      PUTC(opt, pimg->fh);
2068      if (del <= 15 && add <= 15 && (del || add)) {
2069         PUTC((del << 4) | add, pimg->fh);
2070      } else {
2071         PUTC(0x00, pimg->fh);
2072         if (del < 0xff) {
2073            PUTC(del, pimg->fh);
2074         } else {
2075            PUTC(0xff, pimg->fh);
2076            put32(del, pimg->fh);
2077         }
2078         if (add < 0xff) {
2079            PUTC(add, pimg->fh);
2080         } else {
2081            PUTC(0xff, pimg->fh);
2082            put32(add, pimg->fh);
2083         }
2084      }
2085   }
2086
2087   if (add)
2088      fwrite(s + len, add, 1, pimg->fh);
2089
2090   pimg->label_len = len + add;
2091   if (add > del && !check_label_space(pimg, pimg->label_len + 1))
2092      return 0; /* FIXME: distinguish out of memory... */
2093
2094   memcpy(pimg->label_buf + len, s + len, add + 1);
2095
2096   return !ferror(pimg->fh);
2097}
2098
2099static void
2100img_write_item_date_new(img *pimg)
2101{
2102    int same, unset;
2103    /* Only write dates when they've changed. */
2104#if IMG_API_VERSION == 0
2105    if (pimg->date1 == pimg->olddate1 && pimg->date2 == pimg->olddate2)
2106        return;
2107
2108    same = (pimg->date1 == pimg->date2);
2109    unset = (pimg->date1 == 0);
2110#else /* IMG_API_VERSION == 1 */
2111    if (pimg->days1 == pimg->olddays1 && pimg->days2 == pimg->olddays2)
2112        return;
2113
2114    same = (pimg->days1 == pimg->days2);
2115    unset = (pimg->days1 == -1);
2116#endif
2117
2118    if (same) {
2119        if (unset) {
2120            PUTC(0x10, pimg->fh);
2121        } else {
2122            PUTC(0x11, pimg->fh);
2123#if IMG_API_VERSION == 0
2124            put16(pimg->date1 / 86400 + 25567, pimg->fh);
2125#else /* IMG_API_VERSION == 1 */
2126            put16(pimg->days1, pimg->fh);
2127#endif
2128        }
2129    } else {
2130#if IMG_API_VERSION == 0
2131        int diff = (pimg->date2 - pimg->date1) / 86400;
2132        if (diff > 0 && diff <= 256) {
2133            PUTC(0x12, pimg->fh);
2134            put16(pimg->date1 / 86400 + 25567, pimg->fh);
2135            PUTC(diff - 1, pimg->fh);
2136        } else {
2137            PUTC(0x13, pimg->fh);
2138            put16(pimg->date1 / 86400 + 25567, pimg->fh);
2139            put16(pimg->date2 / 86400 + 25567, pimg->fh);
2140        }
2141#else /* IMG_API_VERSION == 1 */
2142        int diff = pimg->days2 - pimg->days1;
2143        if (diff > 0 && diff <= 256) {
2144            PUTC(0x12, pimg->fh);
2145            put16(pimg->days1, pimg->fh);
2146            PUTC(diff - 1, pimg->fh);
2147        } else {
2148            PUTC(0x13, pimg->fh);
2149            put16(pimg->days1, pimg->fh);
2150            put16(pimg->days2, pimg->fh);
2151        }
2152#endif
2153    }
2154#if IMG_API_VERSION == 0
2155    pimg->olddate1 = pimg->date1;
2156    pimg->olddate2 = pimg->date2;
2157#else /* IMG_API_VERSION == 1 */
2158    pimg->olddays1 = pimg->days1;
2159    pimg->olddays2 = pimg->days2;
2160#endif
2161}
2162
2163static void
2164img_write_item_date(img *pimg)
2165{
2166    int same, unset;
2167    /* Only write dates when they've changed. */
2168#if IMG_API_VERSION == 0
2169    if (pimg->date1 == pimg->olddate1 && pimg->date2 == pimg->olddate2)
2170        return;
2171
2172    same = (pimg->date1 == pimg->date2);
2173    unset = (pimg->date1 == 0);
2174#else /* IMG_API_VERSION == 1 */
2175    if (pimg->days1 == pimg->olddays1 && pimg->days2 == pimg->olddays2)
2176        return;
2177
2178    same = (pimg->days1 == pimg->days2);
2179    unset = (pimg->days1 == -1);
2180#endif
2181
2182    if (same) {
2183        if (img_output_version < 7) {
2184            PUTC(0x20, pimg->fh);
2185#if IMG_API_VERSION == 0
2186            put32(pimg->date1, pimg->fh);
2187#else /* IMG_API_VERSION == 1 */
2188            put32((pimg->days1 - 25567) * 86400, pimg->fh);
2189#endif
2190        } else {
2191            if (unset) {
2192                PUTC(0x24, pimg->fh);
2193            } else {
2194                PUTC(0x20, pimg->fh);
2195#if IMG_API_VERSION == 0
2196                put16(pimg->date1 / 86400 + 25567, pimg->fh);
2197#else /* IMG_API_VERSION == 1 */
2198                put16(pimg->days1, pimg->fh);
2199#endif
2200            }
2201        }
2202    } else {
2203        if (img_output_version < 7) {
2204            PUTC(0x21, pimg->fh);
2205#if IMG_API_VERSION == 0
2206            put32(pimg->date1, pimg->fh);
2207            put32(pimg->date2, pimg->fh);
2208#else /* IMG_API_VERSION == 1 */
2209            put32((pimg->days1 - 25567) * 86400, pimg->fh);
2210            put32((pimg->days2 - 25567) * 86400, pimg->fh);
2211#endif
2212        } else {
2213#if IMG_API_VERSION == 0
2214            int diff = (pimg->date2 - pimg->date1) / 86400;
2215            if (diff > 0 && diff <= 256) {
2216                PUTC(0x21, pimg->fh);
2217                put16(pimg->date1 / 86400 + 25567, pimg->fh);
2218                PUTC(diff - 1, pimg->fh);
2219            } else {
2220                PUTC(0x23, pimg->fh);
2221                put16(pimg->date1 / 86400 + 25567, pimg->fh);
2222                put16(pimg->date2 / 86400 + 25567, pimg->fh);
2223            }
2224#else /* IMG_API_VERSION == 1 */
2225            int diff = pimg->days2 - pimg->days1;
2226            if (diff > 0 && diff <= 256) {
2227                PUTC(0x21, pimg->fh);
2228                put16(pimg->days1, pimg->fh);
2229                PUTC(diff - 1, pimg->fh);
2230            } else {
2231                PUTC(0x23, pimg->fh);
2232                put16(pimg->days1, pimg->fh);
2233                put16(pimg->days2, pimg->fh);
2234            }
2235#endif
2236        }
2237    }
2238#if IMG_API_VERSION == 0
2239    pimg->olddate1 = pimg->date1;
2240    pimg->olddate2 = pimg->date2;
2241#else /* IMG_API_VERSION == 1 */
2242    pimg->olddays1 = pimg->days1;
2243    pimg->olddays2 = pimg->days2;
2244#endif
2245}
2246
2247static void
2248img_write_item_new(img *pimg, int code, int flags, const char *s,
2249                   double x, double y, double z);
2250static void
2251img_write_item_v3to7(img *pimg, int code, int flags, const char *s,
2252                     double x, double y, double z);
2253static void
2254img_write_item_ancient(img *pimg, int code, int flags, const char *s,
2255                       double x, double y, double z);
2256
2257void
2258img_write_item(img *pimg, int code, int flags, const char *s,
2259               double x, double y, double z)
2260{
2261   if (!pimg) return;
2262   if (pimg->version >= 8) {
2263      img_write_item_new(pimg, code, flags, s, x, y, z);
2264   } else if (pimg->version >= 3) {
2265      img_write_item_v3to7(pimg, code, flags, s, x, y, z);
2266   } else {
2267      img_write_item_ancient(pimg, code, flags, s, x, y, z);
2268   }
2269}
2270
2271static void
2272img_write_item_new(img *pimg, int code, int flags, const char *s,
2273                   double x, double y, double z)
2274{
2275   switch (code) {
2276    case img_LABEL:
2277      write_v8label(pimg, 0x80 | flags, 0, -1, s);
2278      break;
2279    case img_XSECT: {
2280      INT32_T l, r, u, d, max_dim;
2281      img_write_item_date_new(pimg);
2282      l = (INT32_T)my_lround(pimg->l * 100.0);
2283      r = (INT32_T)my_lround(pimg->r * 100.0);
2284      u = (INT32_T)my_lround(pimg->u * 100.0);
2285      d = (INT32_T)my_lround(pimg->d * 100.0);
2286      if (l < 0) l = -1;
2287      if (r < 0) r = -1;
2288      if (u < 0) u = -1;
2289      if (d < 0) d = -1;
2290      max_dim = max(max(l, r), max(u, d));
2291      flags = (flags & img_XFLAG_END) ? 1 : 0;
2292      if (max_dim >= 32768) flags |= 2;
2293      write_v8label(pimg, 0x30 | flags, 0, -1, s);
2294      if (flags & 2) {
2295         /* Big passage!  Need to use 4 bytes. */
2296         put32(l, pimg->fh);
2297         put32(r, pimg->fh);
2298         put32(u, pimg->fh);
2299         put32(d, pimg->fh);
2300      } else {
2301         put16(l, pimg->fh);
2302         put16(r, pimg->fh);
2303         put16(u, pimg->fh);
2304         put16(d, pimg->fh);
2305      }
2306      return;
2307    }
2308    case img_MOVE:
2309      PUTC(15, pimg->fh);
2310      break;
2311    case img_LINE:
2312      img_write_item_date_new(pimg);
2313      if (pimg->style != pimg->oldstyle) {
2314          switch (pimg->style) {
2315            case img_STYLE_NORMAL:
2316            case img_STYLE_DIVING:
2317            case img_STYLE_CARTESIAN:
2318            case img_STYLE_CYLPOLAR:
2319            case img_STYLE_NOSURVEY:
2320               PUTC(pimg->style, pimg->fh);
2321               break;
2322          }
2323          pimg->oldstyle = pimg->style;
2324      }
2325      write_v8label(pimg, 0x40 | flags, 0x20, 0x00, s ? s : "");
2326      break;
2327    default: /* ignore for now */
2328      return;
2329   }
2330   write_coord(pimg->fh, x, y, z);
2331}
2332
2333static void
2334img_write_item_v3to7(img *pimg, int code, int flags, const char *s,
2335                     double x, double y, double z)
2336{
2337   switch (code) {
2338    case img_LABEL:
2339      write_v3label(pimg, 0x40 | flags, s);
2340      break;
2341    case img_XSECT: {
2342      INT32_T l, r, u, d, max_dim;
2343      /* Need at least version 5 for img_XSECT. */
2344      if (pimg->version < 5) return;
2345      img_write_item_date(pimg);
2346      l = (INT32_T)my_lround(pimg->l * 100.0);
2347      r = (INT32_T)my_lround(pimg->r * 100.0);
2348      u = (INT32_T)my_lround(pimg->u * 100.0);
2349      d = (INT32_T)my_lround(pimg->d * 100.0);
2350      if (l < 0) l = -1;
2351      if (r < 0) r = -1;
2352      if (u < 0) u = -1;
2353      if (d < 0) d = -1;
2354      max_dim = max(max(l, r), max(u, d));
2355      flags = (flags & img_XFLAG_END) ? 1 : 0;
2356      if (max_dim >= 32768) flags |= 2;
2357      write_v3label(pimg, 0x30 | flags, s);
2358      if (flags & 2) {
2359         /* Big passage!  Need to use 4 bytes. */
2360         put32(l, pimg->fh);
2361         put32(r, pimg->fh);
2362         put32(u, pimg->fh);
2363         put32(d, pimg->fh);
2364      } else {
2365         put16(l, pimg->fh);
2366         put16(r, pimg->fh);
2367         put16(u, pimg->fh);
2368         put16(d, pimg->fh);
2369      }
2370      return;
2371    }
2372    case img_MOVE:
2373      PUTC(15, pimg->fh);
2374      break;
2375    case img_LINE:
2376      if (pimg->version >= 4) {
2377          img_write_item_date(pimg);
2378      }
2379      write_v3label(pimg, 0x80 | flags, s ? s : "");
2380      break;
2381    default: /* ignore for now */
2382      return;
2383   }
2384   write_coord(pimg->fh, x, y, z);
2385}
2386
2387static void
2388img_write_item_ancient(img *pimg, int code, int flags, const char *s,
2389                       double x, double y, double z)
2390{
2391   size_t len;
2392   INT32_T opt = 0;
2393   SVX_ASSERT(pimg->version > 0);
2394   switch (code) {
2395    case img_LABEL:
2396      if (pimg->version == 1) {
2397         /* put a move before each label */
2398         img_write_item(pimg, img_MOVE, 0, NULL, x, y, z);
2399         put32(2, pimg->fh);
2400         fputsnl(s, pimg->fh);
2401         return;
2402      }
2403      len = strlen(s);
2404      if (len > 255 || strchr(s, '\n')) {
2405         /* long label - not in early incarnations of v2 format, but few
2406          * 3d files will need these, so better not to force incompatibility
2407          * with a new version I think... */
2408         PUTC(7, pimg->fh);
2409         PUTC(flags, pimg->fh);
2410         put32(len, pimg->fh);
2411         fputs(s, pimg->fh);
2412      } else {
2413         PUTC(0x40 | (flags & 0x3f), pimg->fh);
2414         fputsnl(s, pimg->fh);
2415      }
2416      opt = 0;
2417      break;
2418    case img_MOVE:
2419      opt = 4;
2420      break;
2421    case img_LINE:
2422      if (pimg->version > 1) {
2423         opt = 0x80 | (flags & 0x3f);
2424         break;
2425      }
2426      opt = 5;
2427      break;
2428    default: /* ignore for now */
2429      return;
2430   }
2431   if (pimg->version == 1) {
2432      put32(opt, pimg->fh);
2433   } else {
2434      if (opt) PUTC(opt, pimg->fh);
2435   }
2436   write_coord(pimg->fh, x, y, z);
2437}
2438
2439/* Write error information for the current traverse
2440 * n_legs is the number of legs in the traverse
2441 * length is the traverse length (in m)
2442 * E is the ratio of the observed misclosure to the theoretical one
2443 * H is the ratio of the observed horizontal misclosure to the theoretical one
2444 * V is the ratio of the observed vertical misclosure to the theoretical one
2445 */
2446void
2447img_write_errors(img *pimg, int n_legs, double length,
2448                 double E, double H, double V)
2449{
2450    PUTC((pimg->version >= 8 ? 0x1f : 0x22), pimg->fh);
2451    put32(n_legs, pimg->fh);
2452    put32((INT32_T)my_lround(length * 100.0), pimg->fh);
2453    put32((INT32_T)my_lround(E * 100.0), pimg->fh);
2454    put32((INT32_T)my_lround(H * 100.0), pimg->fh);
2455    put32((INT32_T)my_lround(V * 100.0), pimg->fh);
2456}
2457
2458int
2459img_close(img *pimg)
2460{
2461   int result = 1;
2462   if (pimg) {
2463      if (pimg->fh) {
2464         if (pimg->fRead) {
2465            osfree(pimg->survey);
2466            osfree(pimg->title);
2467            osfree(pimg->datestamp);
2468         } else {
2469            /* write end of data marker */
2470            switch (pimg->version) {
2471             case 1:
2472               put32((INT32_T)-1, pimg->fh);
2473               break;
2474             default:
2475               if (pimg->version <= 7 ?
2476                   (pimg->label_len != 0) :
2477                   (pimg->style != img_STYLE_NORMAL)) {
2478                  PUTC(0, pimg->fh);
2479               }
2480               /* FALL THROUGH */
2481             case 2:
2482               PUTC(0, pimg->fh);
2483               break;
2484            }
2485         }
2486         if (ferror(pimg->fh)) result = 0;
2487         if (fclose(pimg->fh)) result = 0;
2488         if (!result) img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
2489      }
2490      osfree(pimg->label_buf);
2491      osfree(pimg->filename_opened);
2492      osfree(pimg);
2493   }
2494   return result;
2495}
Note: See TracBrowser for help on using the repository browser.