source: git/src/img.c @ c891a7e

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

configure.ac,src/img.c: Only use _putenv_s() under MSVC - mingw
doesn't provide it.

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