source: git/src/img.c @ 9ca0c47

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 9ca0c47 was 984f49e, checked in by Olly Betts <olly@…>, 23 years ago

survey restriction now works on .plt files.

added --enable-testprogs option to configure.

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

  • Property mode set to 100644
File size: 30.2 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
135/* Attempt to string paste to ensure we are passed a literal string */
136#define LITLEN(S) (sizeof(S"") - 1)
137
138static char *
139my_strdup(const char *str)
140{
141   char *p;
142   size_t len = strlen(str) + 1;
143   p = xosmalloc(len);
144   if (p) memcpy(p, str, len);
145   return p;
146}
147
148static char *
149getline_alloc(FILE *fh)
150{
151   int ch;
152   size_t i = 0;
153   size_t len = 16;
154   char *buf = xosmalloc(len);
155   if (!buf) return NULL;
156
157   ch = getc(fh);
158   while (ch != '\n' && ch != '\r' && ch != EOF) {
159      buf[i++] = ch;
160      if (i == len - 1) {
161         char *p;
162         len += len;
163         p = xosrealloc(buf, len);
164         if (!p) {
165            osfree(buf);
166            return NULL;
167         }
168         buf = p;
169      }
170      ch = getc(fh);
171   }
172   if (ch == '\n' || ch == '\r') {
173      /* remove any further eol chars (for DOS text files) */
174      do {
175         ch = getc(fh);
176      } while (ch == '\n' || ch == '\r');
177      ungetc(ch, fh); /* we don't want it, so put it back */
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
197img *
198img_open_survey(const char *fnm, const char *survey)
199{
200   img *pimg;
201   size_t len;
202   char buf[LITLEN(FILEID) + 1];
203   int ch;
204
205   if (fDirectory(fnm)) {
206      img_errno = IMG_DIRECTORY;
207      return NULL;
208   }
209
210   pimg = (img *)xosmalloc(ossizeof(img));
211   if (pimg == NULL) {
212      img_errno = IMG_OUTOFMEMORY;
213      return NULL;
214   }
215
216   pimg->buf_len = 257;
217   pimg->label_buf = xosmalloc(pimg->buf_len);
218   if (!pimg->label_buf) {
219      osfree(pimg);
220      img_errno = IMG_OUTOFMEMORY;
221      return NULL;
222   }
223
224   pimg->fh = fopenWithPthAndExt("", fnm, EXT_SVX_3D, "rb", NULL);
225   if (pimg->fh == NULL) {
226      osfree(pimg);
227      img_errno = IMG_FILENOTFOUND;
228      return NULL;
229   }
230
231   pimg->fRead = fTrue; /* reading from this file */
232   img_errno = IMG_NONE;
233
234   pimg->flags = 0;
235
236   /* for version 3 we use label_buf to store the prefix for reuse */
237   pimg->label_len = 0;
238
239   pimg->survey = NULL;
240   pimg->survey_len = 0;
241
242   pimg->title = NULL;
243   if (survey) {
244      len = strlen(survey);
245      if (len) {
246         if (survey[len - 1] == '.') len--;
247         if (len) {
248            char *p;
249            pimg->survey = osmalloc(len + 2);
250            memcpy(pimg->survey, survey, len);
251            /* Set title to leaf survey name */
252            pimg->survey[len] = '\0';
253            p = strchr(pimg->survey, '.');
254            if (p) p++; else p = pimg->survey;
255            pimg->title = my_strdup(p);
256            if (!pimg->title) {
257               img_errno = IMG_OUTOFMEMORY;
258               goto error;
259            }
260            pimg->survey[len] = '.';
261            pimg->survey[len + 1] = '\0';
262         }
263      }
264      pimg->survey_len = len;
265   }
266
267   /* [version -2] pending IMG_LINE ('D') or IMG_MOVE ('M')
268    * [version -1] already skipped heading line, or there wasn't one
269    * [version 0] not in the middle of a 'LINE' command
270    * [version 3] not in the middle of turning a LINE into a MOVE
271    */
272   pimg->pending = 0;
273
274   len = strlen(fnm);
275   if (len > LITLEN(EXT_SVX_POS) + 1 &&
276       fnm[len - LITLEN(EXT_SVX_POS) - 1] == FNM_SEP_EXT &&
277       my_strcasecmp(fnm + len - LITLEN(EXT_SVX_POS), EXT_SVX_POS) == 0) {
278      pimg->version = -1;
279      if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
280      pimg->datestamp = my_strdup(TIMENA);
281      if (!pimg->datestamp) {
282         img_errno = IMG_OUTOFMEMORY;
283         goto error;
284      }
285      pimg->start = 0;
286      return pimg;
287   }
288   
289   if (len > LITLEN(EXT_PLT) + 1 &&
290       fnm[len - LITLEN(EXT_PLT) - 1] == FNM_SEP_EXT &&
291       my_strcasecmp(fnm + len - LITLEN(EXT_PLT), EXT_PLT) == 0) {
292      char *line = NULL;
293
294      pimg->version = -2;
295      if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
296      pimg->datestamp = my_strdup(TIMENA);
297      if (!pimg->datestamp) {
298         img_errno = IMG_OUTOFMEMORY;
299         goto error;
300      }
301      do {
302         do {
303            osfree(line);
304            line = getline_alloc(pimg->fh);
305            if (!line) {
306               img_errno = IMG_OUTOFMEMORY;
307               goto error;
308            }
309            if (line[0] == '\x1a') {
310               osfree(line);
311               pimg->pending = img_STOP + 4;
312               return pimg;
313            }
314            if (feof(pimg->fh)) {
315               osfree(line);
316               img_errno = IMG_BADFORMAT;
317               goto error;
318            }
319         } while (line[0] != 'N');
320
321         len = 1;
322         while (line[len] > 32) ++len;
323      } while (pimg->survey && (pimg->survey_len != len - 1 ||
324               memcmp(line + 1, pimg->survey, pimg->survey_len) != 0));
325
326      if (len > pimg->buf_len) {
327         char *b = xosrealloc(pimg->label_buf, len);
328         if (!b) {
329            img_errno = IMG_OUTOFMEMORY;
330            goto error;
331         }
332         pimg->label_buf = b;
333         pimg->buf_len = len;
334      }
335      --len;
336      pimg->label_len = len;
337      pimg->label = pimg->label_buf;
338      memcpy(pimg->label, line + 1, len);
339      pimg->label[len] = '\0';
340      if (pimg->survey) {
341         char *q = strchr(line + len, 'C');
342         if (q && q[1]) {
343            osfree(pimg->title);
344            pimg->title = osstrdup(q + 1);
345         } else if (!pimg->title) {
346            pimg->title = osstrdup(pimg->label);
347         }
348         if (!pimg->title) {
349            img_errno = IMG_OUTOFMEMORY;
350            goto error;
351         }
352      }
353      osfree(line);
354      pimg->start = 0;
355      return pimg;
356   }
357
358   if (fread(buf, LITLEN(FILEID) + 1, 1, pimg->fh) != 1 ||
359       memcmp(buf, FILEID"\n", LITLEN(FILEID)) != 0) {
360      img_errno = IMG_BADFORMAT;
361      goto error;
362   }
363
364   /* check file format version */
365   ch = getc(pimg->fh);
366   pimg->version = 0;
367   if (tolower(ch) == 'b') {
368      /* binary file iff B/b prefix */
369      pimg->version = 1;
370      ch = getc(pimg->fh);
371   }
372   if (ch != 'v') {
373      img_errno = IMG_BADFORMAT;
374      goto error;
375   }
376   ch = getc(pimg->fh);
377   if (ch == '0') {
378      if (fread(buf, 4, 1, pimg->fh) != 1 || memcmp(buf, ".01\n", 4) != 0) {
379         img_errno = IMG_BADFORMAT;
380         goto error;
381      }
382      /* nothing special to do */
383   } else if (pimg->version == 0) {
384      if (ch < '2' || ch > '3' || getc(pimg->fh) != '\n') {
385         img_errno = IMG_TOONEW;
386         goto error;
387      }
388      pimg->version = ch - '0';
389   } else {
390      img_errno = IMG_BADFORMAT;
391      goto error;
392   }
393
394   if (!pimg->title)
395       pimg->title = getline_alloc(pimg->fh);
396   else
397       osfree(getline_alloc(pimg->fh));
398   pimg->datestamp = getline_alloc(pimg->fh);
399   if (!pimg->title || !pimg->datestamp) {
400      img_errno = IMG_OUTOFMEMORY;
401      error:
402      osfree(pimg->title);
403      osfree(pimg->datestamp);
404      fclose(pimg->fh);
405      osfree(pimg);
406      return NULL;
407   }
408
409   pimg->start = ftell(pimg->fh);
410
411   return pimg;
412}
413
414void
415img_rewind(img *pimg)
416{
417   fseek(pimg->fh, pimg->start, SEEK_SET);
418   clearerr(pimg->fh);
419   /* [version -1] already skipped heading line, or there wasn't one
420    * [version 0] not in the middle of a 'LINE' command
421    * [version 3] not in the middle of turning a LINE into a MOVE */
422   pimg->pending = 0; /* FIXME: consider rewind on .plt with survey matching nothing ... */
423
424   img_errno = IMG_NONE;
425
426   /* for version 3 we use label_buf to store the prefix for reuse */
427   pimg->label_len = 0;
428}
429
430img *
431img_open_write(const char *fnm, char *title_buf, bool fBinary)
432{
433   time_t tm;
434   img *pimg;
435
436   fBinary = fBinary;
437
438   if (fDirectory(fnm)) {
439      img_errno = IMG_DIRECTORY;
440      return NULL;
441   }
442
443   pimg = (img *)xosmalloc(ossizeof(img));
444   if (pimg == NULL) {
445      img_errno = IMG_OUTOFMEMORY;
446      return NULL;
447   }
448
449   pimg->buf_len = 257;
450   pimg->label_buf = xosmalloc(pimg->buf_len);
451   if (!pimg->label_buf) {
452      osfree(pimg);
453      img_errno = IMG_OUTOFMEMORY;
454      return NULL;
455   }
456
457   pimg->fh = fopen(fnm, "wb");
458   if (!pimg->fh) {
459      osfree(pimg);
460      img_errno = IMG_CANTOPENOUT;
461      return NULL;
462   }
463
464   /* Output image file header */
465   fputs("Survex 3D Image File\n", pimg->fh); /* file identifier string */
466   if (img_output_version < 2) {
467      pimg->version = 1;
468      fputs("Bv0.01\n", pimg->fh); /* binary file format version number */
469   } else {
470      pimg->version = (img_output_version > 2) ? 3 : 2;
471      fprintf(pimg->fh, "v%d\n", pimg->version); /* file format version no. */
472   }
473   fputsnl(title_buf, pimg->fh);
474   tm = time(NULL);
475   if (tm == (time_t)-1) {
476      fputsnl(TIMENA, pimg->fh);
477   } else {
478      char date[256];
479      /* output current date and time in format specified */
480      strftime(date, 256, TIMEFMT, localtime(&tm));
481      fputsnl(date, pimg->fh);
482   }
483   pimg->fRead = fFalse; /* writing to this file */
484   img_errno = IMG_NONE;
485
486   /* for version 3 we use label_buf to store the prefix for reuse */
487   pimg->label_buf[0] = '\0';
488   pimg->label_len = 0;
489
490   /* Don't check for write errors now - let img_close() report them... */
491   return pimg;
492}
493
494static int
495read_coord(FILE *fh, img_point *pt)
496{
497   ASSERT(fh);
498   ASSERT(pt);
499   pt->x = get32(fh) / 100.0;
500   pt->y = get32(fh) / 100.0;
501   pt->z = get32(fh) / 100.0;
502   if (ferror(fh) || feof(fh)) {
503      img_errno = feof(fh) ? IMG_BADFORMAT : IMG_READERROR;
504      return 0;
505   }
506   return 1;
507}
508
509static int
510skip_coord(FILE *fh)
511{
512    return (fseek(fh, 12, SEEK_CUR) == 0);
513}
514
515int
516img_read_item(img *pimg, img_point *p)
517{
518   int result;
519   pimg->flags = 0;
520
521   if (pimg->version == 3) {
522      int opt;
523      if (pimg->pending >= 0x80) {
524         *p = pimg->mv;
525         pimg->flags = (int)(pimg->pending) & 0x3f;
526         pimg->pending = 0;
527         return img_LINE;
528      }
529      again3: /* label to goto if we get a prefix */
530      pimg->label = pimg->label_buf;
531      opt = getc(pimg->fh);
532      if (opt == EOF) {
533         img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
534         return img_BAD;
535      }
536      switch (opt >> 6) {
537       case 0:
538         if (opt == 0) {
539            if (!pimg->label_len) return img_STOP; /* end of data marker */
540            pimg->label_len = 0;
541            goto again3;
542         }
543         if (opt < 15) {
544            /* 1-14 mean trim that many levels from current prefix */
545            int c;
546            if (pimg->label_len <= 16) {
547               /* zero prefix using "0" */
548               img_errno = IMG_BADFORMAT;
549               return img_BAD;
550            }
551            c = pimg->label_len - 16 - 1;
552            while (pimg->label_buf[c] != '.' || --opt > 0) {
553               if (--c < 0) {
554                  /* zero prefix using "0" */
555                  img_errno = IMG_BADFORMAT;
556                  return img_BAD;
557               }
558            }
559            c++;
560            pimg->label_len = c;
561            goto again3;
562         }
563         if (opt == 15) {
564            result = img_MOVE;
565            break;
566         }
567         /* 16-31 mean remove (n - 15) characters from the prefix */
568         /* zero prefix using 0 */
569         if (pimg->label_len <= (size_t)(opt - 15)) {
570            img_errno = IMG_BADFORMAT;
571            return img_BAD;
572         }
573         pimg->label_len -= (opt - 15);
574         goto again3;
575       case 1: case 2: {
576         char *q;
577         long len = getc(pimg->fh);
578         if (len == EOF) {
579            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
580            return img_BAD;
581         }
582         if (len == 0xfe) {
583            len += get16(pimg->fh);
584            if (feof(pimg->fh)) {
585               img_errno = IMG_BADFORMAT;
586               return img_BAD;
587            }
588            if (ferror(pimg->fh)) {
589               img_errno = IMG_READERROR;
590               return img_BAD;
591            }
592         } else if (len == 0xff) {
593            len = get32(pimg->fh);
594            if (ferror(pimg->fh)) {
595               img_errno = IMG_READERROR;
596               return img_BAD;
597            }
598            if (feof(pimg->fh) || len < 0xfe + 0xffff) {
599               img_errno = IMG_BADFORMAT;
600               return img_BAD;
601            }
602         }
603
604         q = pimg->label_buf + pimg->label_len;
605         pimg->label_len += len;
606         if (pimg->label_len >= pimg->buf_len) {
607            char *b = xosrealloc(pimg->label_buf, pimg->label_len + 1);
608            if (!b) {
609               img_errno = IMG_OUTOFMEMORY;
610               return img_BAD;
611            }
612            q = (q - pimg->label_buf) + b;
613            pimg->label = pimg->label_buf = b;
614            pimg->buf_len = pimg->label_len + 1;
615         }
616         if (len && fread(q, len, 1, pimg->fh) != 1) {
617            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
618            return img_BAD;
619         }
620         q[len] = '\0';
621
622         result = opt & 0x40 ? img_LABEL : img_LINE;
623
624         if (pimg->survey_len) {
625            size_t l = pimg->survey_len;
626            const char *s = pimg->label_buf;
627            if (result == img_LINE) {
628               if (strncmp(pimg->survey, s, l) != 0 ||
629                   !(s[l] == '.' || s[l] == '\0')) {
630                  if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
631                  pimg->pending = 15;
632                  goto again3;
633               }
634            } else {
635               if (strncmp(pimg->survey, s, l + 1) != 0) {
636                  if (!skip_coord(pimg->fh)) return img_BAD;
637                  pimg->pending = 0;
638                  goto again3;
639               }
640            }
641            pimg->label += l;
642            /* skip the dot if there */
643            if (*pimg->label) pimg->label++;
644         }
645
646         if (result == img_LINE && pimg->pending) {
647            *p = pimg->mv;
648            if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
649            pimg->pending = opt;
650            return img_MOVE;
651         }
652         pimg->flags = (int)opt & 0x3f;
653         break;
654       }
655       default:
656         img_errno = IMG_BADFORMAT;
657         return img_BAD;
658      }
659      if (!read_coord(pimg->fh, p)) return img_BAD;
660      pimg->pending = 0;
661      return result;
662   }
663
664   pimg->label = pimg->label_buf;
665
666   if (pimg->version > 0) {
667      static long opt_lookahead = 0;
668      static img_point pt = { 0.0, 0.0, 0.0 };
669      long opt;
670      again: /* label to goto if we get a cross */
671      pimg->label[0] = '\0';
672
673      if (pimg->version == 1) {
674         if (opt_lookahead) {
675            opt = opt_lookahead;
676            opt_lookahead = 0;
677         } else {
678            opt = get32(pimg->fh);
679         }
680      } else {
681         opt = getc(pimg->fh);
682      }
683
684      if (feof(pimg->fh)) {
685         img_errno = IMG_BADFORMAT;
686         return img_BAD;
687      }
688      if (ferror(pimg->fh)) {
689         img_errno = IMG_READERROR;
690         return img_BAD;
691      }
692
693      switch (opt) {
694       case -1: case 0:
695         return img_STOP; /* end of data marker */
696       case 1:
697         /* skip coordinates */
698         if (!skip_coord(pimg->fh)) {
699            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
700            return img_BAD;
701         }
702         goto again;
703       case 2: case 3: {
704         char *q;
705         int ch;
706         result = img_LABEL;
707         ch = getc(pimg->fh);
708         if (ch == EOF) {
709            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
710            return img_BAD;
711         }
712         if (ch != '\\') ungetc(ch, pimg->fh);
713         fgets(pimg->label_buf, 257, pimg->fh);
714         if (feof(pimg->fh)) {
715            img_errno = IMG_BADFORMAT;
716            return img_BAD;
717         }
718         if (ferror(pimg->fh)) {
719            img_errno = IMG_READERROR;
720            return img_BAD;
721         }
722         q = pimg->label_buf + strlen(pimg->label_buf) - 1;
723         if (*q != '\n') {
724            img_errno = IMG_BADFORMAT;
725            return img_BAD;
726         }
727         /* Ignore empty labels in some .3d files (caused by a bug) */
728         if (q == pimg->label_buf) goto again;
729         *q = '\0';
730         pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
731         if (opt == 2) goto done;
732         break;
733       }
734       case 6: case 7: {
735         long len;
736         result = img_LABEL;
737
738         if (opt == 7)
739            pimg->flags = getc(pimg->fh);
740         else
741            pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
742
743         len = get32(pimg->fh);
744
745         if (feof(pimg->fh)) {
746            img_errno = IMG_BADFORMAT;
747            return img_BAD;
748         }
749         if (ferror(pimg->fh)) {
750            img_errno = IMG_READERROR;
751            return img_BAD;
752         }
753
754         /* Ignore empty labels in some .3d files (caused by a bug) */
755         if (len == 0) goto again;
756         if (len >= (long)pimg->buf_len) {
757            char *b = xosrealloc(pimg->label_buf, len + 1);
758            if (!b) {
759               img_errno = IMG_OUTOFMEMORY;
760               return img_BAD;
761            }
762            pimg->label = pimg->label_buf = b;
763            pimg->buf_len = len + 1;
764         }
765         if (fread(pimg->label_buf, len, 1, pimg->fh) != 1) {
766            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
767            return img_BAD;
768         }
769         break;
770       }
771       case 4:
772         result = img_MOVE;
773         break;
774       case 5:
775         result = img_LINE;
776         break;
777       default:
778         switch ((int)opt & 0xc0) {
779          case 0x80:
780            pimg->flags = (int)opt & 0x3f;
781            result = img_LINE;
782            break;
783          case 0x40: {
784            char *q;
785            pimg->flags = (int)opt & 0x3f;
786            result = img_LABEL;
787            if (!fgets(pimg->label_buf, 257, pimg->fh)) {
788               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
789               return img_BAD;
790            }
791            q = pimg->label_buf + strlen(pimg->label_buf) - 1;
792            /* Ignore empty-labels in some .3d files (caused by a bug) */
793            if (q == pimg->label_buf) goto again;
794            if (*q != '\n') {
795               img_errno = IMG_BADFORMAT;
796               return img_BAD;
797            }
798            *q = '\0';
799            break;
800          }
801          case 0xc0:
802            /* use this for an extra leg or station flag if we need it */
803          default:
804            img_errno = IMG_BADFORMAT;
805            return img_BAD;
806         }
807         break;
808      }
809
810      if (!read_coord(pimg->fh, &pt)) return img_BAD;
811
812      if (result == img_LABEL && pimg->survey_len) {
813         if (strncmp(pimg->label_buf, pimg->survey, pimg->survey_len + 1) != 0)
814            goto again;
815         pimg->label += pimg->survey_len + 1;
816      }
817
818      done:
819      *p = pt;
820
821      if (result == img_MOVE && pimg->version == 1) {
822         /* peek at next code and see if it's an old-style label */
823         opt_lookahead = get32(pimg->fh);
824
825         if (feof(pimg->fh)) {
826            img_errno = IMG_BADFORMAT;
827            return img_BAD;
828         }
829         if (ferror(pimg->fh)) {
830            img_errno = IMG_READERROR;
831            return img_BAD;
832         }
833
834         if (opt_lookahead == 2) return img_read_item(pimg, p);
835      }
836
837      return result;
838   } else if (pimg->version == 0) {
839      ascii_again:
840      pimg->label[0] = '\0';
841      if (feof(pimg->fh)) return img_STOP;
842      if (pimg->pending) {
843         pimg->pending = 0;
844         result = img_LINE;
845      } else {
846         char cmd[7];
847         /* Stop if nothing found */
848         if (fscanf(pimg->fh, "%6s", cmd) < 1) return img_STOP;
849         if (strcmp(cmd, "move") == 0)
850            result = img_MOVE;
851         else if (strcmp(cmd, "draw") == 0)
852            result = img_LINE;
853         else if (strcmp(cmd, "line") == 0) {
854            /* set flag to indicate to process second triplet as LINE */
855            pimg->pending = 1;
856            result = img_MOVE;
857         } else if (strcmp(cmd, "cross") == 0) {
858            if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
859               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
860               return img_BAD;
861            }
862            goto ascii_again;
863         } else if (strcmp(cmd, "name") == 0) {
864            size_t off = 0;
865            int ch = getc(pimg->fh);
866            if (ch == ' ') ch = getc(pimg->fh);
867            while (ch != ' ') {
868               if (ch == '\n' || ch == EOF) {
869                  img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
870                  return img_BAD;
871               }
872               if (off == pimg->buf_len) {
873                  char *b;
874                  b = xosrealloc(pimg->label_buf, pimg->buf_len * 2);
875                  if (!b) {
876                     img_errno = IMG_OUTOFMEMORY;
877                     return img_BAD;
878                  }
879                  pimg->label_buf = b;
880                  pimg->buf_len *= 2;
881               }
882               pimg->label_buf[off++] = ch;
883               ch = getc(pimg->fh);
884            }
885            pimg->label_buf[off] = '\0';
886
887            pimg->label = pimg->label_buf;
888            if (pimg->label[0] == '\\') pimg->label++;
889
890            result = img_LABEL;
891         } else {
892            img_errno = IMG_BADFORMAT;
893            return img_BAD; /* unknown keyword */
894         }
895      }
896
897      if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
898         img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
899         return img_BAD;
900      }
901
902      if (result == img_LABEL && pimg->survey_len) {
903         if (strncmp(pimg->label, pimg->survey, pimg->survey_len + 1) != 0)
904            goto ascii_again;
905         pimg->label += pimg->survey_len + 1;
906      }
907
908      return result;
909   } else if (pimg->version == -1) {
910      /* version -1: .pos file */
911      size_t off;
912      pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
913      againpos:
914      off = 0;
915      while (fscanf(pimg->fh, "(%lf,%lf,%lf ) ", &p->x, &p->y, &p->z) != 3) {
916         int ch;
917         if (ferror(pimg->fh)) {
918            img_errno = IMG_READERROR;
919            return img_BAD;
920         }
921         if (feof(pimg->fh)) return img_STOP;
922         if (pimg->pending) {
923            img_errno = IMG_BADFORMAT;
924            return img_BAD;
925         }
926         pimg->pending = 1;
927         /* ignore rest of line */
928         do {
929            ch = getc(pimg->fh);
930         } while (ch != '\n' && ch != '\r' && ch != EOF);
931      }
932
933      pimg->label_buf[0] = '\0';
934      while (!feof(pimg->fh)) {
935         char *b;
936         if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
937            img_errno = IMG_READERROR;
938            return img_BAD;
939         }
940
941         off += strlen(pimg->label_buf + off);
942         if (off && pimg->label_buf[off - 1] == '\n') {
943            pimg->label_buf[off - 1] = '\0';
944            break;
945         }
946         b = xosrealloc(pimg->label_buf, pimg->buf_len * 2);
947         if (!b) {
948            img_errno = IMG_OUTOFMEMORY;
949            return img_BAD;
950         }
951         pimg->label_buf = b;
952         pimg->buf_len *= 2;
953      }
954
955      pimg->label = pimg->label_buf;
956
957      if (pimg->label[0] == '\\') pimg->label++;
958
959      if (pimg->survey_len) {
960         size_t l = pimg->survey_len + 1;
961         if (strncmp(pimg->survey, pimg->label, l) != 0) goto againpos;
962         pimg->label += l;
963      }
964
965      return img_LABEL;
966   } else {
967      /* version -2: Compass .plt file */
968      if (pimg->pending) {
969         /* pending MOVE or LINE or STOP */
970         int r = pimg->pending - 4;
971         pimg->pending = 0;
972         pimg->flags = 0;
973         pimg->label[pimg->label_len] = '\0';
974         return r;
975      }
976
977      while (1) {
978         char *line = getline_alloc(pimg->fh);
979         char *q;
980         size_t len = 0;
981
982         switch (line[0]) {
983            case 'M': case 'D':
984               /* Move or Draw */
985               if (sscanf(line + 1, "%lf %lf %lf", &p->x, &p->y, &p->z) != 3) {
986                  osfree(line);
987                  if (ferror(pimg->fh)) {
988                     img_errno = IMG_READERROR;
989                  } else {
990                     img_errno = IMG_BADFORMAT;
991                  }
992                  return img_BAD;
993               }
994               q = strchr(line, 'S');
995               if (!q) {
996                  osfree(line);
997                  img_errno = IMG_BADFORMAT;
998                  return img_BAD;
999               }
1000               ++q;
1001               len = 0;
1002               while (q[len] > ' ') ++len;
1003               q[len] = '\0';
1004               len += 2; /* '.' and '\0' */
1005               if (pimg->label_len + len > pimg->buf_len) {
1006                  char *b = xosrealloc(pimg->label_buf, pimg->label_len + len);
1007                  if (!b) {
1008                     img_errno = IMG_OUTOFMEMORY;
1009                     return img_BAD;
1010                  }
1011                  pimg->label_buf = b;
1012                  pimg->buf_len = pimg->label_len + len;
1013               }
1014               pimg->label = pimg->label_buf;
1015               pimg->label[pimg->label_len] = '.';
1016               memcpy(pimg->label + pimg->label_len + 1, q, len - 1);
1017               pimg->pending = (line[0] == 'M' ? img_MOVE : img_LINE) + 4;
1018               osfree(line);
1019               pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1020               return img_LABEL;
1021            case 'X': case 'F': case 'S':
1022               /* bounding boX (marks end of survey), Feature survey, or
1023                * new Section - skip to next survey */
1024               do {
1025                  do {
1026                     osfree(line);
1027                     line = getline_alloc(pimg->fh);
1028                     if (!line) {
1029                        img_errno = IMG_OUTOFMEMORY;
1030                        return img_BAD;
1031                     }
1032                     if (line[0] == '\x1a') {
1033                        osfree(line);
1034                        return img_STOP;
1035                     }
1036                     if (feof(pimg->fh)) {
1037                        osfree(line);
1038                        img_errno = IMG_BADFORMAT;
1039                        return img_BAD;
1040                     }
1041                  } while (line[0] != 'N');
1042
1043                  len = 1;
1044                  while (line[len] > 32) ++len;
1045               } while (pimg->survey && (pimg->survey_len != len - 1 ||
1046                        memcmp(line + 1, pimg->survey, pimg->survey_len) != 0));
1047
1048               if (len > pimg->buf_len) {
1049                  char *b = xosrealloc(pimg->label_buf, len);
1050                  if (!b) {
1051                     img_errno = IMG_OUTOFMEMORY;
1052                     return img_BAD;
1053                  }
1054                  pimg->label_buf = b;
1055                  pimg->buf_len = len;
1056               }
1057               --len;
1058               pimg->label_len = len;
1059               pimg->label = pimg->label_buf;
1060               memcpy(pimg->label, line + 1, len);
1061               pimg->label[len] = '\0';
1062
1063               osfree(line);
1064               break;
1065            default:
1066               osfree(line);
1067               img_errno = IMG_BADFORMAT;
1068               return img_BAD;
1069         }
1070      }
1071   }
1072}
1073
1074static int
1075write_v3label(img *pimg, int opt, const char *s)
1076{
1077   size_t len, n, dot;
1078
1079   /* find length of common prefix */
1080   dot = 0;
1081   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
1082      if (s[len] == '.') dot = len + 1;
1083   }
1084
1085   ASSERT(len <= pimg->label_len);
1086   n = pimg->label_len - len;
1087   if (len == 0) {
1088      if (pimg->label_len) putc(0, pimg->fh);
1089   } else if (n <= 16) {
1090      if (n) putc(n + 15, pimg->fh);
1091   } else if (dot == 0) {
1092      if (pimg->label_len) putc(0, pimg->fh);
1093      len = 0;
1094   } else {
1095      const char *p = pimg->label_buf + dot;
1096      n = 1;
1097      for (len = pimg->label_len - dot - 17; len; len--) {
1098         if (*p++ == '.') n++;
1099      }
1100      if (n <= 14) {
1101         putc(n, pimg->fh);
1102         len = dot;
1103      } else {
1104         if (pimg->label_len) putc(0, pimg->fh);
1105         len = 0;
1106      }
1107   }
1108
1109   n = strlen(s + len);
1110   putc(opt, pimg->fh);
1111   if (n < 0xfe) {
1112      putc(n, pimg->fh);
1113   } else if (n < 0xffff + 0xfe) {
1114      putc(0xfe, pimg->fh);
1115      put16(n - 0xfe, pimg->fh);
1116   } else {
1117      putc(0xff, pimg->fh);
1118      put32(n, pimg->fh);
1119   }
1120   fwrite(s + len, n, 1, pimg->fh);
1121
1122   n += len;
1123   pimg->label_len = n;
1124   if (n >= pimg->buf_len) {
1125      char *p = xosrealloc(pimg->label_buf, n + 1);
1126      if (!p) return 0; /* FIXME: distinguish out of memory... */
1127      pimg->label_buf = p;
1128      pimg->buf_len = n + 1;
1129   }
1130   memcpy(pimg->label_buf + len, s + len, n - len + 1);
1131
1132   return !ferror(pimg->fh);
1133}
1134
1135void
1136img_write_item(img *pimg, int code, int flags, const char *s,
1137               double x, double y, double z)
1138{
1139   if (!pimg) return;
1140   if (pimg->version == 3) {
1141      int opt = 0;
1142      switch (code) {
1143       case img_LABEL:
1144         write_v3label(pimg, 0x40 | flags, s);
1145         opt = 0;
1146         break;
1147       case img_MOVE:
1148         opt = 15;
1149         break;
1150       case img_LINE:
1151         write_v3label(pimg, 0x80 | flags, s ? s : "");
1152         opt = 0;
1153         break;
1154       default: /* ignore for now */
1155         return;
1156      }
1157      if (opt) putc(opt, pimg->fh);
1158      /* Output in cm */
1159      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1160      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1161      put32((INT32_T)my_round(z * 100.0), pimg->fh);
1162   } else {
1163      size_t len;
1164      INT32_T opt = 0;
1165      ASSERT(pimg->version > 0);
1166      switch (code) {
1167       case img_LABEL:
1168         if (pimg->version == 1) {
1169            /* put a move before each label */
1170            img_write_item(pimg, img_MOVE, 0, NULL, x, y, z);
1171            put32(2, pimg->fh);
1172            fputsnl(s, pimg->fh);
1173            return;
1174         }
1175         len = strlen(s);
1176         if (len > 255 || strchr(s, '\n')) {
1177            /* long label - not in early incarnations of v2 format, but few
1178             * 3d files will need these, so better not to force incompatibility
1179             * with a new version I think... */
1180            putc(7, pimg->fh);
1181            putc(flags, pimg->fh);
1182            put32(len, pimg->fh);
1183            fputs(s, pimg->fh);
1184         } else {
1185            putc(0x40 | (flags & 0x3f), pimg->fh);
1186            fputsnl(s, pimg->fh);
1187         }
1188         opt = 0;
1189         break;
1190       case img_MOVE:
1191         opt = 4;
1192         break;
1193       case img_LINE:
1194         if (pimg->version > 1) {
1195            opt = 0x80 | (flags & 0x3f);
1196            break;
1197         }
1198         opt = 5;
1199         break;
1200       default: /* ignore for now */
1201         return;
1202      }
1203      if (pimg->version == 1) {
1204         put32(opt, pimg->fh);
1205      } else {
1206         if (opt) putc(opt, pimg->fh);
1207      }
1208      /* Output in cm */
1209      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1210      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1211      put32((INT32_T)my_round(z * 100.0), pimg->fh);
1212   }
1213}
1214
1215int
1216img_close(img *pimg)
1217{
1218   int result = 1;
1219   if (pimg) {
1220      if (pimg->fh) {
1221         if (pimg->fRead) {
1222            osfree(pimg->survey);
1223            osfree(pimg->title);
1224            osfree(pimg->datestamp);
1225         } else {
1226            /* write end of data marker */
1227            switch (pimg->version) {
1228             case 1:
1229               put32((INT32_T)-1, pimg->fh);
1230               break;
1231             case 2:
1232               putc(0, pimg->fh);
1233               break;
1234             case 3:
1235               if (pimg->label_len) putc(0, pimg->fh);
1236               putc(0, pimg->fh);
1237               break;
1238            }
1239         }
1240         if (ferror(pimg->fh)) result = 0;
1241         if (fclose(pimg->fh)) result = 0;
1242         img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
1243      }
1244      osfree(pimg->label_buf);
1245      osfree(pimg);
1246   }
1247   return result;
1248}
Note: See TracBrowser for help on using the repository browser.