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

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

configure.in,src/img.c,src/useful.c,src/useful.h: Use AC_TYPE_INT16_T
and AC_TYPE_INT32_T instead of home-brew equivalents.

git-svn-id: file:///home/survex-svn/survex/trunk@3499 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 45.4 KB
Line 
1/* img.c
2 * Routines for reading and writing Survex ".3d" image files
3 * Copyright (C) 1993-2004,2005,2006,2010 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 LATEST_IMG_VERSION 6
34
35#ifdef IMG_HOSTED
36# include "debug.h"
37# include "filelist.h"
38# include "filename.h"
39# include "message.h"
40# include "useful.h"
41# define TIMENA msg(/*Date and time not available.*/108)
42# define TIMEFMT msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107)
43# define INT32_T int32_t
44#else
45# define INT32_T int
46# define TIMENA "Time not available."
47# define TIMEFMT "%a,%Y.%m.%d %H:%M:%S %Z"
48# define EXT_SVX_3D "3d"
49# define EXT_SVX_POS "pos"
50# define FNM_SEP_EXT '.'
51# define METRES_PER_FOOT 0.3048 /* exact value */
52# define xosmalloc(L) malloc((L))
53# define xosrealloc(L,S) realloc((L),(S))
54# define osfree(P) free((P))
55# define ossizeof(T) sizeof(T)
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) fopen(FNM,MODE)
61# define fFalse 0
62# define fTrue 1
63# define bool int
64/* dummy do {...} while(0) hack to permit stuff like
65 * if (s) fputsnl(s,fh); else exit(1);
66 * to work as intended
67 */
68# define fputsnl(S, FH) do {fputs((S), (FH)); putc('\n', (FH));} while(0)
69# define SVX_ASSERT(X)
70
71static INT32_T
72get32(FILE *fh)
73{
74   INT32_T w = getc(fh);
75   w |= (INT32_T)getc(fh) << 8l;
76   w |= (INT32_T)getc(fh) << 16l;
77   w |= (INT32_T)getc(fh) << 24l;
78   return w;
79}
80
81static void
82put32(long w, FILE *fh)
83{
84   putc((char)(w), fh);
85   putc((char)(w >> 8l), fh);
86   putc((char)(w >> 16l), fh);
87   putc((char)(w >> 24l), fh);
88}
89
90static short
91get16(FILE *fh)
92{
93   short w = getc(fh);
94   w |= (long)getc(fh) << 8l;
95   return w;
96}
97
98static void
99put16(short w, FILE *fh)
100{
101   putc((char)(w), fh);
102   putc((char)(w >> 8l), fh);
103}
104
105static char *
106baseleaf_from_fnm(const char *fnm)
107{
108   const char *p;
109   char *q;
110   size_t len;
111
112   p = fnm;
113   q = strrchr(p, '/');
114   if (q) p = q + 1;
115   q = strrchr(p, '\\');
116   if (q) p = q + 1;
117
118   q = strrchr(p, FNM_SEP_EXT);
119   if (q) len = (const char *)q - p; else len = strlen(p);
120
121   q = xosmalloc(len + 1);
122   if (!q) return NULL;
123   memcpy(q, p, len);
124   q[len] = '\0';
125   return q;
126}
127#endif
128
129#include <math.h>
130#ifdef HAVE_ROUND
131extern double round(double); /* prototype is often missing... */
132# define my_round round
133#else
134static double
135my_round(double x) {
136   if (x >= 0.0) return floor(x + 0.5);
137   return ceil(x - 0.5);
138}
139#endif
140
141/* portable case insensitive string compare */
142#if defined(strcasecmp) || defined(HAVE_STRCASECMP)
143# define my_strcasecmp strcasecmp
144#else
145/* What about top bit set chars? */
146int my_strcasecmp(const char *s1, const char *s2) {
147   register int c1, c2;
148   do {
149      c1 = *s1++;
150      c2 = *s2++;
151   } while (c1 && toupper(c1) == toupper(c2));
152   /* now calculate real difference */
153   return c1 - c2;
154}
155#endif
156
157unsigned int img_output_version = LATEST_IMG_VERSION;
158
159#ifdef IMG_HOSTED
160static enum {
161   IMG_NONE = 0,
162   IMG_FILENOTFOUND = /*Couldn't open data file `%s'*/24,
163   IMG_OUTOFMEMORY  = /*Out of memory %.0s*/38,
164   IMG_DIRECTORY    = /*Filename `%s' refers to directory*/44,
165   IMG_CANTOPENOUT  = /*Failed to open output file `%s'*/47,
166   IMG_BADFORMAT    = /*Bad 3d image file `%s'*/106,
167   IMG_READERROR    = /*Error reading from file `%s'*/109,
168   IMG_WRITEERROR   = /*Error writing to file `%s'*/110,
169   IMG_TOONEW       = /*File `%s' has a newer format than this program can understand*/114
170} img_errno = IMG_NONE;
171#else
172static img_errcode img_errno = IMG_NONE;
173#endif
174
175#define FILEID "Survex 3D Image File"
176
177#define EXT_PLT "plt"
178#define EXT_PLF "plf"
179#define EXT_XYZ "xyz"
180
181/* Attempt to string paste to ensure we are passed a literal string */
182#define LITLEN(S) (sizeof(S"") - 1)
183
184/* Fake "version numbers" for non-3d formats we can read. */
185#define VERSION_CMAP_SHOT       -4
186#define VERSION_CMAP_STATION    -3
187#define VERSION_COMPASS_PLT     -2
188#define VERSION_SURVEX_POS      -1
189
190static char *
191my_strdup(const char *str)
192{
193   char *p;
194   size_t len = strlen(str) + 1;
195   p = xosmalloc(len);
196   if (p) memcpy(p, str, len);
197   return p;
198}
199
200static char *
201getline_alloc(FILE *fh)
202{
203   int ch;
204   size_t i = 0;
205   size_t len = 16;
206   char *buf = xosmalloc(len);
207   if (!buf) return NULL;
208
209   ch = getc(fh);
210   while (ch != '\n' && ch != '\r' && ch != EOF) {
211      buf[i++] = ch;
212      if (i == len - 1) {
213         char *p;
214         len += len;
215         p = xosrealloc(buf, len);
216         if (!p) {
217            osfree(buf);
218            return NULL;
219         }
220         buf = p;
221      }
222      ch = getc(fh);
223   }
224   if (ch == '\n' || ch == '\r') {
225      int otherone = ch ^ ('\n' ^ '\r');
226      ch = getc(fh);
227      /* if it's not the other eol character, put it back */
228      if (ch != otherone) ungetc(ch, fh);
229   }
230   buf[i++] = '\0';
231   return buf;
232}
233
234#ifndef IMG_HOSTED
235img_errcode
236img_error(void)
237{
238   return img_errno;
239}
240#else
241int
242img_error(void)
243{
244   return (int)img_errno;
245}
246#endif
247
248static bool
249check_label_space(img *pimg, size_t len)
250{
251   if (len > pimg->buf_len) {
252      char *b = xosrealloc(pimg->label_buf, len);
253      if (!b) return fFalse;
254      pimg->label = (pimg->label - pimg->label_buf) + b;
255      pimg->label_buf = b;
256      pimg->buf_len = len;
257   }
258   return fTrue;
259}
260
261#define has_ext(F,L,E) ((L) > LITLEN(E) + 1 &&\
262                        (F)[(L) - LITLEN(E) - 1] == FNM_SEP_EXT &&\
263                        my_strcasecmp((F) + (L) - LITLEN(E), E) == 0)
264
265img *
266img_open_survey(const char *fnm, const char *survey)
267{
268   img *pimg;
269   size_t len;
270   char buf[LITLEN(FILEID) + 9];
271   int ch;
272
273   if (fDirectory(fnm)) {
274      img_errno = IMG_DIRECTORY;
275      return NULL;
276   }
277
278   pimg = (img *)xosmalloc(ossizeof(img));
279   if (pimg == NULL) {
280      img_errno = IMG_OUTOFMEMORY;
281      return NULL;
282   }
283
284   pimg->buf_len = 257;
285   pimg->label_buf = xosmalloc(pimg->buf_len);
286   if (!pimg->label_buf) {
287      osfree(pimg);
288      img_errno = IMG_OUTOFMEMORY;
289      return NULL;
290   }
291
292   pimg->fh = fopenWithPthAndExt("", fnm, EXT_SVX_3D, "rb", &(pimg->filename_opened));
293   if (pimg->fh == NULL) {
294      osfree(pimg->label_buf);
295      osfree(pimg);
296      img_errno = IMG_FILENOTFOUND;
297      return NULL;
298   }
299
300   pimg->fRead = fTrue; /* reading from this file */
301   img_errno = IMG_NONE;
302
303   pimg->flags = 0;
304
305   /* for version >= 3 we use label_buf to store the prefix for reuse */
306   /* for VERSION_COMPASS_PLT, 0 value indicates we haven't
307    * entered a survey yet */
308   /* for VERSION_CMAP_SHOT, we store the last station here
309    * to detect whether we MOVE or LINE */
310   pimg->label_len = 0;
311   pimg->label_buf[0] = '\0';
312
313   pimg->survey = NULL;
314   pimg->survey_len = 0;
315   pimg->separator = '.';
316   pimg->date1 = 0;
317   pimg->date2 = 0;
318   pimg->is_extended_elevation = 0;
319
320   pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
321
322   pimg->title = pimg->datestamp = NULL;
323   if (survey) {
324      len = strlen(survey);
325      if (len) {
326         if (survey[len - 1] == '.') len--;
327         if (len) {
328            char *p;
329            pimg->survey = xosmalloc(len + 2);
330            if (!pimg->survey) {
331               img_errno = IMG_OUTOFMEMORY;
332               goto error;
333            }
334            memcpy(pimg->survey, survey, len);
335            /* Set title to leaf survey name */
336            pimg->survey[len] = '\0';
337            p = strchr(pimg->survey, '.');
338            if (p) p++; else p = pimg->survey;
339            pimg->title = my_strdup(p);
340            if (!pimg->title) {
341               img_errno = IMG_OUTOFMEMORY;
342               goto error;
343            }
344            pimg->survey[len] = '.';
345            pimg->survey[len + 1] = '\0';
346         }
347      }
348      pimg->survey_len = len;
349   }
350
351   /* [VERSION_COMPASS_PLT, VERSION_CMAP_STATION, VERSION_CMAP_SHOT] pending
352    * IMG_LINE or IMG_MOVE - both have 4 added.
353    * [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
354    * [version 0] not in the middle of a 'LINE' command
355    * [version >= 3] not in the middle of turning a LINE into a MOVE
356    */
357   pimg->pending = 0;
358
359   len = strlen(fnm);
360   if (has_ext(fnm, len, EXT_SVX_POS)) {
361pos_file:
362      pimg->version = VERSION_SURVEX_POS;
363      if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
364      pimg->datestamp = my_strdup(TIMENA);
365      if (!pimg->datestamp) {
366         img_errno = IMG_OUTOFMEMORY;
367         goto error;
368      }
369      pimg->start = 0;
370      return pimg;
371   }
372
373   if (has_ext(fnm, len, EXT_PLT) || has_ext(fnm, len, EXT_PLF)) {
374      long fpos;
375plt_file:
376      pimg->version = VERSION_COMPASS_PLT;
377      /* Spaces aren't legal in Compass station names, but dots are, so
378       * use space as the level separator */
379      pimg->separator = ' ';
380      pimg->start = 0;
381      if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
382      pimg->datestamp = my_strdup(TIMENA);
383      if (!pimg->datestamp) {
384         img_errno = IMG_OUTOFMEMORY;
385         goto error;
386      }
387      while (1) {
388         ch = getc(pimg->fh);
389         switch (ch) {
390          case '\x1a':
391            fseek(pimg->fh, -1, SEEK_CUR);
392            /* FALL THRU */
393          case EOF:
394            pimg->start = ftell(pimg->fh);
395            return pimg;
396          case 'N': {
397            char *line, *q;
398            fpos = ftell(pimg->fh) - 1;
399            if (!pimg->survey) {
400               /* FIXME : if there's only one survey in the file, it'd be nice
401                * to use its description as the title here...
402                */
403               ungetc('N', pimg->fh);
404               pimg->start = fpos;
405               return pimg;
406            }
407            line = getline_alloc(pimg->fh);
408            if (!line) {
409               img_errno = IMG_OUTOFMEMORY;
410               goto error;
411            }
412            len = 0;
413            while (line[len] > 32) ++len;
414            if (pimg->survey_len != len ||
415                memcmp(line, pimg->survey, len) != 0) {
416               osfree(line);
417               continue;
418            }
419            q = strchr(line + len, 'C');
420            if (q && q[1]) {
421                osfree(pimg->title);
422                pimg->title = my_strdup(q + 1);
423            } else if (!pimg->title) {
424                pimg->title = my_strdup(pimg->label);
425            }
426            osfree(line);
427            if (!pimg->title) {
428                img_errno = IMG_OUTOFMEMORY;
429                goto error;
430            }
431            if (!pimg->start) pimg->start = fpos;
432            fseek(pimg->fh, pimg->start, SEEK_SET);
433            return pimg;
434          }
435          case 'M': case 'D':
436            pimg->start = ftell(pimg->fh) - 1;
437            break;
438         }
439         while (ch != '\n' && ch != '\r') {
440            ch = getc(pimg->fh);
441         }
442      }
443   }
444
445   if (has_ext(fnm, len, EXT_XYZ)) {
446      char *line;
447xyz_file:
448      /* Spaces aren't legal in CMAP station names, but dots are, so
449       * use space as the level separator. */
450      pimg->separator = ' ';
451      line = getline_alloc(pimg->fh);
452      if (!line) {
453         img_errno = IMG_OUTOFMEMORY;
454         goto error;
455      }
456      /* FIXME: reparse date? */
457      len = strlen(line);
458      if (len > 59) line[59] = '\0';
459      if (len > 45) {
460         pimg->datestamp = my_strdup(line + 45);
461      } else {
462         pimg->datestamp = my_strdup(TIMENA);
463      }
464      if (strncmp(line, "  Cave Survey Data Processed by CMAP ",
465                  LITLEN("  Cave Survey Data Processed by CMAP ")) == 0) {
466         len = 0;
467      } else {
468         if (len > 45) {
469            line[45] = '\0';
470            len = 45;
471         }
472         while (len > 2 && line[len - 1] == ' ') --len;
473         if (len > 2) {
474            line[len] = '\0';
475            pimg->title = my_strdup(line + 2);
476         }
477      }
478      if (len <= 2) pimg->title = baseleaf_from_fnm(fnm);
479      osfree(line);
480      if (!pimg->datestamp || !pimg->title) {
481         img_errno = IMG_OUTOFMEMORY;
482         goto error;
483      }
484      line = getline_alloc(pimg->fh);
485      if (!line) {
486         img_errno = IMG_OUTOFMEMORY;
487         goto error;
488      }
489      if (line[0] != ' ' || (line[1] != 'S' && line[1] != 'O')) {
490         img_errno = IMG_BADFORMAT;
491         goto error;
492      }
493      if (line[1] == 'S') {
494         pimg->version = VERSION_CMAP_STATION;
495      } else {
496         pimg->version = VERSION_CMAP_SHOT;
497      }
498      osfree(line);
499      line = getline_alloc(pimg->fh);
500      if (!line) {
501         img_errno = IMG_OUTOFMEMORY;
502         goto error;
503      }
504      if (line[0] != ' ' || line[1] != '-') {
505         img_errno = IMG_BADFORMAT;
506         goto error;
507      }
508      osfree(line);
509      pimg->start = ftell(pimg->fh);
510      return pimg;
511   }
512
513   if (fread(buf, LITLEN(FILEID) + 1, 1, pimg->fh) != 1 ||
514       memcmp(buf, FILEID"\n", LITLEN(FILEID) + 1) != 0) {
515      if (fread(buf + LITLEN(FILEID) + 1, 8, 1, pimg->fh) == 1 &&
516          memcmp(buf, FILEID"\r\nv0.01\r\n", LITLEN(FILEID) + 9) == 0) {
517         /* v0 3d file with DOS EOLs */
518         pimg->version = 0;
519         goto v03d;
520      }
521      rewind(pimg->fh);
522      if (buf[1] == ' ') {
523         if (buf[0] == ' ') {
524            /* Looks like a CMAP .xyz file ... */
525            goto xyz_file;
526         } else if (strchr("ZSNF", buf[0])) {
527            /* Looks like a Compass .plt file ... */
528            /* Almost certainly it'll start "Z " */
529            goto plt_file;
530         }
531      }
532      if (buf[0] == '(') {
533         /* Looks like a Survex .pos file ... */
534         goto pos_file;
535      }
536      img_errno = IMG_BADFORMAT;
537      goto error;
538   }
539
540   /* check file format version */
541   ch = getc(pimg->fh);
542   pimg->version = 0;
543   if (tolower(ch) == 'b') {
544      /* binary file iff B/b prefix */
545      pimg->version = 1;
546      ch = getc(pimg->fh);
547   }
548   if (ch != 'v') {
549      img_errno = IMG_BADFORMAT;
550      goto error;
551   }
552   ch = getc(pimg->fh);
553   if (ch == '0') {
554      if (fread(buf, 4, 1, pimg->fh) != 1 || memcmp(buf, ".01\n", 4) != 0) {
555         img_errno = IMG_BADFORMAT;
556         goto error;
557      }
558      /* nothing special to do */
559   } else if (pimg->version == 0) {
560      if (ch < '2' || ch > '0' + LATEST_IMG_VERSION || getc(pimg->fh) != '\n') {
561         img_errno = IMG_TOONEW;
562         goto error;
563      }
564      pimg->version = ch - '0';
565   } else {
566      img_errno = IMG_BADFORMAT;
567      goto error;
568   }
569
570v03d:
571   if (!pimg->title)
572       pimg->title = getline_alloc(pimg->fh);
573   else
574       osfree(getline_alloc(pimg->fh));
575   pimg->datestamp = getline_alloc(pimg->fh);
576   if (!pimg->title || !pimg->datestamp) {
577      img_errno = IMG_OUTOFMEMORY;
578      error:
579      osfree(pimg->title);
580      osfree(pimg->datestamp);
581      osfree(pimg->filename_opened);
582      fclose(pimg->fh);
583      osfree(pimg);
584      return NULL;
585   }
586
587   pimg->start = ftell(pimg->fh);
588
589   len = strlen(pimg->title);
590   if (len > 11 && strcmp(pimg->title + len - 11, " (extended)") == 0) {
591       pimg->title[len - 11] = '\0';
592       pimg->is_extended_elevation = 1;
593   }
594
595   return pimg;
596}
597
598int
599img_rewind(img *pimg)
600{
601   if (!pimg->fRead) {
602      img_errno = IMG_WRITEERROR;
603      return 0;
604   }
605   if (fseek(pimg->fh, pimg->start, SEEK_SET) != 0) {
606      img_errno = IMG_READERROR;
607      return 0;
608   }
609   clearerr(pimg->fh);
610   /* [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
611    * [version 0] not in the middle of a 'LINE' command
612    * [version >= 3] not in the middle of turning a LINE into a MOVE */
613   pimg->pending = 0;
614
615   img_errno = IMG_NONE;
616
617   /* for version >= 3 we use label_buf to store the prefix for reuse */
618   /* for VERSION_COMPASS_PLT, 0 value indicates we haven't entered a survey
619    * yet */
620   /* for VERSION_CMAP_SHOT, we store the last station here to detect whether
621    * we MOVE or LINE */
622   pimg->label_len = 0;
623   return 1;
624}
625
626img *
627img_open_write(const char *fnm, char *title_buf, bool fBinary)
628{
629   time_t tm;
630   img *pimg;
631
632   fBinary = fBinary;
633
634   if (fDirectory(fnm)) {
635      img_errno = IMG_DIRECTORY;
636      return NULL;
637   }
638
639   pimg = (img *)xosmalloc(ossizeof(img));
640   if (pimg == NULL) {
641      img_errno = IMG_OUTOFMEMORY;
642      return NULL;
643   }
644
645   pimg->buf_len = 257;
646   pimg->label_buf = xosmalloc(pimg->buf_len);
647   if (!pimg->label_buf) {
648      osfree(pimg);
649      img_errno = IMG_OUTOFMEMORY;
650      return NULL;
651   }
652
653   pimg->fh = fopen(fnm, "wb");
654   if (!pimg->fh) {
655      osfree(pimg->label_buf);
656      osfree(pimg);
657      img_errno = IMG_CANTOPENOUT;
658      return NULL;
659   }
660
661   pimg->filename_opened = NULL;
662
663   /* Output image file header */
664   fputs("Survex 3D Image File\n", pimg->fh); /* file identifier string */
665   if (img_output_version < 2) {
666      pimg->version = 1;
667      fputs("Bv0.01\n", pimg->fh); /* binary file format version number */
668   } else {
669      pimg->version = (img_output_version > LATEST_IMG_VERSION) ? LATEST_IMG_VERSION : img_output_version;
670      fprintf(pimg->fh, "v%d\n", pimg->version); /* file format version no. */
671   }
672   fputsnl(title_buf, pimg->fh);
673   tm = time(NULL);
674   if (tm == (time_t)-1) {
675      fputsnl(TIMENA, pimg->fh);
676   } else {
677      char date[256];
678      /* output current date and time in format specified */
679      strftime(date, 256, TIMEFMT, localtime(&tm));
680      fputsnl(date, pimg->fh);
681   }
682#if 0
683   if (img_output_version >= 5) {
684       static const unsigned char codelengths[32] = {
685           4,  8,  8,  16, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
686           0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
687       };
688       fwrite(codelengths, 32, 1, pimg->fh);
689   }
690#endif
691   pimg->fRead = fFalse; /* writing to this file */
692   img_errno = IMG_NONE;
693
694   /* for version >= 3 we use label_buf to store the prefix for reuse */
695   pimg->label_buf[0] = '\0';
696   pimg->label_len = 0;
697
698   pimg->date1 = 0;
699   pimg->date2 = 0;
700
701   pimg->olddate1 = 0;
702   pimg->olddate2 = 0;
703
704   pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
705
706   pimg->n_legs = 0;
707   pimg->length = 0.0;
708   pimg->E = pimg->H = pimg->V = 0.0;
709
710   /* Don't check for write errors now - let img_close() report them... */
711   return pimg;
712}
713
714static void
715read_xyz_station_coords(img_point *pt, const char *line)
716{
717   char num[12];
718   memcpy(num, line + 6, 9);
719   num[9] = '\0';
720   pt->x = atof(num) / METRES_PER_FOOT;
721   memcpy(num, line + 15, 9);
722   pt->y = atof(num) / METRES_PER_FOOT;
723   memcpy(num, line + 24, 8);
724   num[8] = '\0';
725   pt->z = atof(num) / METRES_PER_FOOT;
726}
727
728static void
729read_xyz_shot_coords(img_point *pt, const char *line)
730{
731   char num[12];
732   memcpy(num, line + 40, 10);
733   num[10] = '\0';
734   pt->x = atof(num) / METRES_PER_FOOT;
735   memcpy(num, line + 50, 10);
736   pt->y = atof(num) / METRES_PER_FOOT;
737   memcpy(num, line + 60, 9);
738   num[9] = '\0';
739   pt->z = atof(num) / METRES_PER_FOOT;
740}
741
742static void
743subtract_xyz_shot_deltas(img_point *pt, const char *line)
744{
745   char num[12];
746   memcpy(num, line + 15, 9);
747   num[9] = '\0';
748   pt->x -= atof(num) / METRES_PER_FOOT;
749   memcpy(num, line + 24, 8);
750   num[8] = '\0';
751   pt->y -= atof(num) / METRES_PER_FOOT;
752   memcpy(num, line + 32, 8);
753   pt->z -= atof(num) / METRES_PER_FOOT;
754}
755
756static int
757read_coord(FILE *fh, img_point *pt)
758{
759   SVX_ASSERT(fh);
760   SVX_ASSERT(pt);
761   pt->x = get32(fh) / 100.0;
762   pt->y = get32(fh) / 100.0;
763   pt->z = get32(fh) / 100.0;
764   if (ferror(fh) || feof(fh)) {
765      img_errno = feof(fh) ? IMG_BADFORMAT : IMG_READERROR;
766      return 0;
767   }
768   return 1;
769}
770
771static int
772skip_coord(FILE *fh)
773{
774    return (fseek(fh, 12, SEEK_CUR) == 0);
775}
776
777static int
778read_v3label(img *pimg)
779{
780   char *q;
781   long len = getc(pimg->fh);
782   if (len == EOF) {
783      img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
784      return img_BAD;
785   }
786   if (len == 0xfe) {
787      len += get16(pimg->fh);
788      if (feof(pimg->fh)) {
789         img_errno = IMG_BADFORMAT;
790         return img_BAD;
791      }
792      if (ferror(pimg->fh)) {
793         img_errno = IMG_READERROR;
794         return img_BAD;
795      }
796   } else if (len == 0xff) {
797      len = get32(pimg->fh);
798      if (ferror(pimg->fh)) {
799         img_errno = IMG_READERROR;
800         return img_BAD;
801      }
802      if (feof(pimg->fh) || len < 0xfe + 0xffff) {
803         img_errno = IMG_BADFORMAT;
804         return img_BAD;
805      }
806   }
807
808   if (!check_label_space(pimg, pimg->label_len + len + 1)) {
809      img_errno = IMG_OUTOFMEMORY;
810      return img_BAD;
811   }
812   q = pimg->label_buf + pimg->label_len;
813   pimg->label_len += len;
814   if (len && fread(q, len, 1, pimg->fh) != 1) {
815      img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
816      return img_BAD;
817   }
818   q[len] = '\0';
819   return 0;
820}
821
822static int img_read_item_ascii(img *pimg, img_point *p);
823
824int
825img_read_item(img *pimg, img_point *p)
826{
827   int result;
828   pimg->flags = 0;
829
830   if (pimg->version >= 3) {
831      int opt;
832      pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
833      if (pimg->pending == 256) {
834         pimg->pending = 0;
835         return img_XSECT_END;
836      }
837      if (pimg->pending >= 0x80) {
838         *p = pimg->mv;
839         pimg->flags = (int)(pimg->pending) & 0x3f;
840         pimg->pending = 0;
841         return img_LINE;
842      }
843      again3: /* label to goto if we get a prefix, date, or lrud */
844      pimg->label = pimg->label_buf;
845      opt = getc(pimg->fh);
846      if (opt == EOF) {
847         img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
848         return img_BAD;
849      }
850      switch (opt >> 6) {
851       case 0:
852         if (opt == 0) {
853            if (!pimg->label_len) return img_STOP; /* end of data marker */
854            pimg->label_len = 0;
855            goto again3;
856         }
857         if (opt < 15) {
858            /* 1-14 mean trim that many levels from current prefix */
859            int c;
860            if (pimg->label_len <= 17) {
861               /* zero prefix using "0" */
862               img_errno = IMG_BADFORMAT;
863               return img_BAD;
864            }
865            /* extra - 1 because label_len points to one past the end */
866            c = pimg->label_len - 17 - 1;
867            while (pimg->label_buf[c] != '.' || --opt > 0) {
868               if (--c < 0) {
869                  /* zero prefix using "0" */
870                  img_errno = IMG_BADFORMAT;
871                  return img_BAD;
872               }
873            }
874            c++;
875            pimg->label_len = c;
876            goto again3;
877         }
878         if (opt == 15) {
879            result = img_MOVE;
880            break;
881         }
882         if (opt >= 0x20) {
883             switch (opt) {
884                 case 0x20: /* Single date */
885                     pimg->date1 = get32(pimg->fh);
886                     pimg->date2 = pimg->date1;
887                     break;
888                 case 0x21: /* Date range */
889                     pimg->date1 = get32(pimg->fh);
890                     pimg->date2 = get32(pimg->fh);
891                     break;
892                 case 0x22: /* Error info */
893                     pimg->n_legs = get32(pimg->fh);
894                     pimg->length = get32(pimg->fh) / 100.0;
895                     pimg->E = get32(pimg->fh) / 100.0;
896                     pimg->H = get32(pimg->fh) / 100.0;
897                     pimg->V = get32(pimg->fh) / 100.0;
898                     return img_ERROR_INFO;
899                 case 0x30: case 0x31: /* LRUD */
900                 case 0x32: case 0x33: /* Big LRUD! */
901                     if (read_v3label(pimg) == img_BAD) return img_BAD;
902                     pimg->flags = (int)opt & 0x01;
903                     if (opt < 0x32) {
904                         pimg->l = get16(pimg->fh) / 100.0;
905                         pimg->r = get16(pimg->fh) / 100.0;
906                         pimg->u = get16(pimg->fh) / 100.0;
907                         pimg->d = get16(pimg->fh) / 100.0;
908                     } else {
909                         pimg->l = get32(pimg->fh) / 100.0;
910                         pimg->r = get32(pimg->fh) / 100.0;
911                         pimg->u = get32(pimg->fh) / 100.0;
912                         pimg->d = get32(pimg->fh) / 100.0;
913                     }
914                     if (pimg->survey_len) {
915                         size_t l = pimg->survey_len;
916                         const char *s = pimg->label_buf;
917                         if (strncmp(pimg->survey, s, l + 1) != 0) {
918                             return img_XSECT_END;
919                         }
920                         pimg->label += l;
921                         /* skip the dot if there */
922                         if (*pimg->label) pimg->label++;
923                     }
924                     /* If this is the last cross-section in this passage, set
925                      * pending so we return img_XSECT_END next time. */
926                     if (pimg->flags & 0x01) {
927                         pimg->pending = 256;
928                         pimg->flags &= ~0x01;
929                     }
930                     return img_XSECT;
931                 default: /* 0x23 - 0x2f and 0x34 - 0x3f are currently unallocated. */
932                     img_errno = IMG_BADFORMAT;
933                     return img_BAD;
934             }
935             goto again3;
936         }
937         /* 16-31 mean remove (n - 15) characters from the prefix */
938         /* zero prefix using 0 */
939         if (pimg->label_len <= (size_t)(opt - 15)) {
940            img_errno = IMG_BADFORMAT;
941            return img_BAD;
942         }
943         pimg->label_len -= (opt - 15);
944         goto again3;
945       case 1:
946         if (read_v3label(pimg) == img_BAD) return img_BAD;
947
948         result = img_LABEL;
949
950         if (pimg->survey_len) {
951            size_t l = pimg->survey_len;
952            const char *s = pimg->label_buf;
953            if (strncmp(pimg->survey, s, l + 1) != 0) {
954               if (!skip_coord(pimg->fh)) return img_BAD;
955               pimg->pending = 0;
956               goto again3;
957            }
958            pimg->label += l;
959            /* skip the dot if there */
960            if (*pimg->label) pimg->label++;
961         }
962
963         pimg->flags = (int)opt & 0x3f;
964         break;
965       case 2:
966         if (read_v3label(pimg) == img_BAD) return img_BAD;
967
968         result = img_LINE;
969
970         if (pimg->survey_len) {
971            size_t l = pimg->survey_len;
972            const char *s = pimg->label_buf;
973            if (strncmp(pimg->survey, s, l) != 0 ||
974                !(s[l] == '.' || s[l] == '\0')) {
975               if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
976               pimg->pending = 15;
977               goto again3;
978            }
979            pimg->label += l;
980            /* skip the dot if there */
981            if (*pimg->label) pimg->label++;
982         }
983
984         if (pimg->pending) {
985            *p = pimg->mv;
986            if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
987            pimg->pending = opt;
988            return img_MOVE;
989         }
990         pimg->flags = (int)opt & 0x3f;
991         break;
992       default:
993         img_errno = IMG_BADFORMAT;
994         return img_BAD;
995      }
996      if (!read_coord(pimg->fh, p)) return img_BAD;
997      pimg->pending = 0;
998      return result;
999   }
1000
1001   pimg->label = pimg->label_buf;
1002
1003   if (pimg->version > 0) {
1004      static long opt_lookahead = 0;
1005      static img_point pt = { 0.0, 0.0, 0.0 };
1006      long opt;
1007      again: /* label to goto if we get a cross */
1008      pimg->label[0] = '\0';
1009
1010      if (pimg->version == 1) {
1011         if (opt_lookahead) {
1012            opt = opt_lookahead;
1013            opt_lookahead = 0;
1014         } else {
1015            opt = get32(pimg->fh);
1016         }
1017      } else {
1018         opt = getc(pimg->fh);
1019      }
1020
1021      if (feof(pimg->fh)) {
1022         img_errno = IMG_BADFORMAT;
1023         return img_BAD;
1024      }
1025      if (ferror(pimg->fh)) {
1026         img_errno = IMG_READERROR;
1027         return img_BAD;
1028      }
1029
1030      switch (opt) {
1031       case -1: case 0:
1032         return img_STOP; /* end of data marker */
1033       case 1:
1034         /* skip coordinates */
1035         if (!skip_coord(pimg->fh)) {
1036            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1037            return img_BAD;
1038         }
1039         goto again;
1040       case 2: case 3: {
1041         char *q;
1042         int ch;
1043         result = img_LABEL;
1044         ch = getc(pimg->fh);
1045         if (ch == EOF) {
1046            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1047            return img_BAD;
1048         }
1049         if (ch != '\\') ungetc(ch, pimg->fh);
1050         fgets(pimg->label_buf, 257, pimg->fh);
1051         if (feof(pimg->fh)) {
1052            img_errno = IMG_BADFORMAT;
1053            return img_BAD;
1054         }
1055         if (ferror(pimg->fh)) {
1056            img_errno = IMG_READERROR;
1057            return img_BAD;
1058         }
1059         q = pimg->label_buf + strlen(pimg->label_buf) - 1;
1060         if (*q != '\n') {
1061            img_errno = IMG_BADFORMAT;
1062            return img_BAD;
1063         }
1064         /* Ignore empty labels in some .3d files (caused by a bug) */
1065         if (q == pimg->label_buf) goto again;
1066         *q = '\0';
1067         pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
1068         if (opt == 2) goto done;
1069         break;
1070       }
1071       case 6: case 7: {
1072         long len;
1073         result = img_LABEL;
1074
1075         if (opt == 7)
1076            pimg->flags = getc(pimg->fh);
1077         else
1078            pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
1079
1080         len = get32(pimg->fh);
1081
1082         if (feof(pimg->fh)) {
1083            img_errno = IMG_BADFORMAT;
1084            return img_BAD;
1085         }
1086         if (ferror(pimg->fh)) {
1087            img_errno = IMG_READERROR;
1088            return img_BAD;
1089         }
1090
1091         /* Ignore empty labels in some .3d files (caused by a bug) */
1092         if (len == 0) goto again;
1093         if (!check_label_space(pimg, len + 1)) {
1094            img_errno = IMG_OUTOFMEMORY;
1095            return img_BAD;
1096         }
1097         if (fread(pimg->label_buf, len, 1, pimg->fh) != 1) {
1098            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1099            return img_BAD;
1100         }
1101         pimg->label_buf[len] = '\0';
1102         break;
1103       }
1104       case 4:
1105         result = img_MOVE;
1106         break;
1107       case 5:
1108         result = img_LINE;
1109         break;
1110       default:
1111         switch ((int)opt & 0xc0) {
1112          case 0x80:
1113            pimg->flags = (int)opt & 0x3f;
1114            result = img_LINE;
1115            break;
1116          case 0x40: {
1117            char *q;
1118            pimg->flags = (int)opt & 0x3f;
1119            result = img_LABEL;
1120            if (!fgets(pimg->label_buf, 257, pimg->fh)) {
1121               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1122               return img_BAD;
1123            }
1124            q = pimg->label_buf + strlen(pimg->label_buf) - 1;
1125            /* Ignore empty-labels in some .3d files (caused by a bug) */
1126            if (q == pimg->label_buf) goto again;
1127            if (*q != '\n') {
1128               img_errno = IMG_BADFORMAT;
1129               return img_BAD;
1130            }
1131            *q = '\0';
1132            break;
1133          }
1134          case 0xc0:
1135            /* use this for an extra leg or station flag if we need it */
1136          default:
1137            img_errno = IMG_BADFORMAT;
1138            return img_BAD;
1139         }
1140         break;
1141      }
1142
1143      if (!read_coord(pimg->fh, &pt)) return img_BAD;
1144
1145      if (result == img_LABEL && pimg->survey_len) {
1146         if (strncmp(pimg->label_buf, pimg->survey, pimg->survey_len + 1) != 0)
1147            goto again;
1148         pimg->label += pimg->survey_len + 1;
1149      }
1150
1151      done:
1152      *p = pt;
1153
1154      if (result == img_MOVE && pimg->version == 1) {
1155         /* peek at next code and see if it's an old-style label */
1156         opt_lookahead = get32(pimg->fh);
1157
1158         if (feof(pimg->fh)) {
1159            img_errno = IMG_BADFORMAT;
1160            return img_BAD;
1161         }
1162         if (ferror(pimg->fh)) {
1163            img_errno = IMG_READERROR;
1164            return img_BAD;
1165         }
1166
1167         if (opt_lookahead == 2) return img_read_item(pimg, p);
1168      }
1169
1170      return result;
1171   } else {
1172      /* We need to set the default locale for fscanf() to work on
1173       * numbers with "." as decimal point. */
1174      char * current_locale = my_strdup(setlocale(LC_NUMERIC, NULL));
1175      setlocale(LC_NUMERIC, "C");
1176      result = img_read_item_ascii(pimg, p);
1177      setlocale(LC_NUMERIC, current_locale);
1178      free(current_locale);
1179      return result;
1180   }
1181}
1182
1183/* Handle all for ASCII formats so we can easily fix up the locale
1184 * so fscanf(), etc work on numbers with "." as decimal point. */
1185static int
1186img_read_item_ascii(img *pimg, img_point *p)
1187{
1188   int result;
1189   if (pimg->version == 0) {
1190      ascii_again:
1191      pimg->label[0] = '\0';
1192      if (feof(pimg->fh)) return img_STOP;
1193      if (pimg->pending) {
1194         pimg->pending = 0;
1195         result = img_LINE;
1196      } else {
1197         char cmd[7];
1198         /* Stop if nothing found */
1199         if (fscanf(pimg->fh, "%6s", cmd) < 1) return img_STOP;
1200         if (strcmp(cmd, "move") == 0)
1201            result = img_MOVE;
1202         else if (strcmp(cmd, "draw") == 0)
1203            result = img_LINE;
1204         else if (strcmp(cmd, "line") == 0) {
1205            /* set flag to indicate to process second triplet as LINE */
1206            pimg->pending = 1;
1207            result = img_MOVE;
1208         } else if (strcmp(cmd, "cross") == 0) {
1209            if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
1210               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1211               return img_BAD;
1212            }
1213            goto ascii_again;
1214         } else if (strcmp(cmd, "name") == 0) {
1215            size_t off = 0;
1216            int ch = getc(pimg->fh);
1217            if (ch == ' ') ch = getc(pimg->fh);
1218            while (ch != ' ') {
1219               if (ch == '\n' || ch == EOF) {
1220                  img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
1221                  return img_BAD;
1222               }
1223               if (off == pimg->buf_len) {
1224                  if (!check_label_space(pimg, pimg->buf_len * 2)) {
1225                     img_errno = IMG_OUTOFMEMORY;
1226                     return img_BAD;
1227                  }
1228               }
1229               pimg->label_buf[off++] = ch;
1230               ch = getc(pimg->fh);
1231            }
1232            pimg->label_buf[off] = '\0';
1233
1234            pimg->label = pimg->label_buf;
1235            if (pimg->label[0] == '\\') pimg->label++;
1236
1237            pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1238
1239            result = img_LABEL;
1240         } else {
1241            img_errno = IMG_BADFORMAT;
1242            return img_BAD; /* unknown keyword */
1243         }
1244      }
1245
1246      if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
1247         img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
1248         return img_BAD;
1249      }
1250
1251      if (result == img_LABEL && pimg->survey_len) {
1252         if (strncmp(pimg->label, pimg->survey, pimg->survey_len + 1) != 0)
1253            goto ascii_again;
1254         pimg->label += pimg->survey_len + 1;
1255      }
1256
1257      return result;
1258   } else if (pimg->version == VERSION_SURVEX_POS) {
1259      /* Survex .pos file */
1260      size_t off;
1261      pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1262      againpos:
1263      off = 0;
1264      while (fscanf(pimg->fh, "(%lf,%lf,%lf ) ", &p->x, &p->y, &p->z) != 3) {
1265         int ch;
1266         if (ferror(pimg->fh)) {
1267            img_errno = IMG_READERROR;
1268            return img_BAD;
1269         }
1270         if (feof(pimg->fh)) return img_STOP;
1271         if (pimg->pending) {
1272            img_errno = IMG_BADFORMAT;
1273            return img_BAD;
1274         }
1275         pimg->pending = 1;
1276         /* ignore rest of line */
1277         do {
1278            ch = getc(pimg->fh);
1279         } while (ch != '\n' && ch != '\r' && ch != EOF);
1280      }
1281
1282      pimg->label_buf[0] = '\0';
1283      while (!feof(pimg->fh)) {
1284         if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
1285            img_errno = IMG_READERROR;
1286            return img_BAD;
1287         }
1288
1289         off += strlen(pimg->label_buf + off);
1290         if (off && pimg->label_buf[off - 1] == '\n') {
1291            pimg->label_buf[off - 1] = '\0';
1292            break;
1293         }
1294         if (!check_label_space(pimg, pimg->buf_len * 2)) {
1295            img_errno = IMG_OUTOFMEMORY;
1296            return img_BAD;
1297         }
1298      }
1299
1300      pimg->label = pimg->label_buf;
1301
1302      if (pimg->label[0] == '\\') pimg->label++;
1303
1304      if (pimg->survey_len) {
1305         size_t l = pimg->survey_len + 1;
1306         if (strncmp(pimg->survey, pimg->label, l) != 0) goto againpos;
1307         pimg->label += l;
1308      }
1309
1310      return img_LABEL;
1311   } else if (pimg->version == VERSION_COMPASS_PLT) {
1312      /* Compass .plt file */
1313      if (pimg->pending > 0) {
1314         /* -1 signals we've entered the first survey we want to
1315          * read, and need to fudge lots if the first action is 'D'...
1316          */
1317         /* pending MOVE or LINE */
1318         int r = pimg->pending - 4;
1319         pimg->pending = 0;
1320         pimg->flags = 0;
1321         pimg->label[pimg->label_len] = '\0';
1322         return r;
1323      }
1324
1325      while (1) {
1326         char *line;
1327         char *q;
1328         size_t len = 0;
1329         int ch = getc(pimg->fh);
1330
1331         switch (ch) {
1332            case '\x1a': case EOF: /* Don't insist on ^Z at end of file */
1333               return img_STOP;
1334            case 'X': case 'F': case 'S':
1335               /* bounding boX (marks end of survey), Feature survey, or
1336                * new Section - skip to next survey */
1337               if (pimg->survey) return img_STOP;
1338skip_to_N:
1339               while (1) {
1340                  do {
1341                     ch = getc(pimg->fh);
1342                  } while (ch != '\n' && ch != '\r' && ch != EOF);
1343                  while (ch == '\n' || ch == '\r') ch = getc(pimg->fh);
1344                  if (ch == 'N') break;
1345                  if (ch == '\x1a' || ch == EOF) return img_STOP;
1346               }
1347               /* FALLTHRU */
1348            case 'N':
1349               line = getline_alloc(pimg->fh);
1350               if (!line) {
1351                  img_errno = IMG_OUTOFMEMORY;
1352                  return img_BAD;
1353               }
1354               while (line[len] > 32) ++len;
1355               if (pimg->label_len == 0) pimg->pending = -1;
1356               if (!check_label_space(pimg, len + 1)) {
1357                  osfree(line);
1358                  img_errno = IMG_OUTOFMEMORY;
1359                  return img_BAD;
1360               }
1361               pimg->label_len = len;
1362               pimg->label = pimg->label_buf;
1363               memcpy(pimg->label, line, len);
1364               pimg->label[len] = '\0';
1365               osfree(line);
1366               break;
1367            case 'M': case 'D': {
1368               /* Move or Draw */
1369               long fpos = -1;
1370               if (pimg->survey && pimg->label_len == 0) {
1371                  /* We're only holding onto this line in case the first line
1372                   * of the 'N' is a 'D', so skip it for now...
1373                   */
1374                  goto skip_to_N;
1375               }
1376               if (ch == 'D' && pimg->pending == -1) {
1377                  if (pimg->survey) {
1378                     fpos = ftell(pimg->fh) - 1;
1379                     fseek(pimg->fh, pimg->start, SEEK_SET);
1380                     ch = getc(pimg->fh);
1381                     pimg->pending = 0;
1382                  } else {
1383                     /* If a file actually has a 'D' before any 'M', then
1384                      * pretend the 'D' is an 'M' - one of the examples
1385                      * in the docs was like this! */
1386                     ch = 'M';
1387                  }
1388               }
1389               line = getline_alloc(pimg->fh);
1390               if (!line) {
1391                  img_errno = IMG_OUTOFMEMORY;
1392                  return img_BAD;
1393               }
1394               /* Compass stores coordinates as North, East, Up = (y,x,z)! */
1395               if (sscanf(line, "%lf%lf%lf", &p->y, &p->x, &p->z) != 3) {
1396                  osfree(line);
1397                  if (ferror(pimg->fh)) {
1398                     img_errno = IMG_READERROR;
1399                  } else {
1400                     img_errno = IMG_BADFORMAT;
1401                  }
1402                  return img_BAD;
1403               }
1404               p->x *= METRES_PER_FOOT;
1405               p->y *= METRES_PER_FOOT;
1406               p->z *= METRES_PER_FOOT;
1407               q = strchr(line, 'S');
1408               if (!q) {
1409                  osfree(line);
1410                  img_errno = IMG_BADFORMAT;
1411                  return img_BAD;
1412               }
1413               ++q;
1414               len = 0;
1415               while (q[len] > ' ') ++len;
1416               q[len] = '\0';
1417               len += 2; /* ' ' and '\0' */
1418               if (!check_label_space(pimg, pimg->label_len + len)) {
1419                  img_errno = IMG_OUTOFMEMORY;
1420                  return img_BAD;
1421               }
1422               pimg->label = pimg->label_buf;
1423               if (pimg->label_len) {
1424                   pimg->label[pimg->label_len] = ' ';
1425                   memcpy(pimg->label + pimg->label_len + 1, q, len - 1);
1426               } else {
1427                   memcpy(pimg->label, q, len - 1);
1428               }
1429               q += len - 1;
1430               /* Now read LRUD.  Technically, this is optional but virtually
1431                * all PLT files have it (with dummy negative values if no LRUD
1432                * was measured) and some versions of Compass can't read PLT
1433                * files without it!
1434                */
1435               while (*q && *q <= ' ') q++;
1436               if (*q == 'P') {
1437                   if (sscanf(q + 1, "%lf%lf%lf%lf",
1438                              &pimg->l, &pimg->r, &pimg->u, &pimg->d) != 4) {
1439                       osfree(line);
1440                       if (ferror(pimg->fh)) {
1441                           img_errno = IMG_READERROR;
1442                       } else {
1443                           img_errno = IMG_BADFORMAT;
1444                       }
1445                       return img_BAD;
1446                   }
1447                   pimg->l *= METRES_PER_FOOT;
1448                   pimg->r *= METRES_PER_FOOT;
1449                   pimg->u *= METRES_PER_FOOT;
1450                   pimg->d *= METRES_PER_FOOT;
1451               } else {
1452                   pimg->l = pimg->r = pimg->u = pimg->d = -1;
1453               }
1454               osfree(line);
1455               pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1456               if (fpos != -1) {
1457                  fseek(pimg->fh, fpos, SEEK_SET);
1458               } else {
1459                  pimg->pending = (ch == 'M' ? img_MOVE : img_LINE) + 4;
1460               }
1461               return img_LABEL;
1462            }
1463            default:
1464               img_errno = IMG_BADFORMAT;
1465               return img_BAD;
1466         }
1467      }
1468   } else {
1469      /* CMAP .xyz file */
1470      char *line = NULL;
1471      char *q;
1472      size_t len;
1473
1474      if (pimg->pending) {
1475         /* pending MOVE or LINE or LABEL or STOP */
1476         int r = pimg->pending - 4;
1477         /* Set label to empty - don't use "" as we adjust label relative
1478          * to label_buf when label_buf is reallocated. */
1479         pimg->label = pimg->label_buf + strlen(pimg->label_buf);
1480         pimg->flags = 0;
1481         if (r == img_LABEL) {
1482            /* nasty magic */
1483            read_xyz_shot_coords(p, pimg->label_buf + 16);
1484            subtract_xyz_shot_deltas(p, pimg->label_buf + 16);
1485            pimg->pending = img_STOP + 4;
1486            return img_MOVE;
1487         }
1488
1489         pimg->pending = 0;
1490
1491         if (r == img_STOP) {
1492            /* nasty magic */
1493            read_xyz_shot_coords(p, pimg->label_buf + 16);
1494            return img_LINE;
1495         }
1496
1497         return r;
1498      }
1499
1500      pimg->label = pimg->label_buf;
1501      do {
1502         osfree(line);
1503         if (feof(pimg->fh)) return img_STOP;
1504         line = getline_alloc(pimg->fh);
1505         if (!line) {
1506            img_errno = IMG_OUTOFMEMORY;
1507            return img_BAD;
1508         }
1509      } while (line[0] == ' ' || line[0] == '\0');
1510      if (line[0] == '\x1a') return img_STOP;
1511
1512      len = strlen(line);
1513      if (pimg->version == VERSION_CMAP_STATION) {
1514         /* station variant */
1515         if (len < 37) {
1516            osfree(line);
1517            img_errno = IMG_BADFORMAT;
1518            return img_BAD;
1519         }
1520         memcpy(pimg->label, line, 6);
1521         q = memchr(pimg->label, ' ', 6);
1522         if (!q) q = pimg->label + 6;
1523         *q = '\0';
1524
1525         read_xyz_station_coords(p, line);
1526
1527         /* FIXME: look at prev for lines (line + 32, 5) */
1528         /* FIXME: duplicate stations... */
1529         return img_LABEL;
1530      } else {
1531         /* Shot variant */
1532         char old[8], new[8];
1533         if (len < 61) {
1534            osfree(line);
1535            img_errno = IMG_BADFORMAT;
1536            return img_BAD;
1537         }
1538
1539         memcpy(old, line, 7);
1540         q = memchr(old, ' ', 7);
1541         if (!q) q = old + 7;
1542         *q = '\0';
1543
1544         memcpy(new, line + 7, 7);
1545         q = memchr(new, ' ', 7);
1546         if (!q) q = new + 7;
1547         *q = '\0';
1548
1549         pimg->flags = img_SFLAG_UNDERGROUND;
1550
1551         if (strcmp(old, new) == 0) {
1552            pimg->pending = img_MOVE + 4;
1553            read_xyz_shot_coords(p, line);
1554            strcpy(pimg->label, new);
1555            osfree(line);
1556            return img_LABEL;
1557         }
1558
1559         if (strcmp(old, pimg->label) == 0) {
1560            pimg->pending = img_LINE + 4;
1561            read_xyz_shot_coords(p, line);
1562            strcpy(pimg->label, new);
1563            osfree(line);
1564            return img_LABEL;
1565         }
1566
1567         pimg->pending = img_LABEL + 4;
1568         read_xyz_shot_coords(p, line);
1569         strcpy(pimg->label, new);
1570         memcpy(pimg->label + 16, line, 70);
1571
1572         osfree(line);
1573         return img_LABEL;
1574      }
1575   }
1576}
1577
1578static int
1579write_v3label(img *pimg, int opt, const char *s)
1580{
1581   size_t len, n, dot;
1582
1583   /* find length of common prefix */
1584   dot = 0;
1585   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
1586      if (s[len] == '.') dot = len + 1;
1587   }
1588
1589   SVX_ASSERT(len <= pimg->label_len);
1590   n = pimg->label_len - len;
1591   if (len == 0) {
1592      if (pimg->label_len) putc(0, pimg->fh);
1593   } else if (n <= 16) {
1594      if (n) putc(n + 15, pimg->fh);
1595   } else if (dot == 0) {
1596      if (pimg->label_len) putc(0, pimg->fh);
1597      len = 0;
1598   } else {
1599      const char *p = pimg->label_buf + dot;
1600      n = 1;
1601      for (len = pimg->label_len - dot - 17; len; len--) {
1602         if (*p++ == '.') n++;
1603      }
1604      if (n <= 14) {
1605         putc(n, pimg->fh);
1606         len = dot;
1607      } else {
1608         if (pimg->label_len) putc(0, pimg->fh);
1609         len = 0;
1610      }
1611   }
1612
1613   n = strlen(s + len);
1614   putc(opt, pimg->fh);
1615   if (n < 0xfe) {
1616      putc(n, pimg->fh);
1617   } else if (n < 0xffff + 0xfe) {
1618      putc(0xfe, pimg->fh);
1619      put16((short)(n - 0xfe), pimg->fh);
1620   } else {
1621      putc(0xff, pimg->fh);
1622      put32(n, pimg->fh);
1623   }
1624   fwrite(s + len, n, 1, pimg->fh);
1625
1626   n += len;
1627   pimg->label_len = n;
1628   if (!check_label_space(pimg, n + 1))
1629      return 0; /* FIXME: distinguish out of memory... */
1630   memcpy(pimg->label_buf + len, s + len, n - len + 1);
1631
1632   return !ferror(pimg->fh);
1633}
1634
1635static void
1636img_write_item_date(img *pimg)
1637{
1638    if (pimg->date1 != pimg->olddate1 ||
1639            pimg->date2 != pimg->olddate2) {
1640        /* Only write dates when they've changed. */
1641        if (pimg->date1 == pimg->date2) {
1642            putc(0x20, pimg->fh);
1643            put32(pimg->date1, pimg->fh);
1644        } else {
1645            putc(0x21, pimg->fh);
1646            put32(pimg->date1, pimg->fh);
1647            put32(pimg->date2, pimg->fh);
1648        }
1649        pimg->olddate1 = pimg->date1;
1650        pimg->olddate2 = pimg->date2;
1651    }
1652}
1653
1654void
1655img_write_item(img *pimg, int code, int flags, const char *s,
1656               double x, double y, double z)
1657{
1658   if (!pimg) return;
1659   if (pimg->version >= 3) {
1660      int opt = 0;
1661      switch (code) {
1662       case img_LABEL:
1663         write_v3label(pimg, 0x40 | flags, s);
1664         opt = 0;
1665         break;
1666       case img_XSECT: {
1667         INT32_T l, r, u, d, max_dim;
1668         /* Need at least version 5 for img_XSECT. */
1669         if (pimg->version < 5) break;
1670         img_write_item_date(pimg);
1671         l = (INT32_T)my_round(pimg->l * 100.0);
1672         r = (INT32_T)my_round(pimg->r * 100.0);
1673         u = (INT32_T)my_round(pimg->u * 100.0);
1674         d = (INT32_T)my_round(pimg->d * 100.0);
1675         if (l < 0) l = -1;
1676         if (r < 0) r = -1;
1677         if (u < 0) u = -1;
1678         if (d < 0) d = -1;
1679         max_dim = max(max(l, r), max(u, d));
1680         flags &= 1;
1681         if (max_dim >= 32768) flags |= 2;
1682         write_v3label(pimg, 0x30 | flags, s);
1683         if (flags & 2) {
1684            /* Big passage!  Need to use 4 bytes. */
1685            put32(l, pimg->fh);
1686            put32(r, pimg->fh);
1687            put32(u, pimg->fh);
1688            put32(d, pimg->fh);
1689         } else {
1690            put16(l, pimg->fh);
1691            put16(r, pimg->fh);
1692            put16(u, pimg->fh);
1693            put16(d, pimg->fh);
1694         }
1695         return;
1696       }
1697       case img_MOVE:
1698         opt = 15;
1699         break;
1700       case img_LINE:
1701         if (pimg->version >= 4) {
1702             img_write_item_date(pimg);
1703         }
1704         write_v3label(pimg, 0x80 | flags, s ? s : "");
1705         opt = 0;
1706         break;
1707       default: /* ignore for now */
1708         return;
1709      }
1710      if (opt) putc(opt, pimg->fh);
1711      /* Output in cm */
1712      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1713      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1714      put32((INT32_T)my_round(z * 100.0), pimg->fh);
1715   } else {
1716      size_t len;
1717      INT32_T opt = 0;
1718      SVX_ASSERT(pimg->version > 0);
1719      switch (code) {
1720       case img_LABEL:
1721         if (pimg->version == 1) {
1722            /* put a move before each label */
1723            img_write_item(pimg, img_MOVE, 0, NULL, x, y, z);
1724            put32(2, pimg->fh);
1725            fputsnl(s, pimg->fh);
1726            return;
1727         }
1728         len = strlen(s);
1729         if (len > 255 || strchr(s, '\n')) {
1730            /* long label - not in early incarnations of v2 format, but few
1731             * 3d files will need these, so better not to force incompatibility
1732             * with a new version I think... */
1733            putc(7, pimg->fh);
1734            putc(flags, pimg->fh);
1735            put32(len, pimg->fh);
1736            fputs(s, pimg->fh);
1737         } else {
1738            putc(0x40 | (flags & 0x3f), pimg->fh);
1739            fputsnl(s, pimg->fh);
1740         }
1741         opt = 0;
1742         break;
1743       case img_MOVE:
1744         opt = 4;
1745         break;
1746       case img_LINE:
1747         if (pimg->version > 1) {
1748            opt = 0x80 | (flags & 0x3f);
1749            break;
1750         }
1751         opt = 5;
1752         break;
1753       default: /* ignore for now */
1754         return;
1755      }
1756      if (pimg->version == 1) {
1757         put32(opt, pimg->fh);
1758      } else {
1759         if (opt) putc(opt, pimg->fh);
1760      }
1761      /* Output in cm */
1762      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1763      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1764      put32((INT32_T)my_round(z * 100.0), pimg->fh);
1765   }
1766}
1767
1768/* Write error information for the current traverse
1769 * n_legs is the number of legs in the traverse
1770 * length is the traverse length (in m)
1771 * E is the ratio of the observed misclosure to the theoretical one
1772 * H is the ratio of the observed horizontal misclosure to the theoretical one
1773 * V is the ratio of the observed vertical misclosure to the theoretical one
1774 */
1775void
1776img_write_errors(img *pimg, int n_legs, double length,
1777                 double E, double H, double V)
1778{
1779    putc(0x22, pimg->fh);
1780    put32(n_legs, pimg->fh);
1781    put32((INT32_T)my_round(length * 100.0), pimg->fh);
1782    put32((INT32_T)my_round(E * 100.0), pimg->fh);
1783    put32((INT32_T)my_round(H * 100.0), pimg->fh);
1784    put32((INT32_T)my_round(V * 100.0), pimg->fh);
1785}
1786
1787int
1788img_close(img *pimg)
1789{
1790   int result = 1;
1791   if (pimg) {
1792      if (pimg->fh) {
1793         if (pimg->fRead) {
1794            osfree(pimg->survey);
1795            osfree(pimg->title);
1796            osfree(pimg->datestamp);
1797         } else {
1798            /* write end of data marker */
1799            switch (pimg->version) {
1800             case 1:
1801               put32((INT32_T)-1, pimg->fh);
1802               break;
1803             case 2:
1804               putc(0, pimg->fh);
1805               break;
1806             default:
1807               if (pimg->label_len) putc(0, pimg->fh);
1808               putc(0, pimg->fh);
1809               break;
1810            }
1811         }
1812         if (ferror(pimg->fh)) result = 0;
1813         if (fclose(pimg->fh)) result = 0;
1814         if (!result) img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
1815      }
1816      osfree(pimg->label_buf);
1817      osfree(pimg->filename_opened);
1818      osfree(pimg);
1819   }
1820   return result;
1821}
Note: See TracBrowser for help on using the repository browser.