source: git/src/img.c @ ad95991

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

src/img.c: Fix my_strcasecmp() to handle top-bit set characters
better.

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