source: git/src/img.c @ 582b10b

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 582b10b was c46952f, checked in by Olly Betts <olly@…>, 21 years ago

Fixed 3d file reading code to fix incorrect unpacking of compressed station
names in a rare case. Also checked other rare cases work correctly.

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

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