source: git/src/img.c @ 8781a97

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

Fixed 3d file reading code to fix incorrect unpacking of compressed station
names in a rare case. Also checked other rare cases work correctly.

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

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