source: git/src/img.c @ 0810c70

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

Fix reading of ASCII 3d files with CR+LF end of lines.

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

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