source: git/src/img.c @ 8be4d09

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

aven: Fix loading 3d files with non-ASCII filenames

On Microsoft Windows such files were failing to load. The fix extends
the img library to support loading survey data from an already open
stream, so is useful for other programs using this library.

Issue reported by Matic.

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