source: git/src/img.c @ ca0fe47

v1.0.12
Last change on this file since ca0fe47 was 4c07c51, checked in by Olly Betts <olly@…>, 23 years ago

Renamed ASSERT to SVXASSERT to avoid clash with Allegro which also defines
it. Renamed ASSERT2 to SVXASSERT2 to match.

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

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