source: git/src/img.c @ f9dc4a0

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

Fixed problem loading an unrecognised filetype (tried to free an
uninitialised pointer)

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

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