source: git/src/img.c @ 168df28

RELEASE/1.0RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since 168df28 was 168df28, checked in by Olly Betts <olly@…>, 19 years ago

Backport a whole pile of fixes and minor tweaks from 1.1.7.

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

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