source: git/src/img.c @ 6120efa

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 6120efa was 255f57b9, checked in by Olly Betts <olly@…>, 22 years ago

Fix various MSVC warnings.

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

  • Property mode set to 100644
File size: 38.2 KB
RevLine 
[0da6e60]1/* img.c
[d1b1380]2 * Routines for reading and writing Survex ".3d" image files
[ff0b3b0]3 * Copyright (C) 1993-2003 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;
760            if (pimg->label_len <= 16) {
[4d3dfdf]761               /* zero prefix using "0" */
[d69255f]762               img_errno = IMG_BADFORMAT;
763               return img_BAD;
764            }
[4b156b3]765            c = pimg->label_len - 16 - 1;
[0ff23cb]766            while (pimg->label_buf[c] != '.' || --opt > 0) {
[d69255f]767               if (--c < 0) {
768                  /* zero prefix using "0" */
769                  img_errno = IMG_BADFORMAT;
770                  return img_BAD;
771               }
772            }
773            c++;
774            pimg->label_len = c;
775            goto again3;
776         }
777         if (opt == 15) {
778            result = img_MOVE;
779            break;
780         }
781         /* 16-31 mean remove (n - 15) characters from the prefix */
782         /* zero prefix using 0 */
[ad4eaf3f]783         if (pimg->label_len <= (size_t)(opt - 15)) {
[d69255f]784            img_errno = IMG_BADFORMAT;
785            return img_BAD;
786         }
[d245d71]787         pimg->label_len -= (opt - 15);
[d69255f]788         goto again3;
789       case 1: case 2: {
790         char *q;
[ad9a5cd]791         long len = getc(pimg->fh);
792         if (len == EOF) {
793            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
794            return img_BAD;
795         }
[d69255f]796         if (len == 0xfe) {
797            len += get16(pimg->fh);
[ad9a5cd]798            if (feof(pimg->fh)) {
799               img_errno = IMG_BADFORMAT;
800               return img_BAD;
801            }
802            if (ferror(pimg->fh)) {
803               img_errno = IMG_READERROR;
804               return img_BAD;
805            }
[d69255f]806         } else if (len == 0xff) {
807            len = get32(pimg->fh);
[ad9a5cd]808            if (ferror(pimg->fh)) {
809               img_errno = IMG_READERROR;
810               return img_BAD;
811            }
812            if (feof(pimg->fh) || len < 0xfe + 0xffff) {
[d69255f]813               img_errno = IMG_BADFORMAT;
814               return img_BAD;
815            }
816         }
817
[6dd3a20]818         if (!check_label_space(pimg, pimg->label_len + len + 1)) {
[d4d3655]819            img_errno = IMG_OUTOFMEMORY;
820            return img_BAD;
[d69255f]821         }
[d4d3655]822         q = pimg->label_buf + pimg->label_len;
[6dd3a20]823         pimg->label_len += len;
[d69255f]824         if (len && fread(q, len, 1, pimg->fh) != 1) {
[ad9a5cd]825            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]826            return img_BAD;
827         }
828         q[len] = '\0';
[76bbb7c9]829
830         result = opt & 0x40 ? img_LABEL : img_LINE;
831
832         if (pimg->survey_len) {
833            size_t l = pimg->survey_len;
[0ff23cb]834            const char *s = pimg->label_buf;
[76bbb7c9]835            if (result == img_LINE) {
836               if (strncmp(pimg->survey, s, l) != 0 ||
837                   !(s[l] == '.' || s[l] == '\0')) {
[421b7d2]838                  if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
[76bbb7c9]839                  pimg->pending = 15;
[421b7d2]840                  goto again3;
[76bbb7c9]841               }
842            } else {
843               if (strncmp(pimg->survey, s, l + 1) != 0) {
[4d3dfdf]844                  if (!skip_coord(pimg->fh)) return img_BAD;
845                  pimg->pending = 0;
[76bbb7c9]846                  goto again3;
847               }
848            }
[0ff23cb]849            pimg->label += l;
850            /* skip the dot if there */
851            if (*pimg->label) pimg->label++;
[76bbb7c9]852         }
853
854         if (result == img_LINE && pimg->pending) {
855            *p = pimg->mv;
[ad9a5cd]856            if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
[76bbb7c9]857            pimg->pending = opt;
858            return img_MOVE;
859         }
860         pimg->flags = (int)opt & 0x3f;
[d69255f]861         break;
862       }
863       default:
864         img_errno = IMG_BADFORMAT;
865         return img_BAD;
866      }
[ad9a5cd]867      if (!read_coord(pimg->fh, p)) return img_BAD;
[76bbb7c9]868      pimg->pending = 0;
[d69255f]869      return result;
[4d3dfdf]870   }
871
872   pimg->label = pimg->label_buf;
873
874   if (pimg->version > 0) {
[d69255f]875      static long opt_lookahead = 0;
[ad9a5cd]876      static img_point pt = { 0.0, 0.0, 0.0 };
[a420b49]877      long opt;
878      again: /* label to goto if we get a cross */
[5d7280f8]879      pimg->label[0] = '\0';
[ad9a5cd]880
[5c3c61a]881      if (pimg->version == 1) {
[c4d4649]882         if (opt_lookahead) {
883            opt = opt_lookahead;
884            opt_lookahead = 0;
885         } else {
886            opt = get32(pimg->fh);
887         }
[5c3c61a]888      } else {
[421b7d2]889         opt = getc(pimg->fh);
[5c3c61a]890      }
[ad9a5cd]891
892      if (feof(pimg->fh)) {
893         img_errno = IMG_BADFORMAT;
894         return img_BAD;
895      }
896      if (ferror(pimg->fh)) {
897         img_errno = IMG_READERROR;
898         return img_BAD;
899      }
900
[a420b49]901      switch (opt) {
[437caf3]902       case -1: case 0:
[a420b49]903         return img_STOP; /* end of data marker */
904       case 1:
[ad9a5cd]905         /* skip coordinates */
[4d3dfdf]906         if (!skip_coord(pimg->fh)) {
[ad9a5cd]907            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
908            return img_BAD;
909         }
[a420b49]910         goto again;
[23f7ea7]911       case 2: case 3: {
[fe8e80e]912         char *q;
[3a1f8da]913         int ch;
[437caf3]914         result = img_LABEL;
[3a1f8da]915         ch = getc(pimg->fh);
[d69255f]916         if (ch == EOF) {
[ad9a5cd]917            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]918            return img_BAD;
919         }
[3a1f8da]920         if (ch != '\\') ungetc(ch, pimg->fh);
[0ff23cb]921         fgets(pimg->label_buf, 257, pimg->fh);
[421b7d2]922         if (feof(pimg->fh)) {
[ad9a5cd]923            img_errno = IMG_BADFORMAT;
924            return img_BAD;
925         }
[421b7d2]926         if (ferror(pimg->fh)) {
[ad9a5cd]927            img_errno = IMG_READERROR;
928            return img_BAD;
929         }
[0ff23cb]930         q = pimg->label_buf + strlen(pimg->label_buf) - 1;
[d69255f]931         if (*q != '\n') {
932            img_errno = IMG_BADFORMAT;
933            return img_BAD;
934         }
[9968ebf]935         /* Ignore empty labels in some .3d files (caused by a bug) */
[0ff23cb]936         if (q == pimg->label_buf) goto again;
[fe8e80e]937         *q = '\0';
[509e099]938         pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
[437caf3]939         if (opt == 2) goto done;
940         break;
[23f7ea7]941       }
[95c3272]942       case 6: case 7: {
[ad9a5cd]943         long len;
[23f7ea7]944         result = img_LABEL;
[ad9a5cd]945
[509e099]946         if (opt == 7)
947            pimg->flags = getc(pimg->fh);
948         else
949            pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
[ad9a5cd]950
[23f7ea7]951         len = get32(pimg->fh);
[ad9a5cd]952
953         if (feof(pimg->fh)) {
954            img_errno = IMG_BADFORMAT;
955            return img_BAD;
956         }
957         if (ferror(pimg->fh)) {
958            img_errno = IMG_READERROR;
959            return img_BAD;
960         }
961
[9968ebf]962         /* Ignore empty labels in some .3d files (caused by a bug) */
963         if (len == 0) goto again;
[d4d3655]964         if (!check_label_space(pimg, len + 1)) {
965            img_errno = IMG_OUTOFMEMORY;
966            return img_BAD;
[23f7ea7]967         }
[0ff23cb]968         if (fread(pimg->label_buf, len, 1, pimg->fh) != 1) {
[ad9a5cd]969            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]970            return img_BAD;
971         }
[c3e5ba5]972         pimg->label_buf[len] = '\0';
[23f7ea7]973         break;
974       }
[a420b49]975       case 4:
976         result = img_MOVE;
977         break;
978       case 5:
979         result = img_LINE;
980         break;
981       default:
[95c3272]982         switch ((int)opt & 0xc0) {
983          case 0x80:
984            pimg->flags = (int)opt & 0x3f;
985            result = img_LINE;
986            break;
987          case 0x40: {
988            char *q;
989            pimg->flags = (int)opt & 0x3f;
990            result = img_LABEL;
[0ff23cb]991            if (!fgets(pimg->label_buf, 257, pimg->fh)) {
[ad9a5cd]992               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]993               return img_BAD;
994            }
[0ff23cb]995            q = pimg->label_buf + strlen(pimg->label_buf) - 1;
[9968ebf]996            /* Ignore empty-labels in some .3d files (caused by a bug) */
[0ff23cb]997            if (q == pimg->label_buf) goto again;
[d69255f]998            if (*q != '\n') {
999               img_errno = IMG_BADFORMAT;
1000               return img_BAD;
1001            }
[95c3272]1002            *q = '\0';
1003            break;
1004          }
1005          case 0xc0:
1006            /* use this for an extra leg or station flag if we need it */
1007          default:
[d69255f]1008            img_errno = IMG_BADFORMAT;
[95c3272]1009            return img_BAD;
1010         }
[437caf3]1011         break;
[a420b49]1012      }
[ad9a5cd]1013
1014      if (!read_coord(pimg->fh, &pt)) return img_BAD;
[76bbb7c9]1015
[ed0f5b6]1016      if (result == img_LABEL && pimg->survey_len) {
1017         if (strncmp(pimg->label_buf, pimg->survey, pimg->survey_len + 1) != 0)
[0ff23cb]1018            goto again;
1019         pimg->label += pimg->survey_len + 1;
1020      }
[76bbb7c9]1021
[a420b49]1022      done:
[ad9a5cd]1023      *p = pt;
[c4d4649]1024
1025      if (result == img_MOVE && pimg->version == 1) {
[d69255f]1026         /* peek at next code and see if it's an old-style label */
[23f7ea7]1027         opt_lookahead = get32(pimg->fh);
[ad9a5cd]1028
1029         if (feof(pimg->fh)) {
1030            img_errno = IMG_BADFORMAT;
1031            return img_BAD;
1032         }
1033         if (ferror(pimg->fh)) {
1034            img_errno = IMG_READERROR;
1035            return img_BAD;
1036         }
1037
[d69255f]1038         if (opt_lookahead == 2) return img_read_item(pimg, p);
[c4d4649]1039      }
[23f7ea7]1040
[a420b49]1041      return result;
[0da6e60]1042   } else if (pimg->version == 0) {
[a420b49]1043      ascii_again:
[5d7280f8]1044      pimg->label[0] = '\0';
[a420b49]1045      if (feof(pimg->fh)) return img_STOP;
[76bbb7c9]1046      if (pimg->pending) {
1047         pimg->pending = 0;
[a420b49]1048         result = img_LINE;
1049      } else {
[7d27e36]1050         char cmd[7];
[a420b49]1051         /* Stop if nothing found */
[7d27e36]1052         if (fscanf(pimg->fh, "%6s", cmd) < 1) return img_STOP;
1053         if (strcmp(cmd, "move") == 0)
[a420b49]1054            result = img_MOVE;
[7d27e36]1055         else if (strcmp(cmd, "draw") == 0)
[a420b49]1056            result = img_LINE;
[7d27e36]1057         else if (strcmp(cmd, "line") == 0) {
[bd1913f]1058            /* set flag to indicate to process second triplet as LINE */
[76bbb7c9]1059            pimg->pending = 1;
[a420b49]1060            result = img_MOVE;
[7d27e36]1061         } else if (strcmp(cmd, "cross") == 0) {
[d69255f]1062            if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
[ad9a5cd]1063               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[0ed0e16]1064               return img_BAD;
[d69255f]1065            }
[a420b49]1066            goto ascii_again;
[7d27e36]1067         } else if (strcmp(cmd, "name") == 0) {
[0da6e60]1068            size_t off = 0;
[58f02ae]1069            int ch = getc(pimg->fh);
1070            if (ch == ' ') ch = getc(pimg->fh);
1071            while (ch != ' ') {
1072               if (ch == '\n' || ch == EOF) {
1073                  img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
[ad9a5cd]1074                  return img_BAD;
1075               }
[58f02ae]1076               if (off == pimg->buf_len) {
[d4d3655]1077                  if (!check_label_space(pimg, pimg->buf_len * 2)) {
[9b56f4d]1078                     img_errno = IMG_OUTOFMEMORY;
1079                     return img_BAD;
1080                  }
[0da6e60]1081               }
[58f02ae]1082               pimg->label_buf[off++] = ch;
1083               ch = getc(pimg->fh);
[ad9a5cd]1084            }
[58f02ae]1085            pimg->label_buf[off] = '\0';
1086
1087            pimg->label = pimg->label_buf;
1088            if (pimg->label[0] == '\\') pimg->label++;
[0da6e60]1089
[a420b49]1090            result = img_LABEL;
[d69255f]1091         } else {
1092            img_errno = IMG_BADFORMAT;
[a420b49]1093            return img_BAD; /* unknown keyword */
[d69255f]1094         }
[a420b49]1095      }
[d1b1380]1096
[d69255f]1097      if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
[ad9a5cd]1098         img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
[fe8e80e]1099         return img_BAD;
[d69255f]1100      }
1101
[58f02ae]1102      if (result == img_LABEL && pimg->survey_len) {
1103         if (strncmp(pimg->label, pimg->survey, pimg->survey_len + 1) != 0)
[0ff23cb]1104            goto ascii_again;
1105         pimg->label += pimg->survey_len + 1;
1106      }
[76bbb7c9]1107
[a420b49]1108      return result;
[ad4eaf3f]1109   } else if (pimg->version == -1) {
[0da6e60]1110      /* version -1: .pos file */
[58f02ae]1111      size_t off;
[5eea574]1112      pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
[58f02ae]1113      againpos:
1114      off = 0;
[0da6e60]1115      while (fscanf(pimg->fh, "(%lf,%lf,%lf ) ", &p->x, &p->y, &p->z) != 3) {
1116         int ch;
1117         if (ferror(pimg->fh)) {
1118            img_errno = IMG_READERROR;
1119            return img_BAD;
1120         }
1121         if (feof(pimg->fh)) return img_STOP;
1122         if (pimg->pending) {
1123            img_errno = IMG_BADFORMAT;
1124            return img_BAD;
1125         }
1126         pimg->pending = 1;
1127         /* ignore rest of line */
1128         do {
1129            ch = getc(pimg->fh);
1130         } while (ch != '\n' && ch != '\r' && ch != EOF);
1131      }
1132
1133      pimg->label_buf[0] = '\0';
1134      while (!feof(pimg->fh)) {
1135         if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
1136            img_errno = IMG_READERROR;
1137            return img_BAD;
1138         }
1139
1140         off += strlen(pimg->label_buf + off);
1141         if (off && pimg->label_buf[off - 1] == '\n') {
1142            pimg->label_buf[off - 1] = '\0';
1143            break;
1144         }
[d4d3655]1145         if (!check_label_space(pimg, pimg->buf_len * 2)) {
[9b56f4d]1146            img_errno = IMG_OUTOFMEMORY;
1147            return img_BAD;
1148         }
[0da6e60]1149      }
1150
[58f02ae]1151      pimg->label = pimg->label_buf;
1152
[421b7d2]1153      if (pimg->label[0] == '\\') pimg->label++;
[58f02ae]1154
1155      if (pimg->survey_len) {
1156         size_t l = pimg->survey_len + 1;
1157         if (strncmp(pimg->survey, pimg->label, l) != 0) goto againpos;
1158         pimg->label += l;
1159      }
1160
[0da6e60]1161      return img_LABEL;
[f1c7c9c7]1162   } else if (pimg->version == -2) {
[ad4eaf3f]1163      /* version -2: Compass .plt file */
[4ebc557]1164      if (pimg->pending > 0) {
1165         /* -1 signals we've entered the first survey we want to
1166          * read, and need to fudge lots if the first action is 'D'...
1167          */
1168         /* pending MOVE or LINE */
[984f49e]1169         int r = pimg->pending - 4;
[ad4eaf3f]1170         pimg->pending = 0;
1171         pimg->flags = 0;
[f710a3f]1172         pimg->label[pimg->label_len] = '\0';
[ad4eaf3f]1173         return r;
1174      }
1175
1176      while (1) {
[4ebc557]1177         char *line;
[ad4eaf3f]1178         char *q;
1179         size_t len = 0;
[4ebc557]1180         int ch = getc(pimg->fh);
1181
1182         switch (ch) {
1183            case '\x1a': case EOF: /* Don't insist on ^Z at end of file */
1184               return img_STOP;
[27bd06b]1185            case 'X': case 'F': case 'S':
1186               /* bounding boX (marks end of survey), Feature survey, or
1187                * new Section - skip to next survey */
1188               if (pimg->survey) return img_STOP;
1189skip_to_N:
1190               while (1) {
1191                  do {
1192                     ch = getc(pimg->fh);
1193                  } while (ch != '\n' && ch != '\r' && ch != EOF);
1194                  while (ch == '\n' || ch == '\r') ch = getc(pimg->fh);
1195                  if (ch == 'N') break;
1196                  if (ch == '\x1a' || ch == EOF) return img_STOP;
1197               }
1198               /* FALLTHRU */
[4ebc557]1199            case 'N':
1200               line = getline_alloc(pimg->fh);
[f1c7c9c7]1201               if (!line) {
1202                  img_errno = IMG_OUTOFMEMORY;
1203                  return img_BAD;
1204               }
[9f9c05e]1205               while (line[len] > 32) ++len;
[ef59f5e]1206               if (pimg->label_len == 0) pimg->pending = -1;
[4ebc557]1207               if (!check_label_space(pimg, len + 1)) {
1208                  osfree(line);
1209                  img_errno = IMG_OUTOFMEMORY;
1210                  return img_BAD;
1211               }
1212               pimg->label_len = len;
1213               pimg->label = pimg->label_buf;
1214               memcpy(pimg->label, line, len);
1215               pimg->label[len] = '\0';
1216               osfree(line);
1217               break;
1218            case 'M': case 'D': {
[ad4eaf3f]1219               /* Move or Draw */
[4ebc557]1220               long fpos = -1;
[27bd06b]1221               if (pimg->survey && pimg->label_len == 0) {
[4ebc557]1222                  /* We're only holding onto this line in case the first line
[27bd06b]1223                   * of the 'N' is a 'D', so skip it for now...
[4ebc557]1224                   */
[27bd06b]1225                  goto skip_to_N;
[4ebc557]1226               }
1227               if (ch == 'D' && pimg->pending == -1) {
[ef59f5e]1228                  if (pimg->survey) {
1229                     fpos = ftell(pimg->fh) - 1;
1230                     fseek(pimg->fh, pimg->start, SEEK_SET);
1231                     ch = getc(pimg->fh);
1232                     pimg->pending = 0;
1233                  } else {
1234                     /* If a file actually has a 'D' before any 'M', then
1235                      * pretend the 'D' is an 'M' - one of the examples
1236                      * in the docs was like this! */
1237                     ch = 'M';
1238                  }
[4ebc557]1239               }
1240               line = getline_alloc(pimg->fh);
[f1c7c9c7]1241               if (!line) {
1242                  img_errno = IMG_OUTOFMEMORY;
1243                  return img_BAD;
1244               }
[88c780d]1245               /* Compass store coordinates as North, East, Up = (y,x,z)! */
1246               if (sscanf(line, "%lf%lf%lf", &p->y, &p->x, &p->z) != 3) {
[ad4eaf3f]1247                  osfree(line);
1248                  if (ferror(pimg->fh)) {
1249                     img_errno = IMG_READERROR;
1250                  } else {
1251                     img_errno = IMG_BADFORMAT;
1252                  }
1253                  return img_BAD;
1254               }
[a9ab65a]1255               p->x *= METRES_PER_FOOT;
1256               p->y *= METRES_PER_FOOT;
1257               p->z *= METRES_PER_FOOT;
[ad4eaf3f]1258               q = strchr(line, 'S');
1259               if (!q) {
1260                  osfree(line);
[5757725]1261                  img_errno = IMG_BADFORMAT;
[ad4eaf3f]1262                  return img_BAD;
1263               }
1264               ++q;
1265               len = 0;
[9f9c05e]1266               while (q[len] > ' ') ++len;
[ad4eaf3f]1267               q[len] = '\0';
[9f9c05e]1268               len += 2; /* ' ' and '\0' */
[d4d3655]1269               if (!check_label_space(pimg, pimg->label_len + len)) {
1270                  img_errno = IMG_OUTOFMEMORY;
1271                  return img_BAD;
[ad4eaf3f]1272               }
1273               pimg->label = pimg->label_buf;
[9df78d0]1274               if (pimg->label_len) {
1275                   pimg->label[pimg->label_len] = ' ';
1276                   memcpy(pimg->label + pimg->label_len + 1, q, len - 1);
1277               } else {
1278                   memcpy(pimg->label, q, len - 1);
1279               }
[ad4eaf3f]1280               osfree(line);
1281               pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
[0d922fc]1282               if (fpos != -1) {
1283                  fseek(pimg->fh, fpos, SEEK_SET);
1284               } else {
1285                  pimg->pending = (ch == 'M' ? img_MOVE : img_LINE) + 4;
1286               }
[ad4eaf3f]1287               return img_LABEL;
[4ebc557]1288            }
[ad4eaf3f]1289            default:
1290               img_errno = IMG_BADFORMAT;
1291               return img_BAD;
1292         }
1293      }
[f1c7c9c7]1294   } else {
1295      /* version -3 or -4: CMAP .xyz file */
1296      char *line = NULL;
1297      char *q;
1298      size_t len;
1299
1300      if (pimg->pending) {
1301         /* pending MOVE or LINE or LABEL or STOP */
1302         int r = pimg->pending - 4;
[c3e5ba5]1303         /* Set label to empty - don't use "" as we adjust label relative
1304          * to label_buf when label_buf is reallocated. */
1305         pimg->label = pimg->label_buf + strlen(pimg->label_buf);
[f1c7c9c7]1306         pimg->flags = 0;
1307         if (r == img_LABEL) {
1308            /* nasty magic */
1309            read_xyz_shot_coords(p, pimg->label_buf + 16);
1310            subtract_xyz_shot_deltas(p, pimg->label_buf + 16);
1311            pimg->pending = img_STOP + 4;
1312            return img_MOVE;
1313         }
1314       
1315         pimg->pending = 0;
1316
1317         if (r == img_STOP) {
1318            /* nasty magic */
1319            read_xyz_shot_coords(p, pimg->label_buf + 16);
1320            return img_LINE;
1321         }
1322         
1323         return r;
1324      }
1325
1326      pimg->label = pimg->label_buf;
1327      do {
1328         osfree(line);
1329         if (feof(pimg->fh)) return img_STOP;
1330         line = getline_alloc(pimg->fh);
1331         if (!line) {
1332            img_errno = IMG_OUTOFMEMORY;
1333            return img_BAD;
1334         }
1335      } while (line[0] == ' ' || line[0] == '\0');
1336      if (line[0] == '\x1a') return img_STOP;
1337
1338      len = strlen(line);
1339      if (pimg->version == -3) {
1340         /* station variant */
1341         if (len < 37) {
1342            osfree(line);
1343            img_errno = IMG_BADFORMAT;
1344            return img_BAD;
1345         }
1346         memcpy(pimg->label, line, 6);
1347         q = memchr(pimg->label, ' ', 6);
1348         if (!q) q = pimg->label + 6;
1349         *q = '\0';
1350
1351         read_xyz_station_coords(p, line);
[9f9c05e]1352 
[f1c7c9c7]1353         /* FIXME: look at prev for lines (line + 32, 5) */
1354         /* FIXME: duplicate stations... */
1355         return img_LABEL;
1356      } else {
1357         /* Shot variant */
1358         char old[8], new[8];
1359         if (len < 61) {
1360            osfree(line);
1361            img_errno = IMG_BADFORMAT;
1362            return img_BAD;
1363         }
[9f9c05e]1364 
[f1c7c9c7]1365         memcpy(old, line, 7);
1366         q = memchr(old, ' ', 7);
1367         if (!q) q = old + 7;
1368         *q = '\0';
[9f9c05e]1369
[f1c7c9c7]1370         memcpy(new, line + 7, 7);
1371         q = memchr(new, ' ', 7);
1372         if (!q) q = new + 7;
1373         *q = '\0';
[9f9c05e]1374
[f1c7c9c7]1375         pimg->flags = img_SFLAG_UNDERGROUND;
1376
1377         if (strcmp(old, new) == 0) {
1378            pimg->pending = img_MOVE + 4;
1379            read_xyz_shot_coords(p, line);
1380            strcpy(pimg->label, new);
1381            osfree(line);
1382            return img_LABEL;
1383         }
[9f9c05e]1384 
[f1c7c9c7]1385         if (strcmp(old, pimg->label) == 0) {
1386            pimg->pending = img_LINE + 4;
1387            read_xyz_shot_coords(p, line);
1388            strcpy(pimg->label, new);
1389            osfree(line);
1390            return img_LABEL;
1391         }
1392         
1393         pimg->pending = img_LABEL + 4;
1394         read_xyz_shot_coords(p, line);
1395         strcpy(pimg->label, new);
1396         memcpy(pimg->label + 16, line, 70);
1397
1398         osfree(line);
1399         return img_LABEL;
1400      }
[a420b49]1401   }
[d1b1380]1402}
1403
[ad9a5cd]1404static int
[d69255f]1405write_v3label(img *pimg, int opt, const char *s)
1406{
1407   size_t len, n, dot;
1408
1409   /* find length of common prefix */
1410   dot = 0;
[0ff23cb]1411   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
[d69255f]1412      if (s[len] == '.') dot = len + 1;
1413   }
1414
[4c07c51]1415   SVX_ASSERT(len <= pimg->label_len);
[d69255f]1416   n = pimg->label_len - len;
1417   if (len == 0) {
1418      if (pimg->label_len) putc(0, pimg->fh);
1419   } else if (n <= 16) {
1420      if (n) putc(n + 15, pimg->fh);
1421   } else if (dot == 0) {
1422      if (pimg->label_len) putc(0, pimg->fh);
[99cf51a]1423      len = 0;
[d69255f]1424   } else {
[0ff23cb]1425      const char *p = pimg->label_buf + dot;
[d69255f]1426      n = 1;
1427      for (len = pimg->label_len - dot - 17; len; len--) {
1428         if (*p++ == '.') n++;
1429      }
1430      if (n <= 14) {
1431         putc(n, pimg->fh);
1432         len = dot;
1433      } else {
1434         if (pimg->label_len) putc(0, pimg->fh);
1435         len = 0;
1436      }
1437   }
1438
1439   n = strlen(s + len);
1440   putc(opt, pimg->fh);
1441   if (n < 0xfe) {
1442      putc(n, pimg->fh);
1443   } else if (n < 0xffff + 0xfe) {
1444      putc(0xfe, pimg->fh);
[255f57b9]1445      put16((short)(n - 0xfe), pimg->fh);
[d69255f]1446   } else {
1447      putc(0xff, pimg->fh);
1448      put32(n, pimg->fh);
1449   }
1450   fwrite(s + len, n, 1, pimg->fh);
1451
1452   n += len;
1453   pimg->label_len = n;
[d4d3655]1454   if (!check_label_space(pimg, n + 1))
1455      return 0; /* FIXME: distinguish out of memory... */
[0ff23cb]1456   memcpy(pimg->label_buf + len, s + len, n - len + 1);
[ad9a5cd]1457
1458   return !ferror(pimg->fh);
[d69255f]1459}
1460
[a420b49]1461void
[95c3272]1462img_write_item(img *pimg, int code, int flags, const char *s,
[a9f5117]1463               double x, double y, double z)
[a420b49]1464{
[647407d]1465   if (!pimg) return;
[d69255f]1466   if (pimg->version == 3) {
1467      int opt = 0;
1468      switch (code) {
1469       case img_LABEL:
1470         write_v3label(pimg, 0x40 | flags, s);
1471         opt = 0;
1472         break;
1473       case img_MOVE:
1474         opt = 15;
1475         break;
1476       case img_LINE:
1477         write_v3label(pimg, 0x80 | flags, s ? s : "");
1478         opt = 0;
1479         break;
1480       default: /* ignore for now */
1481         return;
1482      }
1483      if (opt) putc(opt, pimg->fh);
1484      /* Output in cm */
[dd1d6369]1485      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1486      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1487      put32((INT32_T)my_round(z * 100.0), pimg->fh);
[d69255f]1488   } else {
[23f7ea7]1489      size_t len;
[badeec8]1490      INT32_T opt = 0;
[4c07c51]1491      SVX_ASSERT(pimg->version > 0);
[a420b49]1492      switch (code) {
[437caf3]1493       case img_LABEL:
1494         if (pimg->version == 1) {
1495            /* put a move before each label */
[95c3272]1496            img_write_item(pimg, img_MOVE, 0, NULL, x, y, z);
[437caf3]1497            put32(2, pimg->fh);
[0ed0e16]1498            fputsnl(s, pimg->fh);
[437caf3]1499            return;
1500         }
[23f7ea7]1501         len = strlen(s);
[af56069]1502         if (len > 255 || strchr(s, '\n')) {
[23f7ea7]1503            /* long label - not in early incarnations of v2 format, but few
1504             * 3d files will need these, so better not to force incompatibility
1505             * with a new version I think... */
[95c3272]1506            putc(7, pimg->fh);
1507            putc(flags, pimg->fh);
[23f7ea7]1508            put32(len, pimg->fh);
1509            fputs(s, pimg->fh);
1510         } else {
[95c3272]1511            putc(0x40 | (flags & 0x3f), pimg->fh);
[23f7ea7]1512            fputsnl(s, pimg->fh);
1513         }
[437caf3]1514         opt = 0;
[23f7ea7]1515         break;
[a420b49]1516       case img_MOVE:
1517         opt = 4;
1518         break;
1519       case img_LINE:
[421b7d2]1520         if (pimg->version > 1) {
[d69255f]1521            opt = 0x80 | (flags & 0x3f);
[437caf3]1522            break;
[421b7d2]1523         }
[437caf3]1524         opt = 5;
[a420b49]1525         break;
[437caf3]1526       default: /* ignore for now */
1527         return;
[a420b49]1528      }
[437caf3]1529      if (pimg->version == 1) {
1530         put32(opt, pimg->fh);
1531      } else {
1532         if (opt) putc(opt, pimg->fh);
[a420b49]1533      }
[d69255f]1534      /* Output in cm */
[dd1d6369]1535      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1536      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1537      put32((INT32_T)my_round(z * 100.0), pimg->fh);
[a420b49]1538   }
[d1b1380]1539}
1540
[ad9a5cd]1541int
[a420b49]1542img_close(img *pimg)
1543{
[ad9a5cd]1544   int result = 1;
[dbb4e19]1545   if (pimg) {
1546      if (pimg->fh) {
[76bbb7c9]1547         if (pimg->fRead) {
1548            osfree(pimg->survey);
[a2ad284]1549            osfree(pimg->title);
1550            osfree(pimg->datestamp);
[76bbb7c9]1551         } else {
1552            /* write end of data marker */
[d69255f]1553            switch (pimg->version) {
1554             case 1:
[5c3c61a]1555               put32((INT32_T)-1, pimg->fh);
[d69255f]1556               break;
1557             case 2:
1558               putc(0, pimg->fh);
1559               break;
1560             case 3:
1561               if (pimg->label_len) putc(0, pimg->fh);
[437caf3]1562               putc(0, pimg->fh);
[d69255f]1563               break;
[5c3c61a]1564            }
1565         }
[ad9a5cd]1566         if (ferror(pimg->fh)) result = 0;
1567         if (fclose(pimg->fh)) result = 0;
[0d922fc]1568         if (!result) img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
[dbb4e19]1569      }
[0ff23cb]1570      osfree(pimg->label_buf);
[dbb4e19]1571      osfree(pimg);
[a420b49]1572   }
[ad9a5cd]1573   return result;
[d1b1380]1574}
Note: See TracBrowser for help on using the repository browser.