source: git/src/img.c @ 1ec8db0

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

aven: recognise ".plf" as a Compass PLT file too; and on Unix list "*.PLT",
"*.PLF", "*.XYZ", and "*.3D" as well as the lowercase versions to help people
who transfer files from MSDOS/MS Windows.

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

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