source: git/src/img.c @ ad83822

RELEASE/1.0RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-datawalls-data-hanging-as-warning
Last change on this file since ad83822 was 76d0b1e, checked in by Olly Betts <olly@…>, 19 years ago

Backport new img file format (so we can read 3d files generated by 1.1.x).
Backport the filename normalisation tweak.

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

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