source: git/src/img.c @ 7c663ef

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

Fixed scaling when reading PLT files.

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

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