source: git/src/img.c @ 1c80bef

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-datawalls-data-hanging-as-warning
Last change on this file since 1c80bef was 1908f42, checked in by Olly Betts <olly@…>, 7 years ago

img: Fix extracting leaf survey name for title

When there are three or more levels of survey, we were taking
everything after the first dot rather than everything after the last
dot.

  • 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,2017 Olly Betts
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#include <ctype.h>
25#include <errno.h>
26#include <limits.h>
27#include <locale.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <time.h>
32
33#include "img.h"
34
35#define TIMENA "?"
36#ifdef IMG_HOSTED
37# define INT32_T int32_t
38# include "debug.h"
39# include "filelist.h"
40# include "filename.h"
41# include "message.h"
42# include "useful.h"
43# define TIMEFMT msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107)
44#else
45# ifdef HAVE_STDINT_H
46#  include <stdint.h>
47#  define INT32_T int32_t
48# else
49#  include <limits.h>
50#  if INT_MAX >= 2147483647
51#   define INT32_T int
52#  else
53#   define INT32_T long
54#  endif
55# endif
56# define TIMEFMT "%a,%Y.%m.%d %H:%M:%S %Z"
57# define EXT_SVX_3D "3d"
58# define EXT_SVX_POS "pos"
59# define FNM_SEP_EXT '.'
60# define METRES_PER_FOOT 0.3048 /* exact value */
61# define xosmalloc(L) malloc((L))
62# define xosrealloc(L,S) realloc((L),(S))
63# define osfree(P) free((P))
64# define osnew(T) (T*)malloc(sizeof(T))
65
66/* in IMG_HOSTED mode, this tests if a filename refers to a directory */
67# define fDirectory(X) 0
68/* open file FNM with mode MODE, maybe using path PTH and/or extension EXT */
69/* path isn't used in img.c, but EXT is */
70# define fopenWithPthAndExt(PTH,FNM,EXT,MODE,X) \
71    ((*(X) = NULL), fopen(FNM,MODE))
72# ifndef PUTC
73#  define PUTC(C, FH) putc(C, FH)
74# endif
75# ifndef GETC
76#  define GETC(FH) getc(FH)
77# endif
78# define fputsnl(S, FH) (fputs((S), (FH)) == EOF ? EOF : putc('\n', (FH)))
79# define SVX_ASSERT(X)
80
81#ifdef __cplusplus
82# include <algorithm>
83using std::max;
84using std::min;
85#else
86/* Return max/min of two numbers. */
87/* May be defined already (e.g. by Borland C in stdlib.h) */
88/* NB Bad news if X or Y has side-effects... */
89# ifndef max
90#  define max(X, Y) ((X) > (Y) ? (X) : (Y))
91# endif
92# ifndef min
93#  define min(X, Y) ((X) < (Y) ? (X) : (Y))
94# endif
95#endif
96
97static INT32_T
98get32(FILE *fh)
99{
100   INT32_T w = GETC(fh);
101   w |= (INT32_T)GETC(fh) << 8l;
102   w |= (INT32_T)GETC(fh) << 16l;
103   w |= (INT32_T)GETC(fh) << 24l;
104   return w;
105}
106
107static void
108put32(long w, FILE *fh)
109{
110   PUTC((char)(w), fh);
111   PUTC((char)(w >> 8l), fh);
112   PUTC((char)(w >> 16l), fh);
113   PUTC((char)(w >> 24l), fh);
114}
115
116static short
117get16(FILE *fh)
118{
119   short w = GETC(fh);
120   w |= (short)GETC(fh) << 8l;
121   return w;
122}
123
124static void
125put16(short w, FILE *fh)
126{
127   PUTC((char)(w), fh);
128   PUTC((char)(w >> 8l), fh);
129}
130
131static char *
132baseleaf_from_fnm(const char *fnm)
133{
134   const char *p;
135   const char *q;
136   char * res;
137   size_t len;
138
139   p = fnm;
140   q = strrchr(p, '/');
141   if (q) p = q + 1;
142   q = strrchr(p, '\\');
143   if (q) p = q + 1;
144
145   q = strrchr(p, FNM_SEP_EXT);
146   if (q) len = (const char *)q - p; else len = strlen(p);
147
148   res = (char *)xosmalloc(len + 1);
149   if (!res) return NULL;
150   memcpy(res, p, len);
151   res[len] = '\0';
152   return res;
153}
154#endif
155
156static char * my_strdup(const char *str);
157
158static time_t
159mktime_with_tz(struct tm * tm, const char * tz)
160{
161    time_t r;
162    char * old_tz = getenv("TZ");
163#ifdef _MSC_VER
164    if (old_tz) {
165        old_tz = my_strdup(old_tz);
166        if (!old_tz)
167            return (time_t)-1;
168    }
169    if (_putenv_s("TZ", tz) != 0) {
170        osfree(old_tz);
171        return (time_t)-1;
172    }
173#elif defined HAVE_SETENV
174    if (old_tz) {
175        old_tz = my_strdup(old_tz);
176        if (!old_tz)
177            return (time_t)-1;
178    }
179    if (setenv("TZ", tz, 1) < 0) {
180        osfree(old_tz);
181        return (time_t)-1;
182    }
183#else
184    char * p;
185    if (old_tz) {
186        size_t len = strlen(old_tz) + 1;
187        p = (char *)xosmalloc(len + 3);
188        if (!p)
189            return (time_t)-1;
190        memcpy(p, "TZ=", 3);
191        memcpy(p + 3, tz, len);
192        old_tz = p;
193    }
194    p = (char *)xosmalloc(strlen(tz) + 4);
195    if (!p) {
196        osfree(old_tz);
197        return (time_t)-1;
198    }
199    memcpy(p, "TZ=", 3);
200    strcpy(p + 3, tz);
201    if (putenv(p) != 0) {
202        osfree(p);
203        osfree(old_tz);
204        return (time_t)-1;
205    }
206#define CLEANUP() osfree(p)
207#endif
208    tzset();
209    r = mktime(tm);
210    if (old_tz) {
211#ifdef _MSC_VER
212        _putenv_s("TZ", old_tz);
213#elif !defined HAVE_SETENV
214        putenv(old_tz);
215#else
216        setenv("TZ", old_tz, 1);
217#endif
218        osfree(old_tz);
219    } else {
220#ifdef _MSC_VER
221        _putenv_s("TZ", "");
222#elif !defined HAVE_UNSETENV
223        putenv((char*)"TZ");
224#else
225        unsetenv("TZ");
226#endif
227    }
228#ifdef CLEANUP
229    CLEANUP();
230#undef CLEANUP
231#endif
232    return r;
233}
234
235static unsigned short
236getu16(FILE *fh)
237{
238   return (unsigned short)get16(fh);
239}
240
241#include <math.h>
242
243#if !defined HAVE_LROUND && !defined HAVE_DECL_LROUND
244/* The autoconf tests are not in use, but C99 and C++11 both added lround(),
245 * so set HAVE_LROUND and HAVE_DECL_LROUND conservatively based on the language
246 * standard version the compiler claims to support. */
247# if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \
248     (defined __cplusplus && __cplusplus >= 201103L)
249#  define HAVE_LROUND 1
250#  define HAVE_DECL_LROUND 1
251# endif
252#endif
253
254#ifdef HAVE_LROUND
255# if defined HAVE_DECL_LROUND && !HAVE_DECL_LROUND
256/* On older systems, the prototype may be missing. */
257extern long lround(double);
258# endif
259# define my_lround lround
260#else
261static long
262my_lround(double x) {
263   return (x >= 0.0) ? (long)(x + 0.5) : -(long)(0.5 - x);
264}
265#endif
266
267/* portable case insensitive string compare */
268#if defined(strcasecmp) || defined(HAVE_STRCASECMP)
269# define my_strcasecmp strcasecmp
270#else
271static int my_strcasecmp(const char *s1, const char *s2) {
272   unsigned char c1, c2;
273   do {
274      c1 = *s1++;
275      c2 = *s2++;
276   } while (c1 && toupper(c1) == toupper(c2));
277   /* now calculate real difference */
278   return c1 - c2;
279}
280#endif
281
282unsigned int img_output_version = IMG_VERSION_MAX;
283
284static img_errcode img_errno = IMG_NONE;
285
286#define FILEID "Survex 3D Image File"
287
288#define EXT_PLT "plt"
289#define EXT_PLF "plf"
290
291/* Attempt to string paste to ensure we are passed a literal string */
292#define LITLEN(S) (sizeof(S"") - 1)
293
294/* Fake "version numbers" for non-3d formats we can read. */
295#define VERSION_CMAP_SHOT       -4
296#define VERSION_CMAP_STATION    -3
297#define VERSION_COMPASS_PLT     -2
298#define VERSION_SURVEX_POS      -1
299
300static char *
301my_strdup(const char *str)
302{
303   char *p;
304   size_t len = strlen(str) + 1;
305   p = (char *)xosmalloc(len);
306   if (p) memcpy(p, str, len);
307   return p;
308}
309
310#define getline_alloc(FH) getline_alloc_len(FH, NULL)
311
312static char *
313getline_alloc_len(FILE *fh, size_t * p_len)
314{
315   int ch;
316   size_t i = 0;
317   size_t len = 16;
318   char *buf = (char *)xosmalloc(len);
319   if (!buf) return NULL;
320
321   ch = GETC(fh);
322   while (ch != '\n' && ch != '\r' && ch != EOF) {
323      buf[i++] = ch;
324      if (i == len - 1) {
325         char *p;
326         len += len;
327         p = (char *)xosrealloc(buf, len);
328         if (!p) {
329            osfree(buf);
330            return NULL;
331         }
332         buf = p;
333      }
334      ch = GETC(fh);
335   }
336   if (ch == '\n' || ch == '\r') {
337      int otherone = ch ^ ('\n' ^ '\r');
338      ch = GETC(fh);
339      /* if it's not the other eol character, put it back */
340      if (ch != otherone) ungetc(ch, fh);
341   }
342   buf[i] = '\0';
343   if (p_len) *p_len = i;
344   return buf;
345}
346
347img_errcode
348img_error(void)
349{
350   return img_errno;
351}
352
353static int
354check_label_space(img *pimg, size_t len)
355{
356   if (len > pimg->buf_len) {
357      char *b = (char *)xosrealloc(pimg->label_buf, len);
358      if (!b) return 0;
359      pimg->label = (pimg->label - pimg->label_buf) + b;
360      pimg->label_buf = b;
361      pimg->buf_len = len;
362   }
363   return 1;
364}
365
366#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 = strrchr(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      while (fscanf(pimg->fh, "(%lf,%lf,%lf )", &p->x, &p->y, &p->z) != 3) {
1908         if (ferror(pimg->fh)) {
1909            img_errno = IMG_READERROR;
1910            return img_BAD;
1911         }
1912         if (feof(pimg->fh)) return img_STOP;
1913         if (pimg->pending) {
1914            img_errno = IMG_BADFORMAT;
1915            return img_BAD;
1916         }
1917         pimg->pending = 1;
1918         /* ignore rest of line */
1919         do {
1920            ch = GETC(pimg->fh);
1921         } while (ch != '\n' && ch != '\r' && ch != EOF);
1922      }
1923
1924      pimg->label_buf[0] = '\0';
1925      do {
1926          ch = GETC(pimg->fh);
1927      } while (ch == ' ' || ch == '\t');
1928      if (ch == '\n' || ch == EOF) {
1929          /* If there's no label, set img_SFLAG_ANON. */
1930          pimg->flags |= img_SFLAG_ANON;
1931          return img_LABEL;
1932      }
1933      pimg->label_buf[0] = ch;
1934      off = 1;
1935      while (!feof(pimg->fh)) {
1936         if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
1937            img_errno = IMG_READERROR;
1938            return img_BAD;
1939         }
1940
1941         off += strlen(pimg->label_buf + off);
1942         if (off && pimg->label_buf[off - 1] == '\n') {
1943            pimg->label_buf[off - 1] = '\0';
1944            break;
1945         }
1946         if (!check_label_space(pimg, pimg->buf_len * 2)) {
1947            img_errno = IMG_OUTOFMEMORY;
1948            return img_BAD;
1949         }
1950      }
1951
1952      pimg->label = pimg->label_buf;
1953
1954      if (pimg->label[0] == '\\') pimg->label++;
1955
1956      if (pimg->survey_len) {
1957         size_t l = pimg->survey_len + 1;
1958         if (strncmp(pimg->survey, pimg->label, l) != 0) goto againpos;
1959         pimg->label += l;
1960      }
1961
1962      return img_LABEL;
1963   } else if (pimg->version == VERSION_COMPASS_PLT) {
1964      /* Compass .plt file */
1965      if (pimg->pending > 0) {
1966         /* -1 signals we've entered the first survey we want to
1967          * read, and need to fudge lots if the first action is 'D'...
1968          */
1969         /* pending MOVE or LINE */
1970         int r = pimg->pending - 4;
1971         pimg->pending = 0;
1972         pimg->flags = 0;
1973         pimg->label[pimg->label_len] = '\0';
1974         return r;
1975      }
1976
1977      while (1) {
1978         char *line;
1979         char *q;
1980         size_t len = 0;
1981         int ch = GETC(pimg->fh);
1982
1983         switch (ch) {
1984            case '\x1a': case EOF: /* Don't insist on ^Z at end of file */
1985               return img_STOP;
1986            case 'X': case 'F': case 'S':
1987               /* bounding boX (marks end of survey), Feature survey, or
1988                * new Section - skip to next survey */
1989               if (pimg->survey) return img_STOP;
1990skip_to_N:
1991               while (1) {
1992                  do {
1993                     ch = GETC(pimg->fh);
1994                  } while (ch != '\n' && ch != '\r' && ch != EOF);
1995                  while (ch == '\n' || ch == '\r') ch = GETC(pimg->fh);
1996                  if (ch == 'N') break;
1997                  if (ch == '\x1a' || ch == EOF) return img_STOP;
1998               }
1999               /* FALLTHRU */
2000            case 'N':
2001               line = getline_alloc(pimg->fh);
2002               if (!line) {
2003                  img_errno = IMG_OUTOFMEMORY;
2004                  return img_BAD;
2005               }
2006               while (line[len] > 32) ++len;
2007               if (pimg->label_len == 0) pimg->pending = -1;
2008               if (!check_label_space(pimg, len + 1)) {
2009                  osfree(line);
2010                  img_errno = IMG_OUTOFMEMORY;
2011                  return img_BAD;
2012               }
2013               pimg->label_len = len;
2014               pimg->label = pimg->label_buf;
2015               memcpy(pimg->label, line, len);
2016               pimg->label[len] = '\0';
2017               osfree(line);
2018               break;
2019            case 'M': case 'D': {
2020               /* Move or Draw */
2021               long fpos = -1;
2022               if (pimg->survey && pimg->label_len == 0) {
2023                  /* We're only holding onto this line in case the first line
2024                   * of the 'N' is a 'D', so skip it for now...
2025                   */
2026                  goto skip_to_N;
2027               }
2028               if (ch == 'D' && pimg->pending == -1) {
2029                  if (pimg->survey) {
2030                     fpos = ftell(pimg->fh) - 1;
2031                     fseek(pimg->fh, pimg->start, SEEK_SET);
2032                     ch = GETC(pimg->fh);
2033                     pimg->pending = 0;
2034                  } else {
2035                     /* If a file actually has a 'D' before any 'M', then
2036                      * pretend the 'D' is an 'M' - one of the examples
2037                      * in the docs was like this! */
2038                     ch = 'M';
2039                  }
2040               }
2041               line = getline_alloc(pimg->fh);
2042               if (!line) {
2043                  img_errno = IMG_OUTOFMEMORY;
2044                  return img_BAD;
2045               }
2046               /* Compass stores coordinates as North, East, Up = (y,x,z)! */
2047               if (sscanf(line, "%lf%lf%lf", &p->y, &p->x, &p->z) != 3) {
2048                  osfree(line);
2049                  if (ferror(pimg->fh)) {
2050                     img_errno = IMG_READERROR;
2051                  } else {
2052                     img_errno = IMG_BADFORMAT;
2053                  }
2054                  return img_BAD;
2055               }
2056               p->x *= METRES_PER_FOOT;
2057               p->y *= METRES_PER_FOOT;
2058               p->z *= METRES_PER_FOOT;
2059               q = strchr(line, 'S');
2060               if (!q) {
2061                  osfree(line);
2062                  img_errno = IMG_BADFORMAT;
2063                  return img_BAD;
2064               }
2065               ++q;
2066               len = 0;
2067               while (q[len] > ' ') ++len;
2068               q[len] = '\0';
2069               len += 2; /* ' ' and '\0' */
2070               if (!check_label_space(pimg, pimg->label_len + len)) {
2071                  img_errno = IMG_OUTOFMEMORY;
2072                  return img_BAD;
2073               }
2074               pimg->label = pimg->label_buf;
2075               if (pimg->label_len) {
2076                   pimg->label[pimg->label_len] = ' ';
2077                   memcpy(pimg->label + pimg->label_len + 1, q, len - 1);
2078               } else {
2079                   memcpy(pimg->label, q, len - 1);
2080               }
2081               q += len - 1;
2082               /* Now read LRUD.  Technically, this is optional but virtually
2083                * all PLT files have it (with dummy negative values if no LRUD
2084                * was measured) and some versions of Compass can't read PLT
2085                * files without it!
2086                */
2087               while (*q && *q <= ' ') q++;
2088               if (*q == 'P') {
2089                   if (sscanf(q + 1, "%lf%lf%lf%lf",
2090                              &pimg->l, &pimg->r, &pimg->u, &pimg->d) != 4) {
2091                       osfree(line);
2092                       if (ferror(pimg->fh)) {
2093                           img_errno = IMG_READERROR;
2094                       } else {
2095                           img_errno = IMG_BADFORMAT;
2096                       }
2097                       return img_BAD;
2098                   }
2099                   pimg->l *= METRES_PER_FOOT;
2100                   pimg->r *= METRES_PER_FOOT;
2101                   pimg->u *= METRES_PER_FOOT;
2102                   pimg->d *= METRES_PER_FOOT;
2103               } else {
2104                   pimg->l = pimg->r = pimg->u = pimg->d = -1;
2105               }
2106               osfree(line);
2107               pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
2108               if (fpos != -1) {
2109                  fseek(pimg->fh, fpos, SEEK_SET);
2110               } else {
2111                  pimg->pending = (ch == 'M' ? img_MOVE : img_LINE) + 4;
2112               }
2113               return img_LABEL;
2114            }
2115            default:
2116               img_errno = IMG_BADFORMAT;
2117               return img_BAD;
2118         }
2119      }
2120   } else {
2121      /* CMAP .xyz file */
2122      char *line = NULL;
2123      char *q;
2124      size_t len;
2125
2126      if (pimg->pending) {
2127         /* pending MOVE or LINE or LABEL or STOP */
2128         int r = pimg->pending - 4;
2129         /* Set label to empty - don't use "" as we adjust label relative
2130          * to label_buf when label_buf is reallocated. */
2131         pimg->label = pimg->label_buf + strlen(pimg->label_buf);
2132         pimg->flags = 0;
2133         if (r == img_LABEL) {
2134            /* nasty magic */
2135            read_xyz_shot_coords(p, pimg->label_buf + 16);
2136            subtract_xyz_shot_deltas(p, pimg->label_buf + 16);
2137            pimg->pending = img_STOP + 4;
2138            return img_MOVE;
2139         }
2140
2141         pimg->pending = 0;
2142
2143         if (r == img_STOP) {
2144            /* nasty magic */
2145            read_xyz_shot_coords(p, pimg->label_buf + 16);
2146            return img_LINE;
2147         }
2148
2149         return r;
2150      }
2151
2152      pimg->label = pimg->label_buf;
2153      do {
2154         osfree(line);
2155         if (feof(pimg->fh)) return img_STOP;
2156         line = getline_alloc(pimg->fh);
2157         if (!line) {
2158            img_errno = IMG_OUTOFMEMORY;
2159            return img_BAD;
2160         }
2161      } while (line[0] == ' ' || line[0] == '\0');
2162      if (line[0] == '\x1a') return img_STOP;
2163
2164      len = strlen(line);
2165      if (pimg->version == VERSION_CMAP_STATION) {
2166         /* station variant */
2167         if (len < 37) {
2168            osfree(line);
2169            img_errno = IMG_BADFORMAT;
2170            return img_BAD;
2171         }
2172         memcpy(pimg->label, line, 6);
2173         q = (char *)memchr(pimg->label, ' ', 6);
2174         if (!q) q = pimg->label + 6;
2175         *q = '\0';
2176
2177         read_xyz_station_coords(p, line);
2178
2179         /* FIXME: look at prev for lines (line + 32, 5) */
2180         /* FIXME: duplicate stations... */
2181         return img_LABEL;
2182      } else {
2183         /* Shot variant (VERSION_CMAP_SHOT) */
2184         char old[8], new_[8];
2185         if (len < 61) {
2186            osfree(line);
2187            img_errno = IMG_BADFORMAT;
2188            return img_BAD;
2189         }
2190
2191         memcpy(old, line, 7);
2192         q = (char *)memchr(old, ' ', 7);
2193         if (!q) q = old + 7;
2194         *q = '\0';
2195
2196         memcpy(new_, line + 7, 7);
2197         q = (char *)memchr(new_, ' ', 7);
2198         if (!q) q = new_ + 7;
2199         *q = '\0';
2200
2201         pimg->flags = img_SFLAG_UNDERGROUND;
2202
2203         if (strcmp(old, new_) == 0) {
2204            pimg->pending = img_MOVE + 4;
2205            read_xyz_shot_coords(p, line);
2206            strcpy(pimg->label, new_);
2207            osfree(line);
2208            return img_LABEL;
2209         }
2210
2211         if (strcmp(old, pimg->label) == 0) {
2212            pimg->pending = img_LINE + 4;
2213            read_xyz_shot_coords(p, line);
2214            strcpy(pimg->label, new_);
2215            osfree(line);
2216            return img_LABEL;
2217         }
2218
2219         pimg->pending = img_LABEL + 4;
2220         read_xyz_shot_coords(p, line);
2221         strcpy(pimg->label, new_);
2222         memcpy(pimg->label + 16, line, 70);
2223
2224         osfree(line);
2225         return img_LABEL;
2226      }
2227   }
2228}
2229
2230static void
2231write_coord(FILE *fh, double x, double y, double z)
2232{
2233   SVX_ASSERT(fh);
2234   /* Output in cm */
2235   static INT32_T X_, Y_, Z_;
2236   INT32_T X = my_lround(x * 100.0);
2237   INT32_T Y = my_lround(y * 100.0);
2238   INT32_T Z = my_lround(z * 100.0);
2239
2240   X_ -= X;
2241   Y_ -= Y;
2242   Z_ -= Z;
2243   put32(X, fh);
2244   put32(Y, fh);
2245   put32(Z, fh);
2246   X_ = X; Y_ = Y; Z_ = Z;
2247}
2248
2249static int
2250write_v3label(img *pimg, int opt, const char *s)
2251{
2252   size_t len, n, dot;
2253
2254   /* find length of common prefix */
2255   dot = 0;
2256   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
2257      if (s[len] == '.') dot = len + 1;
2258   }
2259
2260   SVX_ASSERT(len <= pimg->label_len);
2261   n = pimg->label_len - len;
2262   if (len == 0) {
2263      if (pimg->label_len) PUTC(0, pimg->fh);
2264   } else if (n <= 16) {
2265      if (n) PUTC(n + 15, pimg->fh);
2266   } else if (dot == 0) {
2267      if (pimg->label_len) PUTC(0, pimg->fh);
2268      len = 0;
2269   } else {
2270      const char *p = pimg->label_buf + dot;
2271      n = 1;
2272      for (len = pimg->label_len - dot - 17; len; len--) {
2273         if (*p++ == '.') n++;
2274      }
2275      if (n <= 14) {
2276         PUTC(n, pimg->fh);
2277         len = dot;
2278      } else {
2279         if (pimg->label_len) PUTC(0, pimg->fh);
2280         len = 0;
2281      }
2282   }
2283
2284   n = strlen(s + len);
2285   PUTC(opt, pimg->fh);
2286   if (n < 0xfe) {
2287      PUTC(n, pimg->fh);
2288   } else if (n < 0xffff + 0xfe) {
2289      PUTC(0xfe, pimg->fh);
2290      put16((short)(n - 0xfe), pimg->fh);
2291   } else {
2292      PUTC(0xff, pimg->fh);
2293      put32(n, pimg->fh);
2294   }
2295   fwrite(s + len, n, 1, pimg->fh);
2296
2297   n += len;
2298   pimg->label_len = n;
2299   if (!check_label_space(pimg, n + 1))
2300      return 0; /* FIXME: distinguish out of memory... */
2301   memcpy(pimg->label_buf + len, s + len, n - len + 1);
2302
2303   return !ferror(pimg->fh);
2304}
2305
2306static int
2307write_v8label(img *pimg, int opt, int common_flag, size_t common_val,
2308              const char *s)
2309{
2310   size_t len, del, add;
2311
2312   /* find length of common prefix */
2313   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
2314   }
2315
2316   SVX_ASSERT(len <= pimg->label_len);
2317   del = pimg->label_len - len;
2318   add = strlen(s + len);
2319
2320   if (add == common_val && del == common_val) {
2321      PUTC(opt | common_flag, pimg->fh);
2322   } else {
2323      PUTC(opt, pimg->fh);
2324      if (del <= 15 && add <= 15 && (del || add)) {
2325         PUTC((del << 4) | add, pimg->fh);
2326      } else {
2327         PUTC(0x00, pimg->fh);
2328         if (del < 0xff) {
2329            PUTC(del, pimg->fh);
2330         } else {
2331            PUTC(0xff, pimg->fh);
2332            put32(del, pimg->fh);
2333         }
2334         if (add < 0xff) {
2335            PUTC(add, pimg->fh);
2336         } else {
2337            PUTC(0xff, pimg->fh);
2338            put32(add, pimg->fh);
2339         }
2340      }
2341   }
2342
2343   if (add)
2344      fwrite(s + len, add, 1, pimg->fh);
2345
2346   pimg->label_len = len + add;
2347   if (add > del && !check_label_space(pimg, pimg->label_len + 1))
2348      return 0; /* FIXME: distinguish out of memory... */
2349
2350   memcpy(pimg->label_buf + len, s + len, add + 1);
2351
2352   return !ferror(pimg->fh);
2353}
2354
2355static void
2356img_write_item_date_new(img *pimg)
2357{
2358    int same, unset;
2359    /* Only write dates when they've changed. */
2360#if IMG_API_VERSION == 0
2361    if (pimg->date1 == pimg->olddate1 && pimg->date2 == pimg->olddate2)
2362        return;
2363
2364    same = (pimg->date1 == pimg->date2);
2365    unset = (pimg->date1 == 0);
2366#else /* IMG_API_VERSION == 1 */
2367    if (pimg->days1 == pimg->olddays1 && pimg->days2 == pimg->olddays2)
2368        return;
2369
2370    same = (pimg->days1 == pimg->days2);
2371    unset = (pimg->days1 == -1);
2372#endif
2373
2374    if (same) {
2375        if (unset) {
2376            PUTC(0x10, pimg->fh);
2377        } else {
2378            PUTC(0x11, pimg->fh);
2379#if IMG_API_VERSION == 0
2380            put16(pimg->date1 / 86400 + 25567, pimg->fh);
2381#else /* IMG_API_VERSION == 1 */
2382            put16(pimg->days1, pimg->fh);
2383#endif
2384        }
2385    } else {
2386#if IMG_API_VERSION == 0
2387        int diff = (pimg->date2 - pimg->date1) / 86400;
2388        if (diff > 0 && diff <= 256) {
2389            PUTC(0x12, pimg->fh);
2390            put16(pimg->date1 / 86400 + 25567, pimg->fh);
2391            PUTC(diff - 1, pimg->fh);
2392        } else {
2393            PUTC(0x13, pimg->fh);
2394            put16(pimg->date1 / 86400 + 25567, pimg->fh);
2395            put16(pimg->date2 / 86400 + 25567, pimg->fh);
2396        }
2397#else /* IMG_API_VERSION == 1 */
2398        int diff = pimg->days2 - pimg->days1;
2399        if (diff > 0 && diff <= 256) {
2400            PUTC(0x12, pimg->fh);
2401            put16(pimg->days1, pimg->fh);
2402            PUTC(diff - 1, pimg->fh);
2403        } else {
2404            PUTC(0x13, pimg->fh);
2405            put16(pimg->days1, pimg->fh);
2406            put16(pimg->days2, pimg->fh);
2407        }
2408#endif
2409    }
2410#if IMG_API_VERSION == 0
2411    pimg->olddate1 = pimg->date1;
2412    pimg->olddate2 = pimg->date2;
2413#else /* IMG_API_VERSION == 1 */
2414    pimg->olddays1 = pimg->days1;
2415    pimg->olddays2 = pimg->days2;
2416#endif
2417}
2418
2419static void
2420img_write_item_date(img *pimg)
2421{
2422    int same, unset;
2423    /* Only write dates when they've changed. */
2424#if IMG_API_VERSION == 0
2425    if (pimg->date1 == pimg->olddate1 && pimg->date2 == pimg->olddate2)
2426        return;
2427
2428    same = (pimg->date1 == pimg->date2);
2429    unset = (pimg->date1 == 0);
2430#else /* IMG_API_VERSION == 1 */
2431    if (pimg->days1 == pimg->olddays1 && pimg->days2 == pimg->olddays2)
2432        return;
2433
2434    same = (pimg->days1 == pimg->days2);
2435    unset = (pimg->days1 == -1);
2436#endif
2437
2438    if (same) {
2439        if (img_output_version < 7) {
2440            PUTC(0x20, pimg->fh);
2441#if IMG_API_VERSION == 0
2442            put32(pimg->date1, pimg->fh);
2443#else /* IMG_API_VERSION == 1 */
2444            put32((pimg->days1 - 25567) * 86400, pimg->fh);
2445#endif
2446        } else {
2447            if (unset) {
2448                PUTC(0x24, pimg->fh);
2449            } else {
2450                PUTC(0x20, pimg->fh);
2451#if IMG_API_VERSION == 0
2452                put16(pimg->date1 / 86400 + 25567, pimg->fh);
2453#else /* IMG_API_VERSION == 1 */
2454                put16(pimg->days1, pimg->fh);
2455#endif
2456            }
2457        }
2458    } else {
2459        if (img_output_version < 7) {
2460            PUTC(0x21, pimg->fh);
2461#if IMG_API_VERSION == 0
2462            put32(pimg->date1, pimg->fh);
2463            put32(pimg->date2, pimg->fh);
2464#else /* IMG_API_VERSION == 1 */
2465            put32((pimg->days1 - 25567) * 86400, pimg->fh);
2466            put32((pimg->days2 - 25567) * 86400, pimg->fh);
2467#endif
2468        } else {
2469#if IMG_API_VERSION == 0
2470            int diff = (pimg->date2 - pimg->date1) / 86400;
2471            if (diff > 0 && diff <= 256) {
2472                PUTC(0x21, pimg->fh);
2473                put16(pimg->date1 / 86400 + 25567, pimg->fh);
2474                PUTC(diff - 1, pimg->fh);
2475            } else {
2476                PUTC(0x23, pimg->fh);
2477                put16(pimg->date1 / 86400 + 25567, pimg->fh);
2478                put16(pimg->date2 / 86400 + 25567, pimg->fh);
2479            }
2480#else /* IMG_API_VERSION == 1 */
2481            int diff = pimg->days2 - pimg->days1;
2482            if (diff > 0 && diff <= 256) {
2483                PUTC(0x21, pimg->fh);
2484                put16(pimg->days1, pimg->fh);
2485                PUTC(diff - 1, pimg->fh);
2486            } else {
2487                PUTC(0x23, pimg->fh);
2488                put16(pimg->days1, pimg->fh);
2489                put16(pimg->days2, pimg->fh);
2490            }
2491#endif
2492        }
2493    }
2494#if IMG_API_VERSION == 0
2495    pimg->olddate1 = pimg->date1;
2496    pimg->olddate2 = pimg->date2;
2497#else /* IMG_API_VERSION == 1 */
2498    pimg->olddays1 = pimg->days1;
2499    pimg->olddays2 = pimg->days2;
2500#endif
2501}
2502
2503static void
2504img_write_item_new(img *pimg, int code, int flags, const char *s,
2505                   double x, double y, double z);
2506static void
2507img_write_item_v3to7(img *pimg, int code, int flags, const char *s,
2508                     double x, double y, double z);
2509static void
2510img_write_item_ancient(img *pimg, int code, int flags, const char *s,
2511                       double x, double y, double z);
2512
2513void
2514img_write_item(img *pimg, int code, int flags, const char *s,
2515               double x, double y, double z)
2516{
2517   if (!pimg) return;
2518   if (pimg->version >= 8) {
2519      img_write_item_new(pimg, code, flags, s, x, y, z);
2520   } else if (pimg->version >= 3) {
2521      img_write_item_v3to7(pimg, code, flags, s, x, y, z);
2522   } else {
2523      img_write_item_ancient(pimg, code, flags, s, x, y, z);
2524   }
2525}
2526
2527static void
2528img_write_item_new(img *pimg, int code, int flags, const char *s,
2529                   double x, double y, double z)
2530{
2531   switch (code) {
2532    case img_LABEL:
2533      write_v8label(pimg, 0x80 | flags, 0, -1, s);
2534      break;
2535    case img_XSECT: {
2536      INT32_T l, r, u, d, max_dim;
2537      img_write_item_date_new(pimg);
2538      l = (INT32_T)my_lround(pimg->l * 100.0);
2539      r = (INT32_T)my_lround(pimg->r * 100.0);
2540      u = (INT32_T)my_lround(pimg->u * 100.0);
2541      d = (INT32_T)my_lround(pimg->d * 100.0);
2542      if (l < 0) l = -1;
2543      if (r < 0) r = -1;
2544      if (u < 0) u = -1;
2545      if (d < 0) d = -1;
2546      max_dim = max(max(l, r), max(u, d));
2547      flags = (flags & img_XFLAG_END) ? 1 : 0;
2548      if (max_dim >= 32768) flags |= 2;
2549      write_v8label(pimg, 0x30 | flags, 0, -1, s);
2550      if (flags & 2) {
2551         /* Big passage!  Need to use 4 bytes. */
2552         put32(l, pimg->fh);
2553         put32(r, pimg->fh);
2554         put32(u, pimg->fh);
2555         put32(d, pimg->fh);
2556      } else {
2557         put16(l, pimg->fh);
2558         put16(r, pimg->fh);
2559         put16(u, pimg->fh);
2560         put16(d, pimg->fh);
2561      }
2562      return;
2563    }
2564    case img_MOVE:
2565      PUTC(15, pimg->fh);
2566      break;
2567    case img_LINE:
2568      img_write_item_date_new(pimg);
2569      if (pimg->style != pimg->oldstyle) {
2570          switch (pimg->style) {
2571            case img_STYLE_NORMAL:
2572            case img_STYLE_DIVING:
2573            case img_STYLE_CARTESIAN:
2574            case img_STYLE_CYLPOLAR:
2575            case img_STYLE_NOSURVEY:
2576               PUTC(pimg->style, pimg->fh);
2577               break;
2578          }
2579          pimg->oldstyle = pimg->style;
2580      }
2581      write_v8label(pimg, 0x40 | flags, 0x20, 0x00, s ? s : "");
2582      break;
2583    default: /* ignore for now */
2584      return;
2585   }
2586   write_coord(pimg->fh, x, y, z);
2587}
2588
2589static void
2590img_write_item_v3to7(img *pimg, int code, int flags, const char *s,
2591                     double x, double y, double z)
2592{
2593   switch (code) {
2594    case img_LABEL:
2595      write_v3label(pimg, 0x40 | flags, s);
2596      break;
2597    case img_XSECT: {
2598      INT32_T l, r, u, d, max_dim;
2599      /* Need at least version 5 for img_XSECT. */
2600      if (pimg->version < 5) return;
2601      img_write_item_date(pimg);
2602      l = (INT32_T)my_lround(pimg->l * 100.0);
2603      r = (INT32_T)my_lround(pimg->r * 100.0);
2604      u = (INT32_T)my_lround(pimg->u * 100.0);
2605      d = (INT32_T)my_lround(pimg->d * 100.0);
2606      if (l < 0) l = -1;
2607      if (r < 0) r = -1;
2608      if (u < 0) u = -1;
2609      if (d < 0) d = -1;
2610      max_dim = max(max(l, r), max(u, d));
2611      flags = (flags & img_XFLAG_END) ? 1 : 0;
2612      if (max_dim >= 32768) flags |= 2;
2613      write_v3label(pimg, 0x30 | flags, s);
2614      if (flags & 2) {
2615         /* Big passage!  Need to use 4 bytes. */
2616         put32(l, pimg->fh);
2617         put32(r, pimg->fh);
2618         put32(u, pimg->fh);
2619         put32(d, pimg->fh);
2620      } else {
2621         put16(l, pimg->fh);
2622         put16(r, pimg->fh);
2623         put16(u, pimg->fh);
2624         put16(d, pimg->fh);
2625      }
2626      return;
2627    }
2628    case img_MOVE:
2629      PUTC(15, pimg->fh);
2630      break;
2631    case img_LINE:
2632      if (pimg->version >= 4) {
2633          img_write_item_date(pimg);
2634      }
2635      write_v3label(pimg, 0x80 | flags, s ? s : "");
2636      break;
2637    default: /* ignore for now */
2638      return;
2639   }
2640   write_coord(pimg->fh, x, y, z);
2641}
2642
2643static void
2644img_write_item_ancient(img *pimg, int code, int flags, const char *s,
2645                       double x, double y, double z)
2646{
2647   size_t len;
2648   INT32_T opt = 0;
2649   SVX_ASSERT(pimg->version > 0);
2650   switch (code) {
2651    case img_LABEL:
2652      if (pimg->version == 1) {
2653         /* put a move before each label */
2654         img_write_item_ancient(pimg, img_MOVE, 0, NULL, x, y, z);
2655         put32(2, pimg->fh);
2656         fputsnl(s, pimg->fh);
2657         return;
2658      }
2659      len = strlen(s);
2660      if (len > 255 || strchr(s, '\n')) {
2661         /* long label - not in early incarnations of v2 format, but few
2662          * 3d files will need these, so better not to force incompatibility
2663          * with a new version I think... */
2664         PUTC(7, pimg->fh);
2665         PUTC(flags, pimg->fh);
2666         put32(len, pimg->fh);
2667         fputs(s, pimg->fh);
2668      } else {
2669         PUTC(0x40 | (flags & 0x3f), pimg->fh);
2670         fputsnl(s, pimg->fh);
2671      }
2672      opt = 0;
2673      break;
2674    case img_MOVE:
2675      opt = 4;
2676      break;
2677    case img_LINE:
2678      if (pimg->version > 1) {
2679         opt = 0x80 | (flags & 0x3f);
2680         break;
2681      }
2682      opt = 5;
2683      break;
2684    default: /* ignore for now */
2685      return;
2686   }
2687   if (pimg->version == 1) {
2688      put32(opt, pimg->fh);
2689   } else {
2690      if (opt) PUTC(opt, pimg->fh);
2691   }
2692   write_coord(pimg->fh, x, y, z);
2693}
2694
2695/* Write error information for the current traverse
2696 * n_legs is the number of legs in the traverse
2697 * length is the traverse length (in m)
2698 * E is the ratio of the observed misclosure to the theoretical one
2699 * H is the ratio of the observed horizontal misclosure to the theoretical one
2700 * V is the ratio of the observed vertical misclosure to the theoretical one
2701 */
2702void
2703img_write_errors(img *pimg, int n_legs, double length,
2704                 double E, double H, double V)
2705{
2706    PUTC((pimg->version >= 8 ? 0x1f : 0x22), pimg->fh);
2707    put32(n_legs, pimg->fh);
2708    put32((INT32_T)my_lround(length * 100.0), pimg->fh);
2709    put32((INT32_T)my_lround(E * 100.0), pimg->fh);
2710    put32((INT32_T)my_lround(H * 100.0), pimg->fh);
2711    put32((INT32_T)my_lround(V * 100.0), pimg->fh);
2712}
2713
2714int
2715img_close(img *pimg)
2716{
2717   int result = 1;
2718   if (pimg) {
2719      if (pimg->fh) {
2720         if (pimg->fRead) {
2721            osfree(pimg->survey);
2722            osfree(pimg->title);
2723            osfree(pimg->cs);
2724            osfree(pimg->datestamp);
2725         } else {
2726            /* write end of data marker */
2727            switch (pimg->version) {
2728             case 1:
2729               put32((INT32_T)-1, pimg->fh);
2730               break;
2731             default:
2732               if (pimg->version <= 7 ?
2733                   (pimg->label_len != 0) :
2734                   (pimg->style != img_STYLE_NORMAL)) {
2735                  PUTC(0, pimg->fh);
2736               }
2737               /* FALL THROUGH */
2738             case 2:
2739               PUTC(0, pimg->fh);
2740               break;
2741            }
2742         }
2743         if (ferror(pimg->fh)) result = 0;
2744         if (fclose(pimg->fh)) result = 0;
2745         if (!result) img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
2746      }
2747      osfree(pimg->label_buf);
2748      osfree(pimg->filename_opened);
2749      osfree(pimg);
2750   }
2751   return result;
2752}
Note: See TracBrowser for help on using the repository browser.