source: git/src/img.c @ a72ed95

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

src/img.c,src/img.h: Add ability to store a proj string describing
the coordinate system in use in 3d v8 files.

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