source: git/src/img.c @ 0a9b040

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

configure.ac,src/img.c: Use lround() instead of round(), and make
the tests around whether we use the library function or the our
fallback implementation saner.

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