source: git/src/img.c @ 6120efa

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 6120efa was 255f57b9, checked in by Olly Betts <olly@…>, 22 years ago

Fix various MSVC warnings.

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

  • Property mode set to 100644
File size: 38.2 KB
Line 
1/* img.c
2 * Routines for reading and writing Survex ".3d" image files
3 * Copyright (C) 1993-2003 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 <= 16) {
761               /* zero prefix using "0" */
762               img_errno = IMG_BADFORMAT;
763               return img_BAD;
764            }
765            c = pimg->label_len - 16 - 1;
766            while (pimg->label_buf[c] != '.' || --opt > 0) {
767               if (--c < 0) {
768                  /* zero prefix using "0" */
769                  img_errno = IMG_BADFORMAT;
770                  return img_BAD;
771               }
772            }
773            c++;
774            pimg->label_len = c;
775            goto again3;
776         }
777         if (opt == 15) {
778            result = img_MOVE;
779            break;
780         }
781         /* 16-31 mean remove (n - 15) characters from the prefix */
782         /* zero prefix using 0 */
783         if (pimg->label_len <= (size_t)(opt - 15)) {
784            img_errno = IMG_BADFORMAT;
785            return img_BAD;
786         }
787         pimg->label_len -= (opt - 15);
788         goto again3;
789       case 1: case 2: {
790         char *q;
791         long len = getc(pimg->fh);
792         if (len == EOF) {
793            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
794            return img_BAD;
795         }
796         if (len == 0xfe) {
797            len += get16(pimg->fh);
798            if (feof(pimg->fh)) {
799               img_errno = IMG_BADFORMAT;
800               return img_BAD;
801            }
802            if (ferror(pimg->fh)) {
803               img_errno = IMG_READERROR;
804               return img_BAD;
805            }
806         } else if (len == 0xff) {
807            len = get32(pimg->fh);
808            if (ferror(pimg->fh)) {
809               img_errno = IMG_READERROR;
810               return img_BAD;
811            }
812            if (feof(pimg->fh) || len < 0xfe + 0xffff) {
813               img_errno = IMG_BADFORMAT;
814               return img_BAD;
815            }
816         }
817
818         if (!check_label_space(pimg, pimg->label_len + len + 1)) {
819            img_errno = IMG_OUTOFMEMORY;
820            return img_BAD;
821         }
822         q = pimg->label_buf + pimg->label_len;
823         pimg->label_len += len;
824         if (len && fread(q, len, 1, pimg->fh) != 1) {
825            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
826            return img_BAD;
827         }
828         q[len] = '\0';
829
830         result = opt & 0x40 ? img_LABEL : img_LINE;
831
832         if (pimg->survey_len) {
833            size_t l = pimg->survey_len;
834            const char *s = pimg->label_buf;
835            if (result == img_LINE) {
836               if (strncmp(pimg->survey, s, l) != 0 ||
837                   !(s[l] == '.' || s[l] == '\0')) {
838                  if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
839                  pimg->pending = 15;
840                  goto again3;
841               }
842            } else {
843               if (strncmp(pimg->survey, s, l + 1) != 0) {
844                  if (!skip_coord(pimg->fh)) return img_BAD;
845                  pimg->pending = 0;
846                  goto again3;
847               }
848            }
849            pimg->label += l;
850            /* skip the dot if there */
851            if (*pimg->label) pimg->label++;
852         }
853
854         if (result == img_LINE && pimg->pending) {
855            *p = pimg->mv;
856            if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
857            pimg->pending = opt;
858            return img_MOVE;
859         }
860         pimg->flags = (int)opt & 0x3f;
861         break;
862       }
863       default:
864         img_errno = IMG_BADFORMAT;
865         return img_BAD;
866      }
867      if (!read_coord(pimg->fh, p)) return img_BAD;
868      pimg->pending = 0;
869      return result;
870   }
871
872   pimg->label = pimg->label_buf;
873
874   if (pimg->version > 0) {
875      static long opt_lookahead = 0;
876      static img_point pt = { 0.0, 0.0, 0.0 };
877      long opt;
878      again: /* label to goto if we get a cross */
879      pimg->label[0] = '\0';
880
881      if (pimg->version == 1) {
882         if (opt_lookahead) {
883            opt = opt_lookahead;
884            opt_lookahead = 0;
885         } else {
886            opt = get32(pimg->fh);
887         }
888      } else {
889         opt = getc(pimg->fh);
890      }
891
892      if (feof(pimg->fh)) {
893         img_errno = IMG_BADFORMAT;
894         return img_BAD;
895      }
896      if (ferror(pimg->fh)) {
897         img_errno = IMG_READERROR;
898         return img_BAD;
899      }
900
901      switch (opt) {
902       case -1: case 0:
903         return img_STOP; /* end of data marker */
904       case 1:
905         /* skip coordinates */
906         if (!skip_coord(pimg->fh)) {
907            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
908            return img_BAD;
909         }
910         goto again;
911       case 2: case 3: {
912         char *q;
913         int ch;
914         result = img_LABEL;
915         ch = getc(pimg->fh);
916         if (ch == EOF) {
917            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
918            return img_BAD;
919         }
920         if (ch != '\\') ungetc(ch, pimg->fh);
921         fgets(pimg->label_buf, 257, pimg->fh);
922         if (feof(pimg->fh)) {
923            img_errno = IMG_BADFORMAT;
924            return img_BAD;
925         }
926         if (ferror(pimg->fh)) {
927            img_errno = IMG_READERROR;
928            return img_BAD;
929         }
930         q = pimg->label_buf + strlen(pimg->label_buf) - 1;
931         if (*q != '\n') {
932            img_errno = IMG_BADFORMAT;
933            return img_BAD;
934         }
935         /* Ignore empty labels in some .3d files (caused by a bug) */
936         if (q == pimg->label_buf) goto again;
937         *q = '\0';
938         pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
939         if (opt == 2) goto done;
940         break;
941       }
942       case 6: case 7: {
943         long len;
944         result = img_LABEL;
945
946         if (opt == 7)
947            pimg->flags = getc(pimg->fh);
948         else
949            pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
950
951         len = get32(pimg->fh);
952
953         if (feof(pimg->fh)) {
954            img_errno = IMG_BADFORMAT;
955            return img_BAD;
956         }
957         if (ferror(pimg->fh)) {
958            img_errno = IMG_READERROR;
959            return img_BAD;
960         }
961
962         /* Ignore empty labels in some .3d files (caused by a bug) */
963         if (len == 0) goto again;
964         if (!check_label_space(pimg, len + 1)) {
965            img_errno = IMG_OUTOFMEMORY;
966            return img_BAD;
967         }
968         if (fread(pimg->label_buf, len, 1, pimg->fh) != 1) {
969            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
970            return img_BAD;
971         }
972         pimg->label_buf[len] = '\0';
973         break;
974       }
975       case 4:
976         result = img_MOVE;
977         break;
978       case 5:
979         result = img_LINE;
980         break;
981       default:
982         switch ((int)opt & 0xc0) {
983          case 0x80:
984            pimg->flags = (int)opt & 0x3f;
985            result = img_LINE;
986            break;
987          case 0x40: {
988            char *q;
989            pimg->flags = (int)opt & 0x3f;
990            result = img_LABEL;
991            if (!fgets(pimg->label_buf, 257, pimg->fh)) {
992               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
993               return img_BAD;
994            }
995            q = pimg->label_buf + strlen(pimg->label_buf) - 1;
996            /* Ignore empty-labels in some .3d files (caused by a bug) */
997            if (q == pimg->label_buf) goto again;
998            if (*q != '\n') {
999               img_errno = IMG_BADFORMAT;
1000               return img_BAD;
1001            }
1002            *q = '\0';
1003            break;
1004          }
1005          case 0xc0:
1006            /* use this for an extra leg or station flag if we need it */
1007          default:
1008            img_errno = IMG_BADFORMAT;
1009            return img_BAD;
1010         }
1011         break;
1012      }
1013
1014      if (!read_coord(pimg->fh, &pt)) return img_BAD;
1015
1016      if (result == img_LABEL && pimg->survey_len) {
1017         if (strncmp(pimg->label_buf, pimg->survey, pimg->survey_len + 1) != 0)
1018            goto again;
1019         pimg->label += pimg->survey_len + 1;
1020      }
1021
1022      done:
1023      *p = pt;
1024
1025      if (result == img_MOVE && pimg->version == 1) {
1026         /* peek at next code and see if it's an old-style label */
1027         opt_lookahead = get32(pimg->fh);
1028
1029         if (feof(pimg->fh)) {
1030            img_errno = IMG_BADFORMAT;
1031            return img_BAD;
1032         }
1033         if (ferror(pimg->fh)) {
1034            img_errno = IMG_READERROR;
1035            return img_BAD;
1036         }
1037
1038         if (opt_lookahead == 2) return img_read_item(pimg, p);
1039      }
1040
1041      return result;
1042   } else if (pimg->version == 0) {
1043      ascii_again:
1044      pimg->label[0] = '\0';
1045      if (feof(pimg->fh)) return img_STOP;
1046      if (pimg->pending) {
1047         pimg->pending = 0;
1048         result = img_LINE;
1049      } else {
1050         char cmd[7];
1051         /* Stop if nothing found */
1052         if (fscanf(pimg->fh, "%6s", cmd) < 1) return img_STOP;
1053         if (strcmp(cmd, "move") == 0)
1054            result = img_MOVE;
1055         else if (strcmp(cmd, "draw") == 0)
1056            result = img_LINE;
1057         else if (strcmp(cmd, "line") == 0) {
1058            /* set flag to indicate to process second triplet as LINE */
1059            pimg->pending = 1;
1060            result = img_MOVE;
1061         } else if (strcmp(cmd, "cross") == 0) {
1062            if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
1063               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
1064               return img_BAD;
1065            }
1066            goto ascii_again;
1067         } else if (strcmp(cmd, "name") == 0) {
1068            size_t off = 0;
1069            int ch = getc(pimg->fh);
1070            if (ch == ' ') ch = getc(pimg->fh);
1071            while (ch != ' ') {
1072               if (ch == '\n' || ch == EOF) {
1073                  img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
1074                  return img_BAD;
1075               }
1076               if (off == pimg->buf_len) {
1077                  if (!check_label_space(pimg, pimg->buf_len * 2)) {
1078                     img_errno = IMG_OUTOFMEMORY;
1079                     return img_BAD;
1080                  }
1081               }
1082               pimg->label_buf[off++] = ch;
1083               ch = getc(pimg->fh);
1084            }
1085            pimg->label_buf[off] = '\0';
1086
1087            pimg->label = pimg->label_buf;
1088            if (pimg->label[0] == '\\') pimg->label++;
1089
1090            result = img_LABEL;
1091         } else {
1092            img_errno = IMG_BADFORMAT;
1093            return img_BAD; /* unknown keyword */
1094         }
1095      }
1096
1097      if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
1098         img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
1099         return img_BAD;
1100      }
1101
1102      if (result == img_LABEL && pimg->survey_len) {
1103         if (strncmp(pimg->label, pimg->survey, pimg->survey_len + 1) != 0)
1104            goto ascii_again;
1105         pimg->label += pimg->survey_len + 1;
1106      }
1107
1108      return result;
1109   } else if (pimg->version == -1) {
1110      /* version -1: .pos file */
1111      size_t off;
1112      pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1113      againpos:
1114      off = 0;
1115      while (fscanf(pimg->fh, "(%lf,%lf,%lf ) ", &p->x, &p->y, &p->z) != 3) {
1116         int ch;
1117         if (ferror(pimg->fh)) {
1118            img_errno = IMG_READERROR;
1119            return img_BAD;
1120         }
1121         if (feof(pimg->fh)) return img_STOP;
1122         if (pimg->pending) {
1123            img_errno = IMG_BADFORMAT;
1124            return img_BAD;
1125         }
1126         pimg->pending = 1;
1127         /* ignore rest of line */
1128         do {
1129            ch = getc(pimg->fh);
1130         } while (ch != '\n' && ch != '\r' && ch != EOF);
1131      }
1132
1133      pimg->label_buf[0] = '\0';
1134      while (!feof(pimg->fh)) {
1135         if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
1136            img_errno = IMG_READERROR;
1137            return img_BAD;
1138         }
1139
1140         off += strlen(pimg->label_buf + off);
1141         if (off && pimg->label_buf[off - 1] == '\n') {
1142            pimg->label_buf[off - 1] = '\0';
1143            break;
1144         }
1145         if (!check_label_space(pimg, pimg->buf_len * 2)) {
1146            img_errno = IMG_OUTOFMEMORY;
1147            return img_BAD;
1148         }
1149      }
1150
1151      pimg->label = pimg->label_buf;
1152
1153      if (pimg->label[0] == '\\') pimg->label++;
1154
1155      if (pimg->survey_len) {
1156         size_t l = pimg->survey_len + 1;
1157         if (strncmp(pimg->survey, pimg->label, l) != 0) goto againpos;
1158         pimg->label += l;
1159      }
1160
1161      return img_LABEL;
1162   } else if (pimg->version == -2) {
1163      /* version -2: Compass .plt file */
1164      if (pimg->pending > 0) {
1165         /* -1 signals we've entered the first survey we want to
1166          * read, and need to fudge lots if the first action is 'D'...
1167          */
1168         /* pending MOVE or LINE */
1169         int r = pimg->pending - 4;
1170         pimg->pending = 0;
1171         pimg->flags = 0;
1172         pimg->label[pimg->label_len] = '\0';
1173         return r;
1174      }
1175
1176      while (1) {
1177         char *line;
1178         char *q;
1179         size_t len = 0;
1180         int ch = getc(pimg->fh);
1181
1182         switch (ch) {
1183            case '\x1a': case EOF: /* Don't insist on ^Z at end of file */
1184               return img_STOP;
1185            case 'X': case 'F': case 'S':
1186               /* bounding boX (marks end of survey), Feature survey, or
1187                * new Section - skip to next survey */
1188               if (pimg->survey) return img_STOP;
1189skip_to_N:
1190               while (1) {
1191                  do {
1192                     ch = getc(pimg->fh);
1193                  } while (ch != '\n' && ch != '\r' && ch != EOF);
1194                  while (ch == '\n' || ch == '\r') ch = getc(pimg->fh);
1195                  if (ch == 'N') break;
1196                  if (ch == '\x1a' || ch == EOF) return img_STOP;
1197               }
1198               /* FALLTHRU */
1199            case 'N':
1200               line = getline_alloc(pimg->fh);
1201               if (!line) {
1202                  img_errno = IMG_OUTOFMEMORY;
1203                  return img_BAD;
1204               }
1205               while (line[len] > 32) ++len;
1206               if (pimg->label_len == 0) pimg->pending = -1;
1207               if (!check_label_space(pimg, len + 1)) {
1208                  osfree(line);
1209                  img_errno = IMG_OUTOFMEMORY;
1210                  return img_BAD;
1211               }
1212               pimg->label_len = len;
1213               pimg->label = pimg->label_buf;
1214               memcpy(pimg->label, line, len);
1215               pimg->label[len] = '\0';
1216               osfree(line);
1217               break;
1218            case 'M': case 'D': {
1219               /* Move or Draw */
1220               long fpos = -1;
1221               if (pimg->survey && pimg->label_len == 0) {
1222                  /* We're only holding onto this line in case the first line
1223                   * of the 'N' is a 'D', so skip it for now...
1224                   */
1225                  goto skip_to_N;
1226               }
1227               if (ch == 'D' && pimg->pending == -1) {
1228                  if (pimg->survey) {
1229                     fpos = ftell(pimg->fh) - 1;
1230                     fseek(pimg->fh, pimg->start, SEEK_SET);
1231                     ch = getc(pimg->fh);
1232                     pimg->pending = 0;
1233                  } else {
1234                     /* If a file actually has a 'D' before any 'M', then
1235                      * pretend the 'D' is an 'M' - one of the examples
1236                      * in the docs was like this! */
1237                     ch = 'M';
1238                  }
1239               }
1240               line = getline_alloc(pimg->fh);
1241               if (!line) {
1242                  img_errno = IMG_OUTOFMEMORY;
1243                  return img_BAD;
1244               }
1245               /* Compass store coordinates as North, East, Up = (y,x,z)! */
1246               if (sscanf(line, "%lf%lf%lf", &p->y, &p->x, &p->z) != 3) {
1247                  osfree(line);
1248                  if (ferror(pimg->fh)) {
1249                     img_errno = IMG_READERROR;
1250                  } else {
1251                     img_errno = IMG_BADFORMAT;
1252                  }
1253                  return img_BAD;
1254               }
1255               p->x *= METRES_PER_FOOT;
1256               p->y *= METRES_PER_FOOT;
1257               p->z *= METRES_PER_FOOT;
1258               q = strchr(line, 'S');
1259               if (!q) {
1260                  osfree(line);
1261                  img_errno = IMG_BADFORMAT;
1262                  return img_BAD;
1263               }
1264               ++q;
1265               len = 0;
1266               while (q[len] > ' ') ++len;
1267               q[len] = '\0';
1268               len += 2; /* ' ' and '\0' */
1269               if (!check_label_space(pimg, pimg->label_len + len)) {
1270                  img_errno = IMG_OUTOFMEMORY;
1271                  return img_BAD;
1272               }
1273               pimg->label = pimg->label_buf;
1274               if (pimg->label_len) {
1275                   pimg->label[pimg->label_len] = ' ';
1276                   memcpy(pimg->label + pimg->label_len + 1, q, len - 1);
1277               } else {
1278                   memcpy(pimg->label, q, len - 1);
1279               }
1280               osfree(line);
1281               pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1282               if (fpos != -1) {
1283                  fseek(pimg->fh, fpos, SEEK_SET);
1284               } else {
1285                  pimg->pending = (ch == 'M' ? img_MOVE : img_LINE) + 4;
1286               }
1287               return img_LABEL;
1288            }
1289            default:
1290               img_errno = IMG_BADFORMAT;
1291               return img_BAD;
1292         }
1293      }
1294   } else {
1295      /* version -3 or -4: CMAP .xyz file */
1296      char *line = NULL;
1297      char *q;
1298      size_t len;
1299
1300      if (pimg->pending) {
1301         /* pending MOVE or LINE or LABEL or STOP */
1302         int r = pimg->pending - 4;
1303         /* Set label to empty - don't use "" as we adjust label relative
1304          * to label_buf when label_buf is reallocated. */
1305         pimg->label = pimg->label_buf + strlen(pimg->label_buf);
1306         pimg->flags = 0;
1307         if (r == img_LABEL) {
1308            /* nasty magic */
1309            read_xyz_shot_coords(p, pimg->label_buf + 16);
1310            subtract_xyz_shot_deltas(p, pimg->label_buf + 16);
1311            pimg->pending = img_STOP + 4;
1312            return img_MOVE;
1313         }
1314       
1315         pimg->pending = 0;
1316
1317         if (r == img_STOP) {
1318            /* nasty magic */
1319            read_xyz_shot_coords(p, pimg->label_buf + 16);
1320            return img_LINE;
1321         }
1322         
1323         return r;
1324      }
1325
1326      pimg->label = pimg->label_buf;
1327      do {
1328         osfree(line);
1329         if (feof(pimg->fh)) return img_STOP;
1330         line = getline_alloc(pimg->fh);
1331         if (!line) {
1332            img_errno = IMG_OUTOFMEMORY;
1333            return img_BAD;
1334         }
1335      } while (line[0] == ' ' || line[0] == '\0');
1336      if (line[0] == '\x1a') return img_STOP;
1337
1338      len = strlen(line);
1339      if (pimg->version == -3) {
1340         /* station variant */
1341         if (len < 37) {
1342            osfree(line);
1343            img_errno = IMG_BADFORMAT;
1344            return img_BAD;
1345         }
1346         memcpy(pimg->label, line, 6);
1347         q = memchr(pimg->label, ' ', 6);
1348         if (!q) q = pimg->label + 6;
1349         *q = '\0';
1350
1351         read_xyz_station_coords(p, line);
1352 
1353         /* FIXME: look at prev for lines (line + 32, 5) */
1354         /* FIXME: duplicate stations... */
1355         return img_LABEL;
1356      } else {
1357         /* Shot variant */
1358         char old[8], new[8];
1359         if (len < 61) {
1360            osfree(line);
1361            img_errno = IMG_BADFORMAT;
1362            return img_BAD;
1363         }
1364 
1365         memcpy(old, line, 7);
1366         q = memchr(old, ' ', 7);
1367         if (!q) q = old + 7;
1368         *q = '\0';
1369
1370         memcpy(new, line + 7, 7);
1371         q = memchr(new, ' ', 7);
1372         if (!q) q = new + 7;
1373         *q = '\0';
1374
1375         pimg->flags = img_SFLAG_UNDERGROUND;
1376
1377         if (strcmp(old, new) == 0) {
1378            pimg->pending = img_MOVE + 4;
1379            read_xyz_shot_coords(p, line);
1380            strcpy(pimg->label, new);
1381            osfree(line);
1382            return img_LABEL;
1383         }
1384 
1385         if (strcmp(old, pimg->label) == 0) {
1386            pimg->pending = img_LINE + 4;
1387            read_xyz_shot_coords(p, line);
1388            strcpy(pimg->label, new);
1389            osfree(line);
1390            return img_LABEL;
1391         }
1392         
1393         pimg->pending = img_LABEL + 4;
1394         read_xyz_shot_coords(p, line);
1395         strcpy(pimg->label, new);
1396         memcpy(pimg->label + 16, line, 70);
1397
1398         osfree(line);
1399         return img_LABEL;
1400      }
1401   }
1402}
1403
1404static int
1405write_v3label(img *pimg, int opt, const char *s)
1406{
1407   size_t len, n, dot;
1408
1409   /* find length of common prefix */
1410   dot = 0;
1411   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
1412      if (s[len] == '.') dot = len + 1;
1413   }
1414
1415   SVX_ASSERT(len <= pimg->label_len);
1416   n = pimg->label_len - len;
1417   if (len == 0) {
1418      if (pimg->label_len) putc(0, pimg->fh);
1419   } else if (n <= 16) {
1420      if (n) putc(n + 15, pimg->fh);
1421   } else if (dot == 0) {
1422      if (pimg->label_len) putc(0, pimg->fh);
1423      len = 0;
1424   } else {
1425      const char *p = pimg->label_buf + dot;
1426      n = 1;
1427      for (len = pimg->label_len - dot - 17; len; len--) {
1428         if (*p++ == '.') n++;
1429      }
1430      if (n <= 14) {
1431         putc(n, pimg->fh);
1432         len = dot;
1433      } else {
1434         if (pimg->label_len) putc(0, pimg->fh);
1435         len = 0;
1436      }
1437   }
1438
1439   n = strlen(s + len);
1440   putc(opt, pimg->fh);
1441   if (n < 0xfe) {
1442      putc(n, pimg->fh);
1443   } else if (n < 0xffff + 0xfe) {
1444      putc(0xfe, pimg->fh);
1445      put16((short)(n - 0xfe), pimg->fh);
1446   } else {
1447      putc(0xff, pimg->fh);
1448      put32(n, pimg->fh);
1449   }
1450   fwrite(s + len, n, 1, pimg->fh);
1451
1452   n += len;
1453   pimg->label_len = n;
1454   if (!check_label_space(pimg, n + 1))
1455      return 0; /* FIXME: distinguish out of memory... */
1456   memcpy(pimg->label_buf + len, s + len, n - len + 1);
1457
1458   return !ferror(pimg->fh);
1459}
1460
1461void
1462img_write_item(img *pimg, int code, int flags, const char *s,
1463               double x, double y, double z)
1464{
1465   if (!pimg) return;
1466   if (pimg->version == 3) {
1467      int opt = 0;
1468      switch (code) {
1469       case img_LABEL:
1470         write_v3label(pimg, 0x40 | flags, s);
1471         opt = 0;
1472         break;
1473       case img_MOVE:
1474         opt = 15;
1475         break;
1476       case img_LINE:
1477         write_v3label(pimg, 0x80 | flags, s ? s : "");
1478         opt = 0;
1479         break;
1480       default: /* ignore for now */
1481         return;
1482      }
1483      if (opt) putc(opt, pimg->fh);
1484      /* Output in cm */
1485      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1486      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1487      put32((INT32_T)my_round(z * 100.0), pimg->fh);
1488   } else {
1489      size_t len;
1490      INT32_T opt = 0;
1491      SVX_ASSERT(pimg->version > 0);
1492      switch (code) {
1493       case img_LABEL:
1494         if (pimg->version == 1) {
1495            /* put a move before each label */
1496            img_write_item(pimg, img_MOVE, 0, NULL, x, y, z);
1497            put32(2, pimg->fh);
1498            fputsnl(s, pimg->fh);
1499            return;
1500         }
1501         len = strlen(s);
1502         if (len > 255 || strchr(s, '\n')) {
1503            /* long label - not in early incarnations of v2 format, but few
1504             * 3d files will need these, so better not to force incompatibility
1505             * with a new version I think... */
1506            putc(7, pimg->fh);
1507            putc(flags, pimg->fh);
1508            put32(len, pimg->fh);
1509            fputs(s, pimg->fh);
1510         } else {
1511            putc(0x40 | (flags & 0x3f), pimg->fh);
1512            fputsnl(s, pimg->fh);
1513         }
1514         opt = 0;
1515         break;
1516       case img_MOVE:
1517         opt = 4;
1518         break;
1519       case img_LINE:
1520         if (pimg->version > 1) {
1521            opt = 0x80 | (flags & 0x3f);
1522            break;
1523         }
1524         opt = 5;
1525         break;
1526       default: /* ignore for now */
1527         return;
1528      }
1529      if (pimg->version == 1) {
1530         put32(opt, pimg->fh);
1531      } else {
1532         if (opt) putc(opt, pimg->fh);
1533      }
1534      /* Output in cm */
1535      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1536      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1537      put32((INT32_T)my_round(z * 100.0), pimg->fh);
1538   }
1539}
1540
1541int
1542img_close(img *pimg)
1543{
1544   int result = 1;
1545   if (pimg) {
1546      if (pimg->fh) {
1547         if (pimg->fRead) {
1548            osfree(pimg->survey);
1549            osfree(pimg->title);
1550            osfree(pimg->datestamp);
1551         } else {
1552            /* write end of data marker */
1553            switch (pimg->version) {
1554             case 1:
1555               put32((INT32_T)-1, pimg->fh);
1556               break;
1557             case 2:
1558               putc(0, pimg->fh);
1559               break;
1560             case 3:
1561               if (pimg->label_len) putc(0, pimg->fh);
1562               putc(0, pimg->fh);
1563               break;
1564            }
1565         }
1566         if (ferror(pimg->fh)) result = 0;
1567         if (fclose(pimg->fh)) result = 0;
1568         if (!result) img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
1569      }
1570      osfree(pimg->label_buf);
1571      osfree(pimg);
1572   }
1573   return result;
1574}
Note: See TracBrowser for help on using the repository browser.