source: git/src/img.c @ 422eb92

RELEASE/1.0RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereostereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 422eb92 was 9f9c05e, checked in by Olly Betts <olly@…>, 23 years ago

Improved handling of Compass PLT files and CMAP xyz files - no longer change
dots to spaces in survey and station names - instead use a space as the
separator between survey name and station name.

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

  • Property mode set to 100644
File size: 37.2 KB
RevLine 
[0da6e60]1/* img.c
[d1b1380]2 * Routines for reading and writing Survex ".3d" image files
[ad4eaf3f]3 * Copyright (C) 1993-2002 Olly Betts
[846746e]4 *
[89231c4]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.
[846746e]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
[89231c4]12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
[846746e]14 *
[89231c4]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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
[d1b1380]18 */
19
[a420b49]20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
[d1b1380]24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27#include <time.h>
28#include <ctype.h>
29
[72af530]30#include "img.h"
[7d27e36]31
[72af530]32#ifdef IMG_HOSTED
[d69255f]33# include "debug.h"
[d1b1380]34# include "filelist.h"
[d69255f]35# include "filename.h"
36# include "message.h"
37# include "useful.h"
[a420b49]38# define TIMENA msg(/*Date and time not available.*/108)
39# define TIMEFMT msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107)
[d1b1380]40#else
[72af530]41# define INT32_T long
[d1b1380]42# define TIMENA "Time not available."
43# define TIMEFMT "%a,%Y.%m.%d %H:%M:%S %Z"
44# define EXT_SVX_3D "3d"
45# define xosmalloc(L) malloc((L))
[72af530]46# define xosrealloc(L,S) realloc((L),(S))
[d1b1380]47# define osfree(P) free((P))
48# define ossizeof(T) sizeof(T)
[72af530]49/* in IMG_HOSTED mode, this tests if a filename refers to a directory */
[d1b1380]50# define fDirectory(X) 0
51/* open file FNM with mode MODE, maybe using path PTH and/or extension EXT */
52/* path isn't used in img.c, but EXT is */
53# define fopenWithPthAndExt(PTH,FNM,EXT,MODE,X) fopen(FNM,MODE)
54# define fFalse 0
55# define fTrue 1
56# define bool int
57/* dummy do {...} while(0) hack to permit stuff like
58 * if (s) fputsnl(s,fh); else exit(1);
59 * to work as intended
60 */
[23f7ea7]61# define fputsnl(S, FH) do {fputs((S), (FH)); putc('\n', (FH));} while(0)
[4c07c51]62# define SVX_ASSERT(X)
[d1b1380]63
[a420b49]64static long
65get32(FILE *fh)
66{
[ad9a5cd]67   long w = getc(fh);
[a420b49]68   w |= (long)getc(fh) << 8l;
69   w |= (long)getc(fh) << 16l;
70   w |= (long)getc(fh) << 24l;
71   return w;
[d1b1380]72}
73
[a420b49]74static void
75put32(long w, FILE *fh)
76{
77   putc((char)(w), fh);
78   putc((char)(w >> 8l), fh);
79   putc((char)(w >> 16l), fh);
80   putc((char)(w >> 24l), fh);
[d1b1380]81}
[5c3c61a]82
83#endif
84
[dd1d6369]85#ifdef HAVE_ROUND
86# include <math.h>
87extern double round(double); /* prototype is often missing... */
88# define my_round round
89#else
90static double
91my_round(double x) {
92   if (x >= 0.0) return floor(x + 0.5);
93   return ceil(x - 0.5);
94}
95#endif
96
[ad4eaf3f]97/* portable case insensitive string compare */
98#if defined(strcasecmp) || defined(HAVE_STRCASECMP)
99# define my_strcasecmp strcasecmp
100#else
101/* What about top bit set chars? */
102int my_strcasecmp(const char *s1, const char *s2) {
103   register c1, c2;
104   do {
105      c1 = *s1++;
106      c2 = *s2++;
107   } while (c1 && toupper(c1) == toupper(c2));
108   /* now calculate real difference */
109   return c1 - c2;
110}
111#endif
112
[d69255f]113unsigned int img_output_version = 3;
[5c3c61a]114
[72af530]115#ifdef IMG_HOSTED
[5c3c61a]116static enum {
117   IMG_NONE = 0,
[759fb47]118   IMG_FILENOTFOUND = /*Couldn't open data file `%s'*/24,
[5c3c61a]119   IMG_OUTOFMEMORY  = /*Out of memory %.0s*/38,
[759fb47]120   IMG_DIRECTORY    = /*Filename `%s' refers to directory*/44,
121   IMG_CANTOPENOUT  = /*Failed to open output file `%s'*/47,
[ad9a5cd]122   IMG_BADFORMAT    = /*Bad 3d image file `%s'*/106,
123   IMG_READERROR    = /*Error reading from file `%s'*/109,
[6c1c4f3]124   IMG_WRITEERROR   = /*Error writing to file `%s'*/110,
125   IMG_TOONEW       = /*File `%s' has a newer format than this program can understand*/114
[5c3c61a]126} img_errno = IMG_NONE;
127#else
128static img_errcode img_errno = IMG_NONE;
[c0563da]129#endif
[d1b1380]130
[7d27e36]131#define FILEID "Survex 3D Image File"
132
[ad4eaf3f]133#define EXT_PLT "plt"
[d16db18]134#define EXT_PLF "plf"
[f1c7c9c7]135#define EXT_XYZ "xyz"
[ad4eaf3f]136
[0da6e60]137/* Attempt to string paste to ensure we are passed a literal string */
138#define LITLEN(S) (sizeof(S"") - 1)
139
[7d27e36]140static char *
141my_strdup(const char *str)
[a420b49]142{
[7d27e36]143   char *p;
144   size_t len = strlen(str) + 1;
145   p = xosmalloc(len);
146   if (p) memcpy(p, str, len);
147   return p;
[d1b1380]148}
[c0563da]149
[a2ad284]150static char *
151getline_alloc(FILE *fh)
152{
153   int ch;
154   size_t i = 0;
155   size_t len = 16;
156   char *buf = xosmalloc(len);
157   if (!buf) return NULL;
158
159   ch = getc(fh);
160   while (ch != '\n' && ch != '\r' && ch != EOF) {
161      buf[i++] = ch;
162      if (i == len - 1) {
163         char *p;
164         len += len;
165         p = xosrealloc(buf, len);
166         if (!p) {
167            osfree(buf);
168            return NULL;
169         }
170         buf = p;
171      }
172      ch = getc(fh);
173   }
174   if (ch == '\n' || ch == '\r') {
[4ebc557]175      int otherone = ch ^ ('\n' ^ '\r');
176      ch = getc(fh);
177      /* if it's not the other eol character, put it back */
178      if (ch != otherone) ungetc(ch, fh);
[a2ad284]179   }
[421b7d2]180   buf[i++] = '\0';
[a2ad284]181   return buf;
182}
183
[72af530]184#ifndef IMG_HOSTED
[a420b49]185img_errcode
186img_error(void)
187{
188   return img_errno;
[d1b1380]189}
190#else
[a420b49]191int
192img_error(void)
193{
194   return (int)img_errno;
[d1b1380]195}
196#endif
197
[d4d3655]198static bool
199check_label_space(img *pimg, size_t len)
200{
201   if (len > pimg->buf_len) {
202      char *b = xosrealloc(pimg->label_buf, len);
203      if (!b) return fFalse;
204      pimg->label = (pimg->label - pimg->label_buf) + b;
205      pimg->label_buf = b;
206      pimg->buf_len = len;
207   }
208   return fTrue;
209}
210
[d16db18]211#define has_ext(F,L,E) ((L) > LITLEN(E) + 1 &&\
212                        (F)[(L) - LITLEN(E) - 1] == FNM_SEP_EXT &&\
213                        my_strcasecmp((F) + (L) - LITLEN(E), E) == 0)
214
[a420b49]215img *
[a2ad284]216img_open_survey(const char *fnm, const char *survey)
[a420b49]217{
218   img *pimg;
[0da6e60]219   size_t len;
[7d27e36]220   char buf[LITLEN(FILEID) + 1];
221   int ch;
[a420b49]222
223   if (fDirectory(fnm)) {
224      img_errno = IMG_DIRECTORY;
225      return NULL;
226   }
[d1b1380]227
[a420b49]228   pimg = (img *)xosmalloc(ossizeof(img));
229   if (pimg == NULL) {
230      img_errno = IMG_OUTOFMEMORY;
231      return NULL;
232   }
[d1b1380]233
[23f7ea7]234   pimg->buf_len = 257;
[0ff23cb]235   pimg->label_buf = xosmalloc(pimg->buf_len);
236   if (!pimg->label_buf) {
[23f7ea7]237      osfree(pimg);
238      img_errno = IMG_OUTOFMEMORY;
239      return NULL;
240   }
241
[a420b49]242   pimg->fh = fopenWithPthAndExt("", fnm, EXT_SVX_3D, "rb", NULL);
243   if (pimg->fh == NULL) {
[ce1e5ac]244      osfree(pimg->label_buf);
[47e4a2e]245      osfree(pimg);
[a420b49]246      img_errno = IMG_FILENOTFOUND;
247      return NULL;
248   }
[d1b1380]249
[0da6e60]250   pimg->fRead = fTrue; /* reading from this file */
251   img_errno = IMG_NONE;
252
253   pimg->flags = 0;
254
255   /* for version 3 we use label_buf to store the prefix for reuse */
[4ebc557]256   /* for version -2, 0 value indicates we haven't entered a survey yet */
[f1c7c9c7]257   /* for version -4, we store the last station here to detect whether
258    * we MOVE or LINE */
[0da6e60]259   pimg->label_len = 0;
[f1c7c9c7]260   pimg->label_buf[0] = '\0';
[0da6e60]261
262   pimg->survey = NULL;
263   pimg->survey_len = 0;
[9f9c05e]264   pimg->separator = '.';
[0da6e60]265
[c637fad]266   pimg->title = pimg->datestamp = NULL;
[0da6e60]267   if (survey) {
268      len = strlen(survey);
269      if (len) {
270         if (survey[len - 1] == '.') len--;
271         if (len) {
272            char *p;
273            pimg->survey = osmalloc(len + 2);
274            memcpy(pimg->survey, survey, len);
275            /* Set title to leaf survey name */
276            pimg->survey[len] = '\0';
277            p = strchr(pimg->survey, '.');
278            if (p) p++; else p = pimg->survey;
[7d27e36]279            pimg->title = my_strdup(p);
280            if (!pimg->title) {
281               img_errno = IMG_OUTOFMEMORY;
282               goto error;
283            }
[0da6e60]284            pimg->survey[len] = '.';
285            pimg->survey[len + 1] = '\0';
286         }
287      }
288      pimg->survey_len = len;
289   }
290
[f1c7c9c7]291   /* [version -2, -3, -4] pending IMG_LINE or IMG_MOVE - both have 4 added
[ad4eaf3f]292    * [version -1] already skipped heading line, or there wasn't one
[0da6e60]293    * [version 0] not in the middle of a 'LINE' command
294    * [version 3] not in the middle of turning a LINE into a MOVE
295    */
296   pimg->pending = 0;
297
298   len = strlen(fnm);
[d16db18]299   if (has_ext(fnm, len, EXT_SVX_POS)) {
[f830e0d]300pos_file:
[0da6e60]301      pimg->version = -1;
[984f49e]302      if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
[7d27e36]303      pimg->datestamp = my_strdup(TIMENA);
304      if (!pimg->datestamp) {
305         img_errno = IMG_OUTOFMEMORY;
306         goto error;
307      }
[58f02ae]308      pimg->start = 0;
[0da6e60]309      return pimg;
310   }
[5757725]311
[d16db18]312   if (has_ext(fnm, len, EXT_PLT) || has_ext(fnm, len, EXT_PLF)) {
[4ebc557]313      long fpos;
[27bd06b]314plt_file:
[ad4eaf3f]315      pimg->version = -2;
[9f9c05e]316      /* Spaces aren't legal in Compass station names, but dots are, so
317       * use space as the level separator */
318      pimg->separator = ' ';
[4ebc557]319      pimg->start = 0;
[984f49e]320      if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
[ad4eaf3f]321      pimg->datestamp = my_strdup(TIMENA);
322      if (!pimg->datestamp) {
323         img_errno = IMG_OUTOFMEMORY;
324         goto error;
325      }
[4ebc557]326      while (1) {
327         int ch = getc(pimg->fh);
328         switch (ch) {
329          case '\x1a':
330            fseek(pimg->fh, -1, SEEK_CUR);
331            /* FALL THRU */
332          case EOF:
333            pimg->start = ftell(pimg->fh);
334            return pimg;
335          case 'N': {
336            char *line, *q;
337            fpos = ftell(pimg->fh) - 1;
338            if (!pimg->survey) {
339               /* FIXME : if there's only one survey in the file, it'd be nice
340                * to use its description as the title here...
341                */
[0d922fc]342               ungetc('N', pimg->fh);
[4ebc557]343               pimg->start = fpos;
[984f49e]344               return pimg;
345            }
[4ebc557]346            line = getline_alloc(pimg->fh);
[f1c7c9c7]347            if (!line) {
348               img_errno = IMG_OUTOFMEMORY;
349               goto error;
350            }
[0d922fc]351            len = 0;
[4ebc557]352            while (line[len] > 32) ++len;
353            if (pimg->survey_len != len ||
354                memcmp(line, pimg->survey, len) != 0) {
[ad4eaf3f]355               osfree(line);
[4ebc557]356               continue;
[ad4eaf3f]357            }
[4ebc557]358            q = strchr(line + len, 'C');
359            if (q && q[1]) {
360                osfree(pimg->title);
[f1c7c9c7]361                pimg->title = my_strdup(q + 1);
[4ebc557]362            } else if (!pimg->title) {
[f1c7c9c7]363                pimg->title = my_strdup(pimg->label);
[4ebc557]364            }
365            osfree(line);
366            if (!pimg->title) {
367                img_errno = IMG_OUTOFMEMORY;
368                goto error;
369            }
370            if (!pimg->start) pimg->start = fpos;
[27bd06b]371            fseek(pimg->fh, pimg->start, SEEK_SET);
[4ebc557]372            return pimg;
373          }
374          case 'M': case 'D':
375            pimg->start = ftell(pimg->fh) - 1;
376            break;
[984f49e]377         }
[4ebc557]378         while (ch != '\n' && ch != '\r') {
379            ch = getc(pimg->fh);
[984f49e]380         }
381      }
[ad4eaf3f]382   }
[0da6e60]383
[d16db18]384   if (has_ext(fnm, len, EXT_XYZ)) {
[f1c7c9c7]385      char *line;
386xyz_file:
[9f9c05e]387      /* Spaces aren't legal in CMAP station names, but dots are, so
388       * use space as the level separator */
389      pimg->separator = ' ';
[f1c7c9c7]390      line = getline_alloc(pimg->fh);
391      if (!line) {
392         img_errno = IMG_OUTOFMEMORY;
393         goto error;
394      }
395      /* FIXME: reparse date? */
396      len = strlen(line);
397      if (len > 59) line[59] = '\0';
398      if (len > 45) {
399         pimg->datestamp = my_strdup(line + 45);
400      } else {
401         pimg->datestamp = my_strdup(TIMENA);
402      }
403      if (strncmp(line, "  Cave Survey Data Processed by CMAP ",
404                  LITLEN("  Cave Survey Data Processed by CMAP ")) == 0) {
405         len = 0;
406      } else {
407         if (len > 45) {
408            line[45] = '\0';
409            len = 45;
410         }
411         while (len > 2 && line[len - 1] == ' ') --len;
412         if (len > 2) {
413            line[len] = '\0';
414            pimg->title = my_strdup(line + 2);
415         }
416      }
417      if (len <= 2) pimg->title = baseleaf_from_fnm(fnm);
418      osfree(line);
419      if (!pimg->datestamp || !pimg->title) {
420         img_errno = IMG_OUTOFMEMORY;
421         goto error;
422      }
423      line = getline_alloc(pimg->fh);
424      if (!line) {
425         img_errno = IMG_OUTOFMEMORY;
426         goto error;
427      }
428      if (line[0] != ' ' || (line[1] != 'S' && line[1] != 'O')) {
429         img_errno = IMG_BADFORMAT;
430         goto error;
431      }
432      if (line[1] == 'S') {
433         pimg->version = -3; /* Station format */
434      } else {
435         pimg->version = -4; /* Shot format */
436      }
437      osfree(line);
438      line = getline_alloc(pimg->fh);
439      if (!line) {
440         img_errno = IMG_OUTOFMEMORY;
441         goto error;
442      }
443      if (line[0] != ' ' || line[1] != '-') {
444         img_errno = IMG_BADFORMAT;
445         goto error;
446      }
447      osfree(line);
448      pimg->start = ftell(pimg->fh);
449      return pimg;
450   }
451
[7d27e36]452   if (fread(buf, LITLEN(FILEID) + 1, 1, pimg->fh) != 1 ||
[d16db18]453       memcmp(buf, FILEID"\n", LITLEN(FILEID) + 1) != 0) {
454      rewind(pimg->fh);
[f1c7c9c7]455      if (buf[1] == ' ') {
456         if (buf[0] == ' ') {
457            /* Looks like a CMAP .xyz file ... */
458            goto xyz_file;
459         } else if (strchr("ZSNF", buf[0])) {
460            /* Looks like a Compass .plt file ... */
461            /* Almost certainly it'll start "Z " */
462            goto plt_file;
463         }
[27bd06b]464      }
[f830e0d]465      if (buf[0] == '(') {
466         /* Looks like a Survex .pos file ... */
467         goto pos_file;
468      }
[a420b49]469      img_errno = IMG_BADFORMAT;
[58f02ae]470      goto error;
[a420b49]471   }
[d1b1380]472
[7d27e36]473   /* check file format version */
474   ch = getc(pimg->fh);
475   pimg->version = 0;
476   if (tolower(ch) == 'b') {
477      /* binary file iff B/b prefix */
478      pimg->version = 1;
479      ch = getc(pimg->fh);
480   }
481   if (ch != 'v') {
482      img_errno = IMG_BADFORMAT;
483      goto error;
484   }
485   ch = getc(pimg->fh);
486   if (ch == '0') {
487      if (fread(buf, 4, 1, pimg->fh) != 1 || memcmp(buf, ".01\n", 4) != 0) {
488         img_errno = IMG_BADFORMAT;
489         goto error;
490      }
[0da6e60]491      /* nothing special to do */
[7d27e36]492   } else if (pimg->version == 0) {
493      if (ch < '2' || ch > '3' || getc(pimg->fh) != '\n') {
[6c1c4f3]494         img_errno = IMG_TOONEW;
[58f02ae]495         goto error;
[6c1c4f3]496      }
[7d27e36]497      pimg->version = ch - '0';
[5c3c61a]498   } else {
[647407d]499      img_errno = IMG_BADFORMAT;
[58f02ae]500      goto error;
[a420b49]501   }
[421b7d2]502
[4d3dfdf]503   if (!pimg->title)
504       pimg->title = getline_alloc(pimg->fh);
505   else
506       osfree(getline_alloc(pimg->fh));
[a2ad284]507   pimg->datestamp = getline_alloc(pimg->fh);
508   if (!pimg->title || !pimg->datestamp) {
[58f02ae]509      img_errno = IMG_OUTOFMEMORY;
510      error:
[a2ad284]511      osfree(pimg->title);
[984f49e]512      osfree(pimg->datestamp);
[a2ad284]513      fclose(pimg->fh);
514      osfree(pimg);
515      return NULL;
516   }
517
[58f02ae]518   pimg->start = ftell(pimg->fh);
[421b7d2]519
[a420b49]520   return pimg;
[d1b1380]521}
522
[9f3e5df]523int
[f2588ca]524img_rewind(img *pimg)
525{
[9f3e5df]526   if (!pimg->fRead) {
527      img_errno = IMG_WRITEERROR;
528      return 0;
529   }
530   if (fseek(pimg->fh, pimg->start, SEEK_SET) != 0) {
531      img_errno = IMG_READERROR;
532      return 0;
533   }
[58f02ae]534   clearerr(pimg->fh);
535   /* [version -1] already skipped heading line, or there wasn't one
536    * [version 0] not in the middle of a 'LINE' command
[76bbb7c9]537    * [version 3] not in the middle of turning a LINE into a MOVE */
[4ebc557]538   pimg->pending = 0;
[d69255f]539
540   img_errno = IMG_NONE;
[99cf51a]541
[5d7280f8]542   /* for version 3 we use label_buf to store the prefix for reuse */
[4ebc557]543   /* for version -2, 0 value indicates we haven't entered a survey yet */
[f1c7c9c7]544   /* for version -4, we store the last station here to detect whether
545    * we MOVE or LINE */
[d69255f]546   pimg->label_len = 0;
[9f3e5df]547   return 1;
[f2588ca]548}
549
[a420b49]550img *
[fe8e80e]551img_open_write(const char *fnm, char *title_buf, bool fBinary)
[a420b49]552{
553   time_t tm;
554   img *pimg;
[d1b1380]555
[d69255f]556   fBinary = fBinary;
557
[a420b49]558   if (fDirectory(fnm)) {
559      img_errno = IMG_DIRECTORY;
560      return NULL;
561   }
[d1b1380]562
[a420b49]563   pimg = (img *)xosmalloc(ossizeof(img));
564   if (pimg == NULL) {
565      img_errno = IMG_OUTOFMEMORY;
566      return NULL;
567   }
[d1b1380]568
[fe8e80e]569   pimg->buf_len = 257;
[0ff23cb]570   pimg->label_buf = xosmalloc(pimg->buf_len);
571   if (!pimg->label_buf) {
[fe8e80e]572      osfree(pimg);
573      img_errno = IMG_OUTOFMEMORY;
574      return NULL;
575   }
576
[a420b49]577   pimg->fh = fopen(fnm, "wb");
578   if (!pimg->fh) {
[ce1e5ac]579      osfree(pimg->label_buf);
[47e4a2e]580      osfree(pimg);
[a420b49]581      img_errno = IMG_CANTOPENOUT;
582      return NULL;
583   }
[d1b1380]584
[a420b49]585   /* Output image file header */
586   fputs("Survex 3D Image File\n", pimg->fh); /* file identifier string */
[5c3c61a]587   if (img_output_version < 2) {
[d69255f]588      pimg->version = 1;
589      fputs("Bv0.01\n", pimg->fh); /* binary file format version number */
[5c3c61a]590   } else {
[d69255f]591      pimg->version = (img_output_version > 2) ? 3 : 2;
[5c3c61a]592      fprintf(pimg->fh, "v%d\n", pimg->version); /* file format version no. */
593   }
[fe8e80e]594   fputsnl(title_buf, pimg->fh);
[a420b49]595   tm = time(NULL);
596   if (tm == (time_t)-1) {
597      fputsnl(TIMENA, pimg->fh);
598   } else {
[7d27e36]599      char date[256];
[a420b49]600      /* output current date and time in format specified */
[7d27e36]601      strftime(date, 256, TIMEFMT, localtime(&tm));
602      fputsnl(date, pimg->fh);
[a420b49]603   }
604   pimg->fRead = fFalse; /* writing to this file */
605   img_errno = IMG_NONE;
[d69255f]606
[5d7280f8]607   /* for version 3 we use label_buf to store the prefix for reuse */
[0ff23cb]608   pimg->label_buf[0] = '\0';
[d69255f]609   pimg->label_len = 0;
610
[ad9a5cd]611   /* Don't check for write errors now - let img_close() report them... */
[a420b49]612   return pimg;
[d1b1380]613}
614
[f1c7c9c7]615static void
616read_xyz_station_coords(img_point *pt, const char *line)
617{
618   char num[12];
619   memcpy(num, line + 6, 9);
620   num[9] = '\0';
621   pt->x = atof(num) / METRES_PER_FOOT;
622   memcpy(num, line + 15, 9);
623   pt->y = atof(num) / METRES_PER_FOOT;
624   memcpy(num, line + 24, 8);
625   num[8] = '\0';
626   pt->z = atof(num) / METRES_PER_FOOT;
627}
628
629static void
630read_xyz_shot_coords(img_point *pt, const char *line)
631{
632   char num[12];
633   memcpy(num, line + 40, 10);
634   num[10] = '\0';
635   pt->x = atof(num) / METRES_PER_FOOT;
636   memcpy(num, line + 50, 10);
637   pt->y = atof(num) / METRES_PER_FOOT;
638   memcpy(num, line + 60, 9);
639   num[9] = '\0';
640   pt->z = atof(num) / METRES_PER_FOOT;
641}
642
643static void
644subtract_xyz_shot_deltas(img_point *pt, const char *line)
645{
646   char num[12];
647   memcpy(num, line + 15, 9);
648   num[9] = '\0';
649   pt->x -= atof(num) / METRES_PER_FOOT;
650   memcpy(num, line + 24, 8);
651   num[8] = '\0';
652   pt->y -= atof(num) / METRES_PER_FOOT;
653   memcpy(num, line + 32, 8);
654   pt->z -= atof(num) / METRES_PER_FOOT;
655}
656
[ad9a5cd]657static int
658read_coord(FILE *fh, img_point *pt)
659{
[4c07c51]660   SVX_ASSERT(fh);
661   SVX_ASSERT(pt);
[ad9a5cd]662   pt->x = get32(fh) / 100.0;
663   pt->y = get32(fh) / 100.0;
664   pt->z = get32(fh) / 100.0;
665   if (ferror(fh) || feof(fh)) {
666      img_errno = feof(fh) ? IMG_BADFORMAT : IMG_READERROR;
667      return 0;
[421b7d2]668   }
[ad9a5cd]669   return 1;
670}
671
[4d3dfdf]672static int
673skip_coord(FILE *fh)
674{
675    return (fseek(fh, 12, SEEK_CUR) == 0);
676}
677
[a420b49]678int
[23f7ea7]679img_read_item(img *pimg, img_point *p)
[a420b49]680{
681   int result;
[95c3272]682   pimg->flags = 0;
[d69255f]683
684   if (pimg->version == 3) {
685      int opt;
[76bbb7c9]686      if (pimg->pending >= 0x80) {
687         *p = pimg->mv;
688         pimg->flags = (int)(pimg->pending) & 0x3f;
689         pimg->pending = 0;
[421b7d2]690         return img_LINE;
[76bbb7c9]691      }
[d69255f]692      again3: /* label to goto if we get a prefix */
[4d3dfdf]693      pimg->label = pimg->label_buf;
[d69255f]694      opt = getc(pimg->fh);
[ad9a5cd]695      if (opt == EOF) {
696         img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
697         return img_BAD;
698      }
[d69255f]699      switch (opt >> 6) {
700       case 0:
701         if (opt == 0) {
702            if (!pimg->label_len) return img_STOP; /* end of data marker */
703            pimg->label_len = 0;
704            goto again3;
705         }
706         if (opt < 15) {
707            /* 1-14 mean trim that many levels from current prefix */
708            int c;
709            if (pimg->label_len <= 16) {
[4d3dfdf]710               /* zero prefix using "0" */
[d69255f]711               img_errno = IMG_BADFORMAT;
712               return img_BAD;
713            }
[4b156b3]714            c = pimg->label_len - 16 - 1;
[0ff23cb]715            while (pimg->label_buf[c] != '.' || --opt > 0) {
[d69255f]716               if (--c < 0) {
717                  /* zero prefix using "0" */
718                  img_errno = IMG_BADFORMAT;
719                  return img_BAD;
720               }
721            }
722            c++;
723            pimg->label_len = c;
724            goto again3;
725         }
726         if (opt == 15) {
727            result = img_MOVE;
728            break;
729         }
730         /* 16-31 mean remove (n - 15) characters from the prefix */
731         /* zero prefix using 0 */
[ad4eaf3f]732         if (pimg->label_len <= (size_t)(opt - 15)) {
[d69255f]733            img_errno = IMG_BADFORMAT;
734            return img_BAD;
735         }
[d245d71]736         pimg->label_len -= (opt - 15);
[d69255f]737         goto again3;
738       case 1: case 2: {
739         char *q;
[ad9a5cd]740         long len = getc(pimg->fh);
741         if (len == EOF) {
742            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
743            return img_BAD;
744         }
[d69255f]745         if (len == 0xfe) {
746            len += get16(pimg->fh);
[ad9a5cd]747            if (feof(pimg->fh)) {
748               img_errno = IMG_BADFORMAT;
749               return img_BAD;
750            }
751            if (ferror(pimg->fh)) {
752               img_errno = IMG_READERROR;
753               return img_BAD;
754            }
[d69255f]755         } else if (len == 0xff) {
756            len = get32(pimg->fh);
[ad9a5cd]757            if (ferror(pimg->fh)) {
758               img_errno = IMG_READERROR;
759               return img_BAD;
760            }
761            if (feof(pimg->fh) || len < 0xfe + 0xffff) {
[d69255f]762               img_errno = IMG_BADFORMAT;
763               return img_BAD;
764            }
765         }
766
[6dd3a20]767         if (!check_label_space(pimg, pimg->label_len + len + 1)) {
[d4d3655]768            img_errno = IMG_OUTOFMEMORY;
769            return img_BAD;
[d69255f]770         }
[d4d3655]771         q = pimg->label_buf + pimg->label_len;
[6dd3a20]772         pimg->label_len += len;
[d69255f]773         if (len && fread(q, len, 1, pimg->fh) != 1) {
[ad9a5cd]774            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]775            return img_BAD;
776         }
777         q[len] = '\0';
[76bbb7c9]778
779         result = opt & 0x40 ? img_LABEL : img_LINE;
780
781         if (pimg->survey_len) {
782            size_t l = pimg->survey_len;
[0ff23cb]783            const char *s = pimg->label_buf;
[76bbb7c9]784            if (result == img_LINE) {
785               if (strncmp(pimg->survey, s, l) != 0 ||
786                   !(s[l] == '.' || s[l] == '\0')) {
[421b7d2]787                  if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
[76bbb7c9]788                  pimg->pending = 15;
[421b7d2]789                  goto again3;
[76bbb7c9]790               }
791            } else {
792               if (strncmp(pimg->survey, s, l + 1) != 0) {
[4d3dfdf]793                  if (!skip_coord(pimg->fh)) return img_BAD;
794                  pimg->pending = 0;
[76bbb7c9]795                  goto again3;
796               }
797            }
[0ff23cb]798            pimg->label += l;
799            /* skip the dot if there */
800            if (*pimg->label) pimg->label++;
[76bbb7c9]801         }
802
803         if (result == img_LINE && pimg->pending) {
804            *p = pimg->mv;
[ad9a5cd]805            if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
[76bbb7c9]806            pimg->pending = opt;
807            return img_MOVE;
808         }
809         pimg->flags = (int)opt & 0x3f;
[d69255f]810         break;
811       }
812       default:
813         img_errno = IMG_BADFORMAT;
814         return img_BAD;
815      }
[ad9a5cd]816      if (!read_coord(pimg->fh, p)) return img_BAD;
[76bbb7c9]817      pimg->pending = 0;
[d69255f]818      return result;
[4d3dfdf]819   }
820
821   pimg->label = pimg->label_buf;
822
823   if (pimg->version > 0) {
[d69255f]824      static long opt_lookahead = 0;
[ad9a5cd]825      static img_point pt = { 0.0, 0.0, 0.0 };
[a420b49]826      long opt;
827      again: /* label to goto if we get a cross */
[5d7280f8]828      pimg->label[0] = '\0';
[ad9a5cd]829
[5c3c61a]830      if (pimg->version == 1) {
[c4d4649]831         if (opt_lookahead) {
832            opt = opt_lookahead;
833            opt_lookahead = 0;
834         } else {
835            opt = get32(pimg->fh);
836         }
[5c3c61a]837      } else {
[421b7d2]838         opt = getc(pimg->fh);
[5c3c61a]839      }
[ad9a5cd]840
841      if (feof(pimg->fh)) {
842         img_errno = IMG_BADFORMAT;
843         return img_BAD;
844      }
845      if (ferror(pimg->fh)) {
846         img_errno = IMG_READERROR;
847         return img_BAD;
848      }
849
[a420b49]850      switch (opt) {
[437caf3]851       case -1: case 0:
[a420b49]852         return img_STOP; /* end of data marker */
853       case 1:
[ad9a5cd]854         /* skip coordinates */
[4d3dfdf]855         if (!skip_coord(pimg->fh)) {
[ad9a5cd]856            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
857            return img_BAD;
858         }
[a420b49]859         goto again;
[23f7ea7]860       case 2: case 3: {
[fe8e80e]861         char *q;
[3a1f8da]862         int ch;
[437caf3]863         result = img_LABEL;
[3a1f8da]864         ch = getc(pimg->fh);
[d69255f]865         if (ch == EOF) {
[ad9a5cd]866            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]867            return img_BAD;
868         }
[3a1f8da]869         if (ch != '\\') ungetc(ch, pimg->fh);
[0ff23cb]870         fgets(pimg->label_buf, 257, pimg->fh);
[421b7d2]871         if (feof(pimg->fh)) {
[ad9a5cd]872            img_errno = IMG_BADFORMAT;
873            return img_BAD;
874         }
[421b7d2]875         if (ferror(pimg->fh)) {
[ad9a5cd]876            img_errno = IMG_READERROR;
877            return img_BAD;
878         }
[0ff23cb]879         q = pimg->label_buf + strlen(pimg->label_buf) - 1;
[d69255f]880         if (*q != '\n') {
881            img_errno = IMG_BADFORMAT;
882            return img_BAD;
883         }
[9968ebf]884         /* Ignore empty labels in some .3d files (caused by a bug) */
[0ff23cb]885         if (q == pimg->label_buf) goto again;
[fe8e80e]886         *q = '\0';
[509e099]887         pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
[437caf3]888         if (opt == 2) goto done;
889         break;
[23f7ea7]890       }
[95c3272]891       case 6: case 7: {
[ad9a5cd]892         long len;
[23f7ea7]893         result = img_LABEL;
[ad9a5cd]894
[509e099]895         if (opt == 7)
896            pimg->flags = getc(pimg->fh);
897         else
898            pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
[ad9a5cd]899
[23f7ea7]900         len = get32(pimg->fh);
[ad9a5cd]901
902         if (feof(pimg->fh)) {
903            img_errno = IMG_BADFORMAT;
904            return img_BAD;
905         }
906         if (ferror(pimg->fh)) {
907            img_errno = IMG_READERROR;
908            return img_BAD;
909         }
910
[9968ebf]911         /* Ignore empty labels in some .3d files (caused by a bug) */
912         if (len == 0) goto again;
[d4d3655]913         if (!check_label_space(pimg, len + 1)) {
914            img_errno = IMG_OUTOFMEMORY;
915            return img_BAD;
[23f7ea7]916         }
[0ff23cb]917         if (fread(pimg->label_buf, len, 1, pimg->fh) != 1) {
[ad9a5cd]918            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]919            return img_BAD;
920         }
[c3e5ba5]921         pimg->label_buf[len] = '\0';
[23f7ea7]922         break;
923       }
[a420b49]924       case 4:
925         result = img_MOVE;
926         break;
927       case 5:
928         result = img_LINE;
929         break;
930       default:
[95c3272]931         switch ((int)opt & 0xc0) {
932          case 0x80:
933            pimg->flags = (int)opt & 0x3f;
934            result = img_LINE;
935            break;
936          case 0x40: {
937            char *q;
938            pimg->flags = (int)opt & 0x3f;
939            result = img_LABEL;
[0ff23cb]940            if (!fgets(pimg->label_buf, 257, pimg->fh)) {
[ad9a5cd]941               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]942               return img_BAD;
943            }
[0ff23cb]944            q = pimg->label_buf + strlen(pimg->label_buf) - 1;
[9968ebf]945            /* Ignore empty-labels in some .3d files (caused by a bug) */
[0ff23cb]946            if (q == pimg->label_buf) goto again;
[d69255f]947            if (*q != '\n') {
948               img_errno = IMG_BADFORMAT;
949               return img_BAD;
950            }
[95c3272]951            *q = '\0';
952            break;
953          }
954          case 0xc0:
955            /* use this for an extra leg or station flag if we need it */
956          default:
[d69255f]957            img_errno = IMG_BADFORMAT;
[95c3272]958            return img_BAD;
959         }
[437caf3]960         break;
[a420b49]961      }
[ad9a5cd]962
963      if (!read_coord(pimg->fh, &pt)) return img_BAD;
[76bbb7c9]964
[ed0f5b6]965      if (result == img_LABEL && pimg->survey_len) {
966         if (strncmp(pimg->label_buf, pimg->survey, pimg->survey_len + 1) != 0)
[0ff23cb]967            goto again;
968         pimg->label += pimg->survey_len + 1;
969      }
[76bbb7c9]970
[a420b49]971      done:
[ad9a5cd]972      *p = pt;
[c4d4649]973
974      if (result == img_MOVE && pimg->version == 1) {
[d69255f]975         /* peek at next code and see if it's an old-style label */
[23f7ea7]976         opt_lookahead = get32(pimg->fh);
[ad9a5cd]977
978         if (feof(pimg->fh)) {
979            img_errno = IMG_BADFORMAT;
980            return img_BAD;
981         }
982         if (ferror(pimg->fh)) {
983            img_errno = IMG_READERROR;
984            return img_BAD;
985         }
986
[d69255f]987         if (opt_lookahead == 2) return img_read_item(pimg, p);
[c4d4649]988      }
[23f7ea7]989
[a420b49]990      return result;
[0da6e60]991   } else if (pimg->version == 0) {
[a420b49]992      ascii_again:
[5d7280f8]993      pimg->label[0] = '\0';
[a420b49]994      if (feof(pimg->fh)) return img_STOP;
[76bbb7c9]995      if (pimg->pending) {
996         pimg->pending = 0;
[a420b49]997         result = img_LINE;
998      } else {
[7d27e36]999         char cmd[7];
[a420b49]1000         /* Stop if nothing found */
[7d27e36]1001         if (fscanf(pimg->fh, "%6s", cmd) < 1) return img_STOP;
1002         if (strcmp(cmd, "move") == 0)
[a420b49]1003            result = img_MOVE;
[7d27e36]1004         else if (strcmp(cmd, "draw") == 0)
[a420b49]1005            result = img_LINE;
[7d27e36]1006         else if (strcmp(cmd, "line") == 0) {
[bd1913f]1007            /* set flag to indicate to process second triplet as LINE */
[76bbb7c9]1008            pimg->pending = 1;
[a420b49]1009            result = img_MOVE;
[7d27e36]1010         } else if (strcmp(cmd, "cross") == 0) {
[d69255f]1011            if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
[ad9a5cd]1012               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[0ed0e16]1013               return img_BAD;
[d69255f]1014            }
[a420b49]1015            goto ascii_again;
[7d27e36]1016         } else if (strcmp(cmd, "name") == 0) {
[0da6e60]1017            size_t off = 0;
[58f02ae]1018            int ch = getc(pimg->fh);
1019            if (ch == ' ') ch = getc(pimg->fh);
1020            while (ch != ' ') {
1021               if (ch == '\n' || ch == EOF) {
1022                  img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
[ad9a5cd]1023                  return img_BAD;
1024               }
[58f02ae]1025               if (off == pimg->buf_len) {
[d4d3655]1026                  if (!check_label_space(pimg, pimg->buf_len * 2)) {
[9b56f4d]1027                     img_errno = IMG_OUTOFMEMORY;
1028                     return img_BAD;
1029                  }
[0da6e60]1030               }
[58f02ae]1031               pimg->label_buf[off++] = ch;
1032               ch = getc(pimg->fh);
[ad9a5cd]1033            }
[58f02ae]1034            pimg->label_buf[off] = '\0';
1035
1036            pimg->label = pimg->label_buf;
1037            if (pimg->label[0] == '\\') pimg->label++;
[0da6e60]1038
[a420b49]1039            result = img_LABEL;
[d69255f]1040         } else {
1041            img_errno = IMG_BADFORMAT;
[a420b49]1042            return img_BAD; /* unknown keyword */
[d69255f]1043         }
[a420b49]1044      }
[d1b1380]1045
[d69255f]1046      if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
[ad9a5cd]1047         img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
[fe8e80e]1048         return img_BAD;
[d69255f]1049      }
1050
[58f02ae]1051      if (result == img_LABEL && pimg->survey_len) {
1052         if (strncmp(pimg->label, pimg->survey, pimg->survey_len + 1) != 0)
[0ff23cb]1053            goto ascii_again;
1054         pimg->label += pimg->survey_len + 1;
1055      }
[76bbb7c9]1056
[a420b49]1057      return result;
[ad4eaf3f]1058   } else if (pimg->version == -1) {
[0da6e60]1059      /* version -1: .pos file */
[58f02ae]1060      size_t off;
[5eea574]1061      pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
[58f02ae]1062      againpos:
1063      off = 0;
[0da6e60]1064      while (fscanf(pimg->fh, "(%lf,%lf,%lf ) ", &p->x, &p->y, &p->z) != 3) {
1065         int ch;
1066         if (ferror(pimg->fh)) {
1067            img_errno = IMG_READERROR;
1068            return img_BAD;
1069         }
1070         if (feof(pimg->fh)) return img_STOP;
1071         if (pimg->pending) {
1072            img_errno = IMG_BADFORMAT;
1073            return img_BAD;
1074         }
1075         pimg->pending = 1;
1076         /* ignore rest of line */
1077         do {
1078            ch = getc(pimg->fh);
1079         } while (ch != '\n' && ch != '\r' && ch != EOF);
1080      }
1081
1082      pimg->label_buf[0] = '\0';
1083      while (!feof(pimg->fh)) {
1084         if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
1085            img_errno = IMG_READERROR;
1086            return img_BAD;
1087         }
1088
1089         off += strlen(pimg->label_buf + off);
1090         if (off && pimg->label_buf[off - 1] == '\n') {
1091            pimg->label_buf[off - 1] = '\0';
1092            break;
1093         }
[d4d3655]1094         if (!check_label_space(pimg, pimg->buf_len * 2)) {
[9b56f4d]1095            img_errno = IMG_OUTOFMEMORY;
1096            return img_BAD;
1097         }
[0da6e60]1098      }
1099
[58f02ae]1100      pimg->label = pimg->label_buf;
1101
[421b7d2]1102      if (pimg->label[0] == '\\') pimg->label++;
[58f02ae]1103
1104      if (pimg->survey_len) {
1105         size_t l = pimg->survey_len + 1;
1106         if (strncmp(pimg->survey, pimg->label, l) != 0) goto againpos;
1107         pimg->label += l;
1108      }
1109
[0da6e60]1110      return img_LABEL;
[f1c7c9c7]1111   } else if (pimg->version == -2) {
[ad4eaf3f]1112      /* version -2: Compass .plt file */
[4ebc557]1113      if (pimg->pending > 0) {
1114         /* -1 signals we've entered the first survey we want to
1115          * read, and need to fudge lots if the first action is 'D'...
1116          */
1117         /* pending MOVE or LINE */
[984f49e]1118         int r = pimg->pending - 4;
[ad4eaf3f]1119         pimg->pending = 0;
1120         pimg->flags = 0;
[f710a3f]1121         pimg->label[pimg->label_len] = '\0';
[ad4eaf3f]1122         return r;
1123      }
1124
1125      while (1) {
[4ebc557]1126         char *line;
[ad4eaf3f]1127         char *q;
1128         size_t len = 0;
[4ebc557]1129         int ch = getc(pimg->fh);
1130
1131         switch (ch) {
1132            case '\x1a': case EOF: /* Don't insist on ^Z at end of file */
1133               return img_STOP;
[27bd06b]1134            case 'X': case 'F': case 'S':
1135               /* bounding boX (marks end of survey), Feature survey, or
1136                * new Section - skip to next survey */
1137               if (pimg->survey) return img_STOP;
1138skip_to_N:
1139               while (1) {
1140                  do {
1141                     ch = getc(pimg->fh);
1142                  } while (ch != '\n' && ch != '\r' && ch != EOF);
1143                  while (ch == '\n' || ch == '\r') ch = getc(pimg->fh);
1144                  if (ch == 'N') break;
1145                  if (ch == '\x1a' || ch == EOF) return img_STOP;
1146               }
1147               /* FALLTHRU */
[4ebc557]1148            case 'N':
1149               line = getline_alloc(pimg->fh);
[f1c7c9c7]1150               if (!line) {
1151                  img_errno = IMG_OUTOFMEMORY;
1152                  return img_BAD;
1153               }
[9f9c05e]1154               while (line[len] > 32) ++len;
[ef59f5e]1155               if (pimg->label_len == 0) pimg->pending = -1;
[4ebc557]1156               if (!check_label_space(pimg, len + 1)) {
1157                  osfree(line);
1158                  img_errno = IMG_OUTOFMEMORY;
1159                  return img_BAD;
1160               }
1161               pimg->label_len = len;
1162               pimg->label = pimg->label_buf;
1163               memcpy(pimg->label, line, len);
1164               pimg->label[len] = '\0';
1165               osfree(line);
1166               break;
1167            case 'M': case 'D': {
[ad4eaf3f]1168               /* Move or Draw */
[4ebc557]1169               long fpos = -1;
[27bd06b]1170               if (pimg->survey && pimg->label_len == 0) {
[4ebc557]1171                  /* We're only holding onto this line in case the first line
[27bd06b]1172                   * of the 'N' is a 'D', so skip it for now...
[4ebc557]1173                   */
[27bd06b]1174                  goto skip_to_N;
[4ebc557]1175               }
1176               if (ch == 'D' && pimg->pending == -1) {
[ef59f5e]1177                  if (pimg->survey) {
1178                     fpos = ftell(pimg->fh) - 1;
1179                     fseek(pimg->fh, pimg->start, SEEK_SET);
1180                     ch = getc(pimg->fh);
1181                     pimg->pending = 0;
1182                  } else {
1183                     /* If a file actually has a 'D' before any 'M', then
1184                      * pretend the 'D' is an 'M' - one of the examples
1185                      * in the docs was like this! */
1186                     ch = 'M';
1187                  }
[4ebc557]1188               }
1189               line = getline_alloc(pimg->fh);
[f1c7c9c7]1190               if (!line) {
1191                  img_errno = IMG_OUTOFMEMORY;
1192                  return img_BAD;
1193               }
[88c780d]1194               /* Compass store coordinates as North, East, Up = (y,x,z)! */
1195               if (sscanf(line, "%lf%lf%lf", &p->y, &p->x, &p->z) != 3) {
[ad4eaf3f]1196                  osfree(line);
1197                  if (ferror(pimg->fh)) {
1198                     img_errno = IMG_READERROR;
1199                  } else {
1200                     img_errno = IMG_BADFORMAT;
1201                  }
1202                  return img_BAD;
1203               }
[a9ab65a]1204               p->x *= METRES_PER_FOOT;
1205               p->y *= METRES_PER_FOOT;
1206               p->z *= METRES_PER_FOOT;
[ad4eaf3f]1207               q = strchr(line, 'S');
1208               if (!q) {
1209                  osfree(line);
[5757725]1210                  img_errno = IMG_BADFORMAT;
[ad4eaf3f]1211                  return img_BAD;
1212               }
1213               ++q;
1214               len = 0;
[9f9c05e]1215               while (q[len] > ' ') ++len;
[ad4eaf3f]1216               q[len] = '\0';
[9f9c05e]1217               len += 2; /* ' ' and '\0' */
[d4d3655]1218               if (!check_label_space(pimg, pimg->label_len + len)) {
1219                  img_errno = IMG_OUTOFMEMORY;
1220                  return img_BAD;
[ad4eaf3f]1221               }
1222               pimg->label = pimg->label_buf;
[9f9c05e]1223               pimg->label[pimg->label_len] = ' ';
[f710a3f]1224               memcpy(pimg->label + pimg->label_len + 1, q, len - 1);
[ad4eaf3f]1225               osfree(line);
1226               pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
[0d922fc]1227               if (fpos != -1) {
1228                  fseek(pimg->fh, fpos, SEEK_SET);
1229               } else {
1230                  pimg->pending = (ch == 'M' ? img_MOVE : img_LINE) + 4;
1231               }
[ad4eaf3f]1232               return img_LABEL;
[4ebc557]1233            }
[ad4eaf3f]1234            default:
1235               img_errno = IMG_BADFORMAT;
1236               return img_BAD;
1237         }
1238      }
[f1c7c9c7]1239   } else {
1240      /* version -3 or -4: CMAP .xyz file */
1241      char *line = NULL;
1242      char *q;
1243      size_t len;
1244
1245      if (pimg->pending) {
1246         /* pending MOVE or LINE or LABEL or STOP */
1247         int r = pimg->pending - 4;
[c3e5ba5]1248         /* Set label to empty - don't use "" as we adjust label relative
1249          * to label_buf when label_buf is reallocated. */
1250         pimg->label = pimg->label_buf + strlen(pimg->label_buf);
[f1c7c9c7]1251         pimg->flags = 0;
1252         if (r == img_LABEL) {
1253            /* nasty magic */
1254            read_xyz_shot_coords(p, pimg->label_buf + 16);
1255            subtract_xyz_shot_deltas(p, pimg->label_buf + 16);
1256            pimg->pending = img_STOP + 4;
1257            return img_MOVE;
1258         }
1259       
1260         pimg->pending = 0;
1261
1262         if (r == img_STOP) {
1263            /* nasty magic */
1264            read_xyz_shot_coords(p, pimg->label_buf + 16);
1265            return img_LINE;
1266         }
1267         
1268         return r;
1269      }
1270
1271      pimg->label = pimg->label_buf;
1272      do {
1273         osfree(line);
1274         if (feof(pimg->fh)) return img_STOP;
1275         line = getline_alloc(pimg->fh);
1276         if (!line) {
1277            img_errno = IMG_OUTOFMEMORY;
1278            return img_BAD;
1279         }
1280      } while (line[0] == ' ' || line[0] == '\0');
1281      if (line[0] == '\x1a') return img_STOP;
1282
1283      len = strlen(line);
1284      if (pimg->version == -3) {
1285         /* station variant */
1286         if (len < 37) {
1287            osfree(line);
1288            img_errno = IMG_BADFORMAT;
1289            return img_BAD;
1290         }
1291         memcpy(pimg->label, line, 6);
1292         q = memchr(pimg->label, ' ', 6);
1293         if (!q) q = pimg->label + 6;
1294         *q = '\0';
1295
1296         read_xyz_station_coords(p, line);
[9f9c05e]1297 
[f1c7c9c7]1298         /* FIXME: look at prev for lines (line + 32, 5) */
1299         /* FIXME: duplicate stations... */
1300         return img_LABEL;
1301      } else {
1302         /* Shot variant */
1303         char old[8], new[8];
1304         if (len < 61) {
1305            osfree(line);
1306            img_errno = IMG_BADFORMAT;
1307            return img_BAD;
1308         }
[9f9c05e]1309 
[f1c7c9c7]1310         memcpy(old, line, 7);
1311         q = memchr(old, ' ', 7);
1312         if (!q) q = old + 7;
1313         *q = '\0';
[9f9c05e]1314
[f1c7c9c7]1315         memcpy(new, line + 7, 7);
1316         q = memchr(new, ' ', 7);
1317         if (!q) q = new + 7;
1318         *q = '\0';
[9f9c05e]1319
[f1c7c9c7]1320         pimg->flags = img_SFLAG_UNDERGROUND;
1321
1322         if (strcmp(old, new) == 0) {
1323            pimg->pending = img_MOVE + 4;
1324            read_xyz_shot_coords(p, line);
1325            strcpy(pimg->label, new);
1326            osfree(line);
1327            return img_LABEL;
1328         }
[9f9c05e]1329 
[f1c7c9c7]1330         if (strcmp(old, pimg->label) == 0) {
1331            pimg->pending = img_LINE + 4;
1332            read_xyz_shot_coords(p, line);
1333            strcpy(pimg->label, new);
1334            osfree(line);
1335            return img_LABEL;
1336         }
1337         
1338         pimg->pending = img_LABEL + 4;
1339         read_xyz_shot_coords(p, line);
1340         strcpy(pimg->label, new);
1341         memcpy(pimg->label + 16, line, 70);
1342
1343         osfree(line);
1344         return img_LABEL;
1345      }
[a420b49]1346   }
[d1b1380]1347}
1348
[ad9a5cd]1349static int
[d69255f]1350write_v3label(img *pimg, int opt, const char *s)
1351{
1352   size_t len, n, dot;
1353
1354   /* find length of common prefix */
1355   dot = 0;
[0ff23cb]1356   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
[d69255f]1357      if (s[len] == '.') dot = len + 1;
1358   }
1359
[4c07c51]1360   SVX_ASSERT(len <= pimg->label_len);
[d69255f]1361   n = pimg->label_len - len;
1362   if (len == 0) {
1363      if (pimg->label_len) putc(0, pimg->fh);
1364   } else if (n <= 16) {
1365      if (n) putc(n + 15, pimg->fh);
1366   } else if (dot == 0) {
1367      if (pimg->label_len) putc(0, pimg->fh);
[99cf51a]1368      len = 0;
[d69255f]1369   } else {
[0ff23cb]1370      const char *p = pimg->label_buf + dot;
[d69255f]1371      n = 1;
1372      for (len = pimg->label_len - dot - 17; len; len--) {
1373         if (*p++ == '.') n++;
1374      }
1375      if (n <= 14) {
1376         putc(n, pimg->fh);
1377         len = dot;
1378      } else {
1379         if (pimg->label_len) putc(0, pimg->fh);
1380         len = 0;
1381      }
1382   }
1383
1384   n = strlen(s + len);
1385   putc(opt, pimg->fh);
1386   if (n < 0xfe) {
1387      putc(n, pimg->fh);
1388   } else if (n < 0xffff + 0xfe) {
1389      putc(0xfe, pimg->fh);
1390      put16(n - 0xfe, pimg->fh);
1391   } else {
1392      putc(0xff, pimg->fh);
1393      put32(n, pimg->fh);
1394   }
1395   fwrite(s + len, n, 1, pimg->fh);
1396
1397   n += len;
1398   pimg->label_len = n;
[d4d3655]1399   if (!check_label_space(pimg, n + 1))
1400      return 0; /* FIXME: distinguish out of memory... */
[0ff23cb]1401   memcpy(pimg->label_buf + len, s + len, n - len + 1);
[ad9a5cd]1402
1403   return !ferror(pimg->fh);
[d69255f]1404}
1405
[a420b49]1406void
[95c3272]1407img_write_item(img *pimg, int code, int flags, const char *s,
[a9f5117]1408               double x, double y, double z)
[a420b49]1409{
[647407d]1410   if (!pimg) return;
[d69255f]1411   if (pimg->version == 3) {
1412      int opt = 0;
1413      switch (code) {
1414       case img_LABEL:
1415         write_v3label(pimg, 0x40 | flags, s);
1416         opt = 0;
1417         break;
1418       case img_MOVE:
1419         opt = 15;
1420         break;
1421       case img_LINE:
1422         write_v3label(pimg, 0x80 | flags, s ? s : "");
1423         opt = 0;
1424         break;
1425       default: /* ignore for now */
1426         return;
1427      }
1428      if (opt) putc(opt, pimg->fh);
1429      /* Output in cm */
[dd1d6369]1430      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1431      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1432      put32((INT32_T)my_round(z * 100.0), pimg->fh);
[d69255f]1433   } else {
[23f7ea7]1434      size_t len;
[badeec8]1435      INT32_T opt = 0;
[4c07c51]1436      SVX_ASSERT(pimg->version > 0);
[a420b49]1437      switch (code) {
[437caf3]1438       case img_LABEL:
1439         if (pimg->version == 1) {
1440            /* put a move before each label */
[95c3272]1441            img_write_item(pimg, img_MOVE, 0, NULL, x, y, z);
[437caf3]1442            put32(2, pimg->fh);
[0ed0e16]1443            fputsnl(s, pimg->fh);
[437caf3]1444            return;
1445         }
[23f7ea7]1446         len = strlen(s);
[af56069]1447         if (len > 255 || strchr(s, '\n')) {
[23f7ea7]1448            /* long label - not in early incarnations of v2 format, but few
1449             * 3d files will need these, so better not to force incompatibility
1450             * with a new version I think... */
[95c3272]1451            putc(7, pimg->fh);
1452            putc(flags, pimg->fh);
[23f7ea7]1453            put32(len, pimg->fh);
1454            fputs(s, pimg->fh);
1455         } else {
[95c3272]1456            putc(0x40 | (flags & 0x3f), pimg->fh);
[23f7ea7]1457            fputsnl(s, pimg->fh);
1458         }
[437caf3]1459         opt = 0;
[23f7ea7]1460         break;
[a420b49]1461       case img_MOVE:
1462         opt = 4;
1463         break;
1464       case img_LINE:
[421b7d2]1465         if (pimg->version > 1) {
[d69255f]1466            opt = 0x80 | (flags & 0x3f);
[437caf3]1467            break;
[421b7d2]1468         }
[437caf3]1469         opt = 5;
[a420b49]1470         break;
[437caf3]1471       default: /* ignore for now */
1472         return;
[a420b49]1473      }
[437caf3]1474      if (pimg->version == 1) {
1475         put32(opt, pimg->fh);
1476      } else {
1477         if (opt) putc(opt, pimg->fh);
[a420b49]1478      }
[d69255f]1479      /* Output in cm */
[dd1d6369]1480      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1481      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1482      put32((INT32_T)my_round(z * 100.0), pimg->fh);
[a420b49]1483   }
[d1b1380]1484}
1485
[ad9a5cd]1486int
[a420b49]1487img_close(img *pimg)
1488{
[ad9a5cd]1489   int result = 1;
[dbb4e19]1490   if (pimg) {
1491      if (pimg->fh) {
[76bbb7c9]1492         if (pimg->fRead) {
1493            osfree(pimg->survey);
[a2ad284]1494            osfree(pimg->title);
1495            osfree(pimg->datestamp);
[76bbb7c9]1496         } else {
1497            /* write end of data marker */
[d69255f]1498            switch (pimg->version) {
1499             case 1:
[5c3c61a]1500               put32((INT32_T)-1, pimg->fh);
[d69255f]1501               break;
1502             case 2:
1503               putc(0, pimg->fh);
1504               break;
1505             case 3:
1506               if (pimg->label_len) putc(0, pimg->fh);
[437caf3]1507               putc(0, pimg->fh);
[d69255f]1508               break;
[5c3c61a]1509            }
1510         }
[ad9a5cd]1511         if (ferror(pimg->fh)) result = 0;
1512         if (fclose(pimg->fh)) result = 0;
[0d922fc]1513         if (!result) img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
[dbb4e19]1514      }
[0ff23cb]1515      osfree(pimg->label_buf);
[dbb4e19]1516      osfree(pimg);
[a420b49]1517   }
[ad9a5cd]1518   return result;
[d1b1380]1519}
Note: See TracBrowser for help on using the repository browser.