source: git/src/img.c @ f710a3f

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

Read survey names from .plt files too.

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

  • Property mode set to 100644
File size: 29.5 KB
RevLine 
[0da6e60]1/* img.c
[d1b1380]2 * Routines for reading and writing Survex ".3d" image files
[ad4eaf3f]3 * Copyright (C) 1993-2002 Olly Betts
[846746e]4 *
[89231c4]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.
[846746e]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
[89231c4]12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
[846746e]14 *
[89231c4]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
[d1b1380]18 */
19
[a420b49]20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
[d1b1380]24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27#include <time.h>
28#include <ctype.h>
29
[72af530]30#include "img.h"
[7d27e36]31
[72af530]32#ifdef IMG_HOSTED
[d69255f]33# include "debug.h"
[d1b1380]34# include "filelist.h"
[d69255f]35# include "filename.h"
36# include "message.h"
37# include "useful.h"
[a420b49]38# define TIMENA msg(/*Date and time not available.*/108)
39# define TIMEFMT msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107)
[d1b1380]40#else
[72af530]41# define INT32_T long
[d1b1380]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))
[72af530]46# define xosrealloc(L,S) realloc((L),(S))
[d1b1380]47# define osfree(P) free((P))
48# define ossizeof(T) sizeof(T)
[72af530]49/* in IMG_HOSTED mode, this tests if a filename refers to a directory */
[d1b1380]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 */
[23f7ea7]61# define fputsnl(S, FH) do {fputs((S), (FH)); putc('\n', (FH));} while(0)
[d69255f]62# define ASSERT(X)
[d1b1380]63
[a420b49]64static long
65get32(FILE *fh)
66{
[ad9a5cd]67   long w = getc(fh);
[a420b49]68   w |= (long)getc(fh) << 8l;
69   w |= (long)getc(fh) << 16l;
70   w |= (long)getc(fh) << 24l;
71   return w;
[d1b1380]72}
73
[a420b49]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);
[d1b1380]81}
[5c3c61a]82
83#endif
84
[dd1d6369]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
[ad4eaf3f]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
[d69255f]113unsigned int img_output_version = 3;
[5c3c61a]114
[72af530]115#ifdef IMG_HOSTED
[5c3c61a]116static enum {
117   IMG_NONE = 0,
[759fb47]118   IMG_FILENOTFOUND = /*Couldn't open data file `%s'*/24,
[5c3c61a]119   IMG_OUTOFMEMORY  = /*Out of memory %.0s*/38,
[759fb47]120   IMG_DIRECTORY    = /*Filename `%s' refers to directory*/44,
121   IMG_CANTOPENOUT  = /*Failed to open output file `%s'*/47,
[ad9a5cd]122   IMG_BADFORMAT    = /*Bad 3d image file `%s'*/106,
123   IMG_READERROR    = /*Error reading from file `%s'*/109,
[6c1c4f3]124   IMG_WRITEERROR   = /*Error writing to file `%s'*/110,
125   IMG_TOONEW       = /*File `%s' has a newer format than this program can understand*/114
[5c3c61a]126} img_errno = IMG_NONE;
127#else
128static img_errcode img_errno = IMG_NONE;
[c0563da]129#endif
[d1b1380]130
[7d27e36]131#define FILEID "Survex 3D Image File"
132
[ad4eaf3f]133#define EXT_PLT "plt"
134
[0da6e60]135/* Attempt to string paste to ensure we are passed a literal string */
136#define LITLEN(S) (sizeof(S"") - 1)
137
[7d27e36]138static char *
139my_strdup(const char *str)
[a420b49]140{
[7d27e36]141   char *p;
142   size_t len = strlen(str) + 1;
143   p = xosmalloc(len);
144   if (p) memcpy(p, str, len);
145   return p;
[d1b1380]146}
[c0563da]147
[a2ad284]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   }
[421b7d2]179   buf[i++] = '\0';
[a2ad284]180   return buf;
181}
182
[72af530]183#ifndef IMG_HOSTED
[a420b49]184img_errcode
185img_error(void)
186{
187   return img_errno;
[d1b1380]188}
189#else
[a420b49]190int
191img_error(void)
192{
193   return (int)img_errno;
[d1b1380]194}
195#endif
196
[a420b49]197img *
[a2ad284]198img_open_survey(const char *fnm, const char *survey)
[a420b49]199{
200   img *pimg;
[0da6e60]201   size_t len;
[7d27e36]202   char buf[LITLEN(FILEID) + 1];
203   int ch;
[a420b49]204
205   if (fDirectory(fnm)) {
206      img_errno = IMG_DIRECTORY;
207      return NULL;
208   }
[d1b1380]209
[a420b49]210   pimg = (img *)xosmalloc(ossizeof(img));
211   if (pimg == NULL) {
212      img_errno = IMG_OUTOFMEMORY;
213      return NULL;
214   }
[d1b1380]215
[23f7ea7]216   pimg->buf_len = 257;
[0ff23cb]217   pimg->label_buf = xosmalloc(pimg->buf_len);
218   if (!pimg->label_buf) {
[23f7ea7]219      osfree(pimg);
220      img_errno = IMG_OUTOFMEMORY;
221      return NULL;
222   }
223
[a420b49]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   }
[d1b1380]230
[0da6e60]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
[58f02ae]242   pimg->title = NULL;
[0da6e60]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;
[7d27e36]255            pimg->title = my_strdup(p);
256            if (!pimg->title) {
257               img_errno = IMG_OUTOFMEMORY;
258               goto error;
259            }
[0da6e60]260            pimg->survey[len] = '.';
261            pimg->survey[len + 1] = '\0';
262         }
263      }
264      pimg->survey_len = len;
265   }
266
[ad4eaf3f]267   /* [version -2] pending IMG_LINE ('D') or IMG_MOVE ('M')
268    * [version -1] already skipped heading line, or there wasn't one
[0da6e60]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 &&
[ad4eaf3f]277       my_strcasecmp(fnm + len - LITLEN(EXT_SVX_POS), EXT_SVX_POS) == 0) {
[0da6e60]278      pimg->version = -1;
[58f02ae]279      if (!survey) pimg->title = baseleaf_from_fnm(fnm);
[7d27e36]280      pimg->datestamp = my_strdup(TIMENA);
281      if (!pimg->datestamp) {
282         img_errno = IMG_OUTOFMEMORY;
283         goto error;
284      }
[58f02ae]285      pimg->start = 0;
[0da6e60]286      return pimg;
287   }
[ad4eaf3f]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;
[f710a3f]293
[ad4eaf3f]294      pimg->version = -2;
295      if (!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         osfree(line);
303         line = getline_alloc(pimg->fh);
304         if (!line || line[0] == '\x1a' || feof(pimg->fh)) {
305            if (line) {
306               osfree(line);
307               img_errno = IMG_BADFORMAT;
308            } else {
309               img_errno = IMG_OUTOFMEMORY;
310            }
311            osfree(pimg->datestamp);
312            goto error;
313         }
314      } while (line[0] != 'N');
[f710a3f]315
316      len = 1;
317      while (line[len] > 32) ++len;
318
319      if (len > pimg->buf_len) {
320         char *b = xosrealloc(pimg->label_buf, len);
321         if (!b) {
322            img_errno = IMG_OUTOFMEMORY;
323            return NULL;
324         }
325         pimg->label_buf = b;
326         pimg->buf_len = len;
327      }
328      pimg->label_len = len;
329      pimg->label = pimg->label_buf;
330      memcpy(pimg->label, line + 1, len);
331      pimg->label[len] = '\0';
[ad4eaf3f]332      osfree(line);
333      pimg->start = 0;
334      return pimg;
335   }
[0da6e60]336
[7d27e36]337   if (fread(buf, LITLEN(FILEID) + 1, 1, pimg->fh) != 1 ||
338       memcmp(buf, FILEID"\n", LITLEN(FILEID)) != 0) {
[a420b49]339      img_errno = IMG_BADFORMAT;
[58f02ae]340      goto error;
[a420b49]341   }
[d1b1380]342
[7d27e36]343   /* check file format version */
344   ch = getc(pimg->fh);
345   pimg->version = 0;
346   if (tolower(ch) == 'b') {
347      /* binary file iff B/b prefix */
348      pimg->version = 1;
349      ch = getc(pimg->fh);
350   }
351   if (ch != 'v') {
352      img_errno = IMG_BADFORMAT;
353      goto error;
354   }
355   ch = getc(pimg->fh);
356   if (ch == '0') {
357      if (fread(buf, 4, 1, pimg->fh) != 1 || memcmp(buf, ".01\n", 4) != 0) {
358         img_errno = IMG_BADFORMAT;
359         goto error;
360      }
[0da6e60]361      /* nothing special to do */
[7d27e36]362   } else if (pimg->version == 0) {
363      if (ch < '2' || ch > '3' || getc(pimg->fh) != '\n') {
[6c1c4f3]364         img_errno = IMG_TOONEW;
[58f02ae]365         goto error;
[6c1c4f3]366      }
[7d27e36]367      pimg->version = ch - '0';
[5c3c61a]368   } else {
[647407d]369      img_errno = IMG_BADFORMAT;
[58f02ae]370      goto error;
[a420b49]371   }
[421b7d2]372
[4d3dfdf]373   if (!pimg->title)
374       pimg->title = getline_alloc(pimg->fh);
375   else
376       osfree(getline_alloc(pimg->fh));
[a2ad284]377   pimg->datestamp = getline_alloc(pimg->fh);
378   if (!pimg->title || !pimg->datestamp) {
[58f02ae]379      img_errno = IMG_OUTOFMEMORY;
380      error:
[a2ad284]381      osfree(pimg->title);
382      fclose(pimg->fh);
383      osfree(pimg);
384      return NULL;
385   }
386
[58f02ae]387   pimg->start = ftell(pimg->fh);
[421b7d2]388
[a420b49]389   return pimg;
[d1b1380]390}
391
[f2588ca]392void
393img_rewind(img *pimg)
394{
395   fseek(pimg->fh, pimg->start, SEEK_SET);
[58f02ae]396   clearerr(pimg->fh);
397   /* [version -1] already skipped heading line, or there wasn't one
398    * [version 0] not in the middle of a 'LINE' command
[76bbb7c9]399    * [version 3] not in the middle of turning a LINE into a MOVE */
400   pimg->pending = 0;
[d69255f]401
402   img_errno = IMG_NONE;
[99cf51a]403
[5d7280f8]404   /* for version 3 we use label_buf to store the prefix for reuse */
[d69255f]405   pimg->label_len = 0;
[f2588ca]406}
407
[a420b49]408img *
[fe8e80e]409img_open_write(const char *fnm, char *title_buf, bool fBinary)
[a420b49]410{
411   time_t tm;
412   img *pimg;
[d1b1380]413
[d69255f]414   fBinary = fBinary;
415
[a420b49]416   if (fDirectory(fnm)) {
417      img_errno = IMG_DIRECTORY;
418      return NULL;
419   }
[d1b1380]420
[a420b49]421   pimg = (img *)xosmalloc(ossizeof(img));
422   if (pimg == NULL) {
423      img_errno = IMG_OUTOFMEMORY;
424      return NULL;
425   }
[d1b1380]426
[fe8e80e]427   pimg->buf_len = 257;
[0ff23cb]428   pimg->label_buf = xosmalloc(pimg->buf_len);
429   if (!pimg->label_buf) {
[fe8e80e]430      osfree(pimg);
431      img_errno = IMG_OUTOFMEMORY;
432      return NULL;
433   }
434
[a420b49]435   pimg->fh = fopen(fnm, "wb");
436   if (!pimg->fh) {
437      osfree(pimg);
438      img_errno = IMG_CANTOPENOUT;
439      return NULL;
440   }
[d1b1380]441
[a420b49]442   /* Output image file header */
443   fputs("Survex 3D Image File\n", pimg->fh); /* file identifier string */
[5c3c61a]444   if (img_output_version < 2) {
[d69255f]445      pimg->version = 1;
446      fputs("Bv0.01\n", pimg->fh); /* binary file format version number */
[5c3c61a]447   } else {
[d69255f]448      pimg->version = (img_output_version > 2) ? 3 : 2;
[5c3c61a]449      fprintf(pimg->fh, "v%d\n", pimg->version); /* file format version no. */
450   }
[fe8e80e]451   fputsnl(title_buf, pimg->fh);
[a420b49]452   tm = time(NULL);
453   if (tm == (time_t)-1) {
454      fputsnl(TIMENA, pimg->fh);
455   } else {
[7d27e36]456      char date[256];
[a420b49]457      /* output current date and time in format specified */
[7d27e36]458      strftime(date, 256, TIMEFMT, localtime(&tm));
459      fputsnl(date, pimg->fh);
[a420b49]460   }
461   pimg->fRead = fFalse; /* writing to this file */
462   img_errno = IMG_NONE;
[d69255f]463
[5d7280f8]464   /* for version 3 we use label_buf to store the prefix for reuse */
[0ff23cb]465   pimg->label_buf[0] = '\0';
[d69255f]466   pimg->label_len = 0;
467
[ad9a5cd]468   /* Don't check for write errors now - let img_close() report them... */
[a420b49]469   return pimg;
[d1b1380]470}
471
[ad9a5cd]472static int
473read_coord(FILE *fh, img_point *pt)
474{
475   ASSERT(fh);
476   ASSERT(pt);
477   pt->x = get32(fh) / 100.0;
478   pt->y = get32(fh) / 100.0;
479   pt->z = get32(fh) / 100.0;
480   if (ferror(fh) || feof(fh)) {
481      img_errno = feof(fh) ? IMG_BADFORMAT : IMG_READERROR;
482      return 0;
[421b7d2]483   }
[ad9a5cd]484   return 1;
485}
486
[4d3dfdf]487static int
488skip_coord(FILE *fh)
489{
490    return (fseek(fh, 12, SEEK_CUR) == 0);
491}
492
[a420b49]493int
[23f7ea7]494img_read_item(img *pimg, img_point *p)
[a420b49]495{
496   int result;
[95c3272]497   pimg->flags = 0;
[d69255f]498
499   if (pimg->version == 3) {
500      int opt;
[76bbb7c9]501      if (pimg->pending >= 0x80) {
502         *p = pimg->mv;
503         pimg->flags = (int)(pimg->pending) & 0x3f;
504         pimg->pending = 0;
[421b7d2]505         return img_LINE;
[76bbb7c9]506      }
[d69255f]507      again3: /* label to goto if we get a prefix */
[4d3dfdf]508      pimg->label = pimg->label_buf;
[d69255f]509      opt = getc(pimg->fh);
[ad9a5cd]510      if (opt == EOF) {
511         img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
512         return img_BAD;
513      }
[d69255f]514      switch (opt >> 6) {
515       case 0:
516         if (opt == 0) {
517            if (!pimg->label_len) return img_STOP; /* end of data marker */
518            pimg->label_len = 0;
519            goto again3;
520         }
521         if (opt < 15) {
522            /* 1-14 mean trim that many levels from current prefix */
523            int c;
524            if (pimg->label_len <= 16) {
[4d3dfdf]525               /* zero prefix using "0" */
[d69255f]526               img_errno = IMG_BADFORMAT;
527               return img_BAD;
528            }
[4b156b3]529            c = pimg->label_len - 16 - 1;
[0ff23cb]530            while (pimg->label_buf[c] != '.' || --opt > 0) {
[d69255f]531               if (--c < 0) {
532                  /* zero prefix using "0" */
533                  img_errno = IMG_BADFORMAT;
534                  return img_BAD;
535               }
536            }
537            c++;
538            pimg->label_len = c;
539            goto again3;
540         }
541         if (opt == 15) {
542            result = img_MOVE;
543            break;
544         }
545         /* 16-31 mean remove (n - 15) characters from the prefix */
546         /* zero prefix using 0 */
[ad4eaf3f]547         if (pimg->label_len <= (size_t)(opt - 15)) {
[d69255f]548            img_errno = IMG_BADFORMAT;
549            return img_BAD;
550         }
[d245d71]551         pimg->label_len -= (opt - 15);
[d69255f]552         goto again3;
553       case 1: case 2: {
554         char *q;
[ad9a5cd]555         long len = getc(pimg->fh);
556         if (len == EOF) {
557            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
558            return img_BAD;
559         }
[d69255f]560         if (len == 0xfe) {
561            len += get16(pimg->fh);
[ad9a5cd]562            if (feof(pimg->fh)) {
563               img_errno = IMG_BADFORMAT;
564               return img_BAD;
565            }
566            if (ferror(pimg->fh)) {
567               img_errno = IMG_READERROR;
568               return img_BAD;
569            }
[d69255f]570         } else if (len == 0xff) {
571            len = get32(pimg->fh);
[ad9a5cd]572            if (ferror(pimg->fh)) {
573               img_errno = IMG_READERROR;
574               return img_BAD;
575            }
576            if (feof(pimg->fh) || len < 0xfe + 0xffff) {
[d69255f]577               img_errno = IMG_BADFORMAT;
578               return img_BAD;
579            }
580         }
581
[0ff23cb]582         q = pimg->label_buf + pimg->label_len;
[d69255f]583         pimg->label_len += len;
584         if (pimg->label_len >= pimg->buf_len) {
[58f02ae]585            char *b = xosrealloc(pimg->label_buf, pimg->label_len + 1);
586            if (!b) {
[d69255f]587               img_errno = IMG_OUTOFMEMORY;
588               return img_BAD;
589            }
[58f02ae]590            q = (q - pimg->label_buf) + b;
591            pimg->label = pimg->label_buf = b;
[d69255f]592            pimg->buf_len = pimg->label_len + 1;
593         }
594         if (len && fread(q, len, 1, pimg->fh) != 1) {
[ad9a5cd]595            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]596            return img_BAD;
597         }
598         q[len] = '\0';
[76bbb7c9]599
600         result = opt & 0x40 ? img_LABEL : img_LINE;
601
602         if (pimg->survey_len) {
603            size_t l = pimg->survey_len;
[0ff23cb]604            const char *s = pimg->label_buf;
[76bbb7c9]605            if (result == img_LINE) {
606               if (strncmp(pimg->survey, s, l) != 0 ||
607                   !(s[l] == '.' || s[l] == '\0')) {
[421b7d2]608                  if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
[76bbb7c9]609                  pimg->pending = 15;
[421b7d2]610                  goto again3;
[76bbb7c9]611               }
612            } else {
613               if (strncmp(pimg->survey, s, l + 1) != 0) {
[4d3dfdf]614                  if (!skip_coord(pimg->fh)) return img_BAD;
615                  pimg->pending = 0;
[76bbb7c9]616                  goto again3;
617               }
618            }
[0ff23cb]619            pimg->label += l;
620            /* skip the dot if there */
621            if (*pimg->label) pimg->label++;
[76bbb7c9]622         }
623
624         if (result == img_LINE && pimg->pending) {
625            *p = pimg->mv;
[ad9a5cd]626            if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
[76bbb7c9]627            pimg->pending = opt;
628            return img_MOVE;
629         }
630         pimg->flags = (int)opt & 0x3f;
[d69255f]631         break;
632       }
633       default:
634         img_errno = IMG_BADFORMAT;
635         return img_BAD;
636      }
[ad9a5cd]637      if (!read_coord(pimg->fh, p)) return img_BAD;
[76bbb7c9]638      pimg->pending = 0;
[d69255f]639      return result;
[4d3dfdf]640   }
641
642   pimg->label = pimg->label_buf;
643
644   if (pimg->version > 0) {
[d69255f]645      static long opt_lookahead = 0;
[ad9a5cd]646      static img_point pt = { 0.0, 0.0, 0.0 };
[a420b49]647      long opt;
648      again: /* label to goto if we get a cross */
[5d7280f8]649      pimg->label[0] = '\0';
[ad9a5cd]650
[5c3c61a]651      if (pimg->version == 1) {
[c4d4649]652         if (opt_lookahead) {
653            opt = opt_lookahead;
654            opt_lookahead = 0;
655         } else {
656            opt = get32(pimg->fh);
657         }
[5c3c61a]658      } else {
[421b7d2]659         opt = getc(pimg->fh);
[5c3c61a]660      }
[ad9a5cd]661
662      if (feof(pimg->fh)) {
663         img_errno = IMG_BADFORMAT;
664         return img_BAD;
665      }
666      if (ferror(pimg->fh)) {
667         img_errno = IMG_READERROR;
668         return img_BAD;
669      }
670
[a420b49]671      switch (opt) {
[437caf3]672       case -1: case 0:
[a420b49]673         return img_STOP; /* end of data marker */
674       case 1:
[ad9a5cd]675         /* skip coordinates */
[4d3dfdf]676         if (!skip_coord(pimg->fh)) {
[ad9a5cd]677            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
678            return img_BAD;
679         }
[a420b49]680         goto again;
[23f7ea7]681       case 2: case 3: {
[fe8e80e]682         char *q;
[3a1f8da]683         int ch;
[437caf3]684         result = img_LABEL;
[3a1f8da]685         ch = getc(pimg->fh);
[d69255f]686         if (ch == EOF) {
[ad9a5cd]687            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]688            return img_BAD;
689         }
[3a1f8da]690         if (ch != '\\') ungetc(ch, pimg->fh);
[0ff23cb]691         fgets(pimg->label_buf, 257, pimg->fh);
[421b7d2]692         if (feof(pimg->fh)) {
[ad9a5cd]693            img_errno = IMG_BADFORMAT;
694            return img_BAD;
695         }
[421b7d2]696         if (ferror(pimg->fh)) {
[ad9a5cd]697            img_errno = IMG_READERROR;
698            return img_BAD;
699         }
[0ff23cb]700         q = pimg->label_buf + strlen(pimg->label_buf) - 1;
[d69255f]701         if (*q != '\n') {
702            img_errno = IMG_BADFORMAT;
703            return img_BAD;
704         }
[9968ebf]705         /* Ignore empty labels in some .3d files (caused by a bug) */
[0ff23cb]706         if (q == pimg->label_buf) goto again;
[fe8e80e]707         *q = '\0';
[509e099]708         pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
[437caf3]709         if (opt == 2) goto done;
710         break;
[23f7ea7]711       }
[95c3272]712       case 6: case 7: {
[ad9a5cd]713         long len;
[23f7ea7]714         result = img_LABEL;
[ad9a5cd]715
[509e099]716         if (opt == 7)
717            pimg->flags = getc(pimg->fh);
718         else
719            pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
[ad9a5cd]720
[23f7ea7]721         len = get32(pimg->fh);
[ad9a5cd]722
723         if (feof(pimg->fh)) {
724            img_errno = IMG_BADFORMAT;
725            return img_BAD;
726         }
727         if (ferror(pimg->fh)) {
728            img_errno = IMG_READERROR;
729            return img_BAD;
730         }
731
[9968ebf]732         /* Ignore empty labels in some .3d files (caused by a bug) */
733         if (len == 0) goto again;
[dd1d6369]734         if (len >= (long)pimg->buf_len) {
[9b56f4d]735            char *b = xosrealloc(pimg->label_buf, len + 1);
736            if (!b) {
[23f7ea7]737               img_errno = IMG_OUTOFMEMORY;
738               return img_BAD;
739            }
[9b56f4d]740            pimg->label = pimg->label_buf = b;
[23f7ea7]741            pimg->buf_len = len + 1;
742         }
[0ff23cb]743         if (fread(pimg->label_buf, len, 1, pimg->fh) != 1) {
[ad9a5cd]744            img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]745            return img_BAD;
746         }
[23f7ea7]747         break;
748       }
[a420b49]749       case 4:
750         result = img_MOVE;
751         break;
752       case 5:
753         result = img_LINE;
754         break;
755       default:
[95c3272]756         switch ((int)opt & 0xc0) {
757          case 0x80:
758            pimg->flags = (int)opt & 0x3f;
759            result = img_LINE;
760            break;
761          case 0x40: {
762            char *q;
763            pimg->flags = (int)opt & 0x3f;
764            result = img_LABEL;
[0ff23cb]765            if (!fgets(pimg->label_buf, 257, pimg->fh)) {
[ad9a5cd]766               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[d69255f]767               return img_BAD;
768            }
[0ff23cb]769            q = pimg->label_buf + strlen(pimg->label_buf) - 1;
[9968ebf]770            /* Ignore empty-labels in some .3d files (caused by a bug) */
[0ff23cb]771            if (q == pimg->label_buf) goto again;
[d69255f]772            if (*q != '\n') {
773               img_errno = IMG_BADFORMAT;
774               return img_BAD;
775            }
[95c3272]776            *q = '\0';
777            break;
778          }
779          case 0xc0:
780            /* use this for an extra leg or station flag if we need it */
781          default:
[d69255f]782            img_errno = IMG_BADFORMAT;
[95c3272]783            return img_BAD;
784         }
[437caf3]785         break;
[a420b49]786      }
[ad9a5cd]787
788      if (!read_coord(pimg->fh, &pt)) return img_BAD;
[76bbb7c9]789
[ed0f5b6]790      if (result == img_LABEL && pimg->survey_len) {
791         if (strncmp(pimg->label_buf, pimg->survey, pimg->survey_len + 1) != 0)
[0ff23cb]792            goto again;
793         pimg->label += pimg->survey_len + 1;
794      }
[76bbb7c9]795
[a420b49]796      done:
[ad9a5cd]797      *p = pt;
[c4d4649]798
799      if (result == img_MOVE && pimg->version == 1) {
[d69255f]800         /* peek at next code and see if it's an old-style label */
[23f7ea7]801         opt_lookahead = get32(pimg->fh);
[ad9a5cd]802
803         if (feof(pimg->fh)) {
804            img_errno = IMG_BADFORMAT;
805            return img_BAD;
806         }
807         if (ferror(pimg->fh)) {
808            img_errno = IMG_READERROR;
809            return img_BAD;
810         }
811
[d69255f]812         if (opt_lookahead == 2) return img_read_item(pimg, p);
[c4d4649]813      }
[23f7ea7]814
[a420b49]815      return result;
[0da6e60]816   } else if (pimg->version == 0) {
[a420b49]817      ascii_again:
[5d7280f8]818      pimg->label[0] = '\0';
[a420b49]819      if (feof(pimg->fh)) return img_STOP;
[76bbb7c9]820      if (pimg->pending) {
821         pimg->pending = 0;
[a420b49]822         result = img_LINE;
823      } else {
[7d27e36]824         char cmd[7];
[a420b49]825         /* Stop if nothing found */
[7d27e36]826         if (fscanf(pimg->fh, "%6s", cmd) < 1) return img_STOP;
827         if (strcmp(cmd, "move") == 0)
[a420b49]828            result = img_MOVE;
[7d27e36]829         else if (strcmp(cmd, "draw") == 0)
[a420b49]830            result = img_LINE;
[7d27e36]831         else if (strcmp(cmd, "line") == 0) {
[bd1913f]832            /* set flag to indicate to process second triplet as LINE */
[76bbb7c9]833            pimg->pending = 1;
[a420b49]834            result = img_MOVE;
[7d27e36]835         } else if (strcmp(cmd, "cross") == 0) {
[d69255f]836            if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
[ad9a5cd]837               img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
[0ed0e16]838               return img_BAD;
[d69255f]839            }
[a420b49]840            goto ascii_again;
[7d27e36]841         } else if (strcmp(cmd, "name") == 0) {
[0da6e60]842            size_t off = 0;
[58f02ae]843            int ch = getc(pimg->fh);
844            if (ch == ' ') ch = getc(pimg->fh);
845            while (ch != ' ') {
846               if (ch == '\n' || ch == EOF) {
847                  img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
[ad9a5cd]848                  return img_BAD;
849               }
[58f02ae]850               if (off == pimg->buf_len) {
[9b56f4d]851                  char *b;
852                  b = xosrealloc(pimg->label_buf, pimg->buf_len * 2);
853                  if (!b) {
854                     img_errno = IMG_OUTOFMEMORY;
855                     return img_BAD;
856                  }
857                  pimg->label_buf = b;
858                  pimg->buf_len *= 2;
[0da6e60]859               }
[58f02ae]860               pimg->label_buf[off++] = ch;
861               ch = getc(pimg->fh);
[ad9a5cd]862            }
[58f02ae]863            pimg->label_buf[off] = '\0';
864
865            pimg->label = pimg->label_buf;
866            if (pimg->label[0] == '\\') pimg->label++;
[0da6e60]867
[a420b49]868            result = img_LABEL;
[d69255f]869         } else {
870            img_errno = IMG_BADFORMAT;
[a420b49]871            return img_BAD; /* unknown keyword */
[d69255f]872         }
[a420b49]873      }
[d1b1380]874
[d69255f]875      if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
[ad9a5cd]876         img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
[fe8e80e]877         return img_BAD;
[d69255f]878      }
879
[58f02ae]880      if (result == img_LABEL && pimg->survey_len) {
881         if (strncmp(pimg->label, pimg->survey, pimg->survey_len + 1) != 0)
[0ff23cb]882            goto ascii_again;
883         pimg->label += pimg->survey_len + 1;
884      }
[76bbb7c9]885
[a420b49]886      return result;
[ad4eaf3f]887   } else if (pimg->version == -1) {
[0da6e60]888      /* version -1: .pos file */
[58f02ae]889      size_t off;
[5eea574]890      pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
[58f02ae]891      againpos:
892      off = 0;
[0da6e60]893      while (fscanf(pimg->fh, "(%lf,%lf,%lf ) ", &p->x, &p->y, &p->z) != 3) {
894         int ch;
895         if (ferror(pimg->fh)) {
896            img_errno = IMG_READERROR;
897            return img_BAD;
898         }
899         if (feof(pimg->fh)) return img_STOP;
900         if (pimg->pending) {
901            img_errno = IMG_BADFORMAT;
902            return img_BAD;
903         }
904         pimg->pending = 1;
905         /* ignore rest of line */
906         do {
907            ch = getc(pimg->fh);
908         } while (ch != '\n' && ch != '\r' && ch != EOF);
909      }
910
911      pimg->label_buf[0] = '\0';
912      while (!feof(pimg->fh)) {
[9b56f4d]913         char *b;
[0da6e60]914         if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
915            img_errno = IMG_READERROR;
916            return img_BAD;
917         }
918
919         off += strlen(pimg->label_buf + off);
920         if (off && pimg->label_buf[off - 1] == '\n') {
921            pimg->label_buf[off - 1] = '\0';
922            break;
923         }
[9b56f4d]924         b = xosrealloc(pimg->label_buf, pimg->buf_len * 2);
925         if (!b) {
926            img_errno = IMG_OUTOFMEMORY;
927            return img_BAD;
928         }
929         pimg->label_buf = b;
930         pimg->buf_len *= 2;
[0da6e60]931      }
932
[58f02ae]933      pimg->label = pimg->label_buf;
934
[421b7d2]935      if (pimg->label[0] == '\\') pimg->label++;
[58f02ae]936
937      if (pimg->survey_len) {
938         size_t l = pimg->survey_len + 1;
939         if (strncmp(pimg->survey, pimg->label, l) != 0) goto againpos;
940         pimg->label += l;
941      }
942
[0da6e60]943      return img_LABEL;
[ad4eaf3f]944   } else {
945      /* version -2: Compass .plt file */
946      /* FIXME: pimg->survey is ignored - we could compare it to the survey
947       * short-name after the 'N'...
948       */
949      if (pimg->pending) {
950         int r = pimg->pending == 'M' ? img_MOVE : img_LINE;
951         pimg->pending = 0;
952         pimg->flags = 0;
[f710a3f]953         pimg->label[pimg->label_len] = '\0';
[ad4eaf3f]954         return r;
955      }
956
957      while (1) {
958         char *line = getline_alloc(pimg->fh);
959         char *q;
960         size_t len = 0;
961
962         switch (line[0]) {
963            case 'M': case 'D':
964               /* Move or Draw */
965               if (sscanf(line + 1, "%lf %lf %lf", &p->x, &p->y, &p->z) != 3) {
966                  osfree(line);
967                  if (ferror(pimg->fh)) {
968                     img_errno = IMG_READERROR;
969                  } else {
970                     img_errno = IMG_BADFORMAT;
971                  }
972                  return img_BAD;
973               }
974               q = strchr(line, 'S');
975               if (!q) {
976                  osfree(line);
977                  img_errno = IMG_BADFORMAT;
978                  return img_BAD;
979               }
980               ++q;
981               len = 0;
982               while (q[len] > ' ') ++len;
983               q[len] = '\0';
[f710a3f]984               len += 2; /* '.' and '\0' */
985               if (pimg->label_len + len > pimg->buf_len) {
986                  char *b = xosrealloc(pimg->label_buf, pimg->label_len + len);
[ad4eaf3f]987                  if (!b) {
988                     img_errno = IMG_OUTOFMEMORY;
989                     return img_BAD;
990                  }
991                  pimg->label_buf = b;
[f710a3f]992                  pimg->buf_len = pimg->label_len + len;
[ad4eaf3f]993               }
994               pimg->label = pimg->label_buf;
[f710a3f]995               pimg->label[pimg->label_len] = '.';
996               memcpy(pimg->label + pimg->label_len + 1, q, len - 1);
[ad4eaf3f]997               pimg->pending = line[0];
998               osfree(line);
999               pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
1000               return img_LABEL;
1001            case 'X': case 'F': case 'S':
1002               /* bounding boX (marks end of survey), Feature survey, or
1003                * new Section - skip to next survey */
1004               do {
1005                  osfree(line);
1006                  line = getline_alloc(pimg->fh);
1007                  if (!line) {
1008                     img_errno = IMG_OUTOFMEMORY;
1009                     return img_BAD;
1010                  }
1011                  if (line[0] == '\x1a') {
1012                     osfree(line);
1013                     return img_STOP;
1014                  }
1015                  if (feof(pimg->fh)) {
1016                     osfree(line);
1017                     img_errno = IMG_BADFORMAT;
1018                     return img_BAD;
1019                  }
1020               } while (line[0] != 'N');
[f710a3f]1021
1022               len = 1;
1023               while (line[len] > 32) ++len;
1024
1025               if (len > pimg->buf_len) {
1026                  char *b = xosrealloc(pimg->label_buf, len);
1027                  if (!b) {
1028                     img_errno = IMG_OUTOFMEMORY;
1029                     return img_BAD;
1030                  }
1031                  pimg->label_buf = b;
1032                  pimg->buf_len = len;
1033               }
1034               --len;
1035               pimg->label_len = len;
1036               pimg->label = pimg->label_buf;
1037               memcpy(pimg->label, line + 1, len);
1038               pimg->label[len] = '\0';
1039
[ad4eaf3f]1040               osfree(line);
1041               break;
1042            default:
1043               osfree(line);
1044               img_errno = IMG_BADFORMAT;
1045               return img_BAD;
1046         }
1047      }
[a420b49]1048   }
[d1b1380]1049}
1050
[ad9a5cd]1051static int
[d69255f]1052write_v3label(img *pimg, int opt, const char *s)
1053{
1054   size_t len, n, dot;
1055
1056   /* find length of common prefix */
1057   dot = 0;
[0ff23cb]1058   for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
[d69255f]1059      if (s[len] == '.') dot = len + 1;
1060   }
1061
1062   ASSERT(len <= pimg->label_len);
1063   n = pimg->label_len - len;
1064   if (len == 0) {
1065      if (pimg->label_len) putc(0, pimg->fh);
1066   } else if (n <= 16) {
1067      if (n) putc(n + 15, pimg->fh);
1068   } else if (dot == 0) {
1069      if (pimg->label_len) putc(0, pimg->fh);
[99cf51a]1070      len = 0;
[d69255f]1071   } else {
[0ff23cb]1072      const char *p = pimg->label_buf + dot;
[d69255f]1073      n = 1;
1074      for (len = pimg->label_len - dot - 17; len; len--) {
1075         if (*p++ == '.') n++;
1076      }
1077      if (n <= 14) {
1078         putc(n, pimg->fh);
1079         len = dot;
1080      } else {
1081         if (pimg->label_len) putc(0, pimg->fh);
1082         len = 0;
1083      }
1084   }
1085
1086   n = strlen(s + len);
1087   putc(opt, pimg->fh);
1088   if (n < 0xfe) {
1089      putc(n, pimg->fh);
1090   } else if (n < 0xffff + 0xfe) {
1091      putc(0xfe, pimg->fh);
1092      put16(n - 0xfe, pimg->fh);
1093   } else {
1094      putc(0xff, pimg->fh);
1095      put32(n, pimg->fh);
1096   }
1097   fwrite(s + len, n, 1, pimg->fh);
1098
1099   n += len;
1100   pimg->label_len = n;
1101   if (n >= pimg->buf_len) {
[0ff23cb]1102      char *p = xosrealloc(pimg->label_buf, n + 1);
[ad9a5cd]1103      if (!p) return 0; /* FIXME: distinguish out of memory... */
1104      pimg->label_buf = p;
1105      pimg->buf_len = n + 1;
[d69255f]1106   }
[0ff23cb]1107   memcpy(pimg->label_buf + len, s + len, n - len + 1);
[ad9a5cd]1108
1109   return !ferror(pimg->fh);
[d69255f]1110}
1111
[a420b49]1112void
[95c3272]1113img_write_item(img *pimg, int code, int flags, const char *s,
[a9f5117]1114               double x, double y, double z)
[a420b49]1115{
[647407d]1116   if (!pimg) return;
[d69255f]1117   if (pimg->version == 3) {
1118      int opt = 0;
1119      switch (code) {
1120       case img_LABEL:
1121         write_v3label(pimg, 0x40 | flags, s);
1122         opt = 0;
1123         break;
1124       case img_MOVE:
1125         opt = 15;
1126         break;
1127       case img_LINE:
1128         write_v3label(pimg, 0x80 | flags, s ? s : "");
1129         opt = 0;
1130         break;
1131       default: /* ignore for now */
1132         return;
1133      }
1134      if (opt) putc(opt, pimg->fh);
1135      /* Output in cm */
[dd1d6369]1136      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1137      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1138      put32((INT32_T)my_round(z * 100.0), pimg->fh);
[d69255f]1139   } else {
[23f7ea7]1140      size_t len;
[badeec8]1141      INT32_T opt = 0;
[d69255f]1142      ASSERT(pimg->version > 0);
[a420b49]1143      switch (code) {
[437caf3]1144       case img_LABEL:
1145         if (pimg->version == 1) {
1146            /* put a move before each label */
[95c3272]1147            img_write_item(pimg, img_MOVE, 0, NULL, x, y, z);
[437caf3]1148            put32(2, pimg->fh);
[0ed0e16]1149            fputsnl(s, pimg->fh);
[437caf3]1150            return;
1151         }
[23f7ea7]1152         len = strlen(s);
[af56069]1153         if (len > 255 || strchr(s, '\n')) {
[23f7ea7]1154            /* long label - not in early incarnations of v2 format, but few
1155             * 3d files will need these, so better not to force incompatibility
1156             * with a new version I think... */
[95c3272]1157            putc(7, pimg->fh);
1158            putc(flags, pimg->fh);
[23f7ea7]1159            put32(len, pimg->fh);
1160            fputs(s, pimg->fh);
1161         } else {
[95c3272]1162            putc(0x40 | (flags & 0x3f), pimg->fh);
[23f7ea7]1163            fputsnl(s, pimg->fh);
1164         }
[437caf3]1165         opt = 0;
[23f7ea7]1166         break;
[a420b49]1167       case img_MOVE:
1168         opt = 4;
1169         break;
1170       case img_LINE:
[421b7d2]1171         if (pimg->version > 1) {
[d69255f]1172            opt = 0x80 | (flags & 0x3f);
[437caf3]1173            break;
[421b7d2]1174         }
[437caf3]1175         opt = 5;
[a420b49]1176         break;
[437caf3]1177       default: /* ignore for now */
1178         return;
[a420b49]1179      }
[437caf3]1180      if (pimg->version == 1) {
1181         put32(opt, pimg->fh);
1182      } else {
1183         if (opt) putc(opt, pimg->fh);
[a420b49]1184      }
[d69255f]1185      /* Output in cm */
[dd1d6369]1186      put32((INT32_T)my_round(x * 100.0), pimg->fh);
1187      put32((INT32_T)my_round(y * 100.0), pimg->fh);
1188      put32((INT32_T)my_round(z * 100.0), pimg->fh);
[a420b49]1189   }
[d1b1380]1190}
1191
[ad9a5cd]1192int
[a420b49]1193img_close(img *pimg)
1194{
[ad9a5cd]1195   int result = 1;
[dbb4e19]1196   if (pimg) {
1197      if (pimg->fh) {
[76bbb7c9]1198         if (pimg->fRead) {
1199            osfree(pimg->survey);
[a2ad284]1200            osfree(pimg->title);
1201            osfree(pimg->datestamp);
[76bbb7c9]1202         } else {
1203            /* write end of data marker */
[d69255f]1204            switch (pimg->version) {
1205             case 1:
[5c3c61a]1206               put32((INT32_T)-1, pimg->fh);
[d69255f]1207               break;
1208             case 2:
1209               putc(0, pimg->fh);
1210               break;
1211             case 3:
1212               if (pimg->label_len) putc(0, pimg->fh);
[437caf3]1213               putc(0, pimg->fh);
[d69255f]1214               break;
[5c3c61a]1215            }
1216         }
[ad9a5cd]1217         if (ferror(pimg->fh)) result = 0;
1218         if (fclose(pimg->fh)) result = 0;
1219         img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
[dbb4e19]1220      }
[0ff23cb]1221      osfree(pimg->label_buf);
[dbb4e19]1222      osfree(pimg);
[a420b49]1223   }
[ad9a5cd]1224   return result;
[d1b1380]1225}
Note: See TracBrowser for help on using the repository browser.