source: git/src/img.c @ f1d5d26

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-datawalls-data-hanging-as-warning
Last change on this file since f1d5d26 was 55a0527, checked in by Olly Betts <olly@…>, 11 years ago

src/img.c,src/netbits.c: Fix comments in C code for portability
to pre-C99 compilers which don't support these as an extension.

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