source: git/src/img.c @ 7826201

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

Factor out code to restrict to a survey prefix

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