source: git/src/img.c @ d9cd8ae

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

src/img.c,src/mainfrm.cc: Although these are often referred to as
"CMAP .XYZ files", it seems that actually, the extension .XYZ isn't
used, rather .SHT (shot variant, produced by CMAP v16 and later),
.UNA (unadjusted) and .ADJ (adjusted) extensions are. Since we've
long checked for .XYZ, we continue to do so in case anyone is
relying on it, but also check for the other extensions.

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