source: git/src/img.c @ 17edec7

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

src/img.c: In non-hosted mode, check that int is at least 32 bits,
and if not, use long. In practice, platforms with 16 bit int are
mostly obsolete, but it's not hard to be portable here.

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