2 * Routines for reading and writing Survex ".3d" image files
3 * Copyright (C) 1993-2004,2005,2006,2010,2011,2013,2014,2017,2018 Olly Betts
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
37 # define INT32_T int32_t
39 # include "filelist.h"
40 # include "filename.h"
43 # define TIMEFMT msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107)
47 # define INT32_T int32_t
50 # if INT_MAX >= 2147483647
56 # define TIMEFMT "%a,%Y.%m.%d %H:%M:%S %Z"
57 # define EXT_SVX_3D "3d"
58 # define EXT_SVX_POS "pos"
59 # define FNM_SEP_EXT '.'
60 # define METRES_PER_FOOT 0.3048 /* exact value */
61 # define xosmalloc(L) malloc((L))
62 # define xosrealloc(L,S) realloc((L),(S))
63 # define osfree(P) free((P))
64 # define osnew(T) (T*)malloc(sizeof(T))
66 /* in IMG_HOSTED mode, this tests if a filename refers to a directory */
67 # define fDirectory(X) 0
68 /* open file FNM with mode MODE, maybe using path PTH and/or extension EXT */
69 /* path isn't used in img.c, but EXT is */
70 # define fopenWithPthAndExt(PTH,FNM,EXT,MODE,X) \
71 ((*(X) = NULL), fopen(FNM,MODE))
73 # define PUTC(C, FH) putc(C, FH)
76 # define GETC(FH) getc(FH)
78 # define fputsnl(S, FH) (fputs((S), (FH)) == EOF ? EOF : putc('\n', (FH)))
79 # define SVX_ASSERT(X)
86 /* Return max/min of two numbers. */
87 /* May be defined already (e.g. by Borland C in stdlib.h) */
88 /* NB Bad news if X or Y has side-effects... */
90 # define max(X, Y) ((X) > (Y) ? (X) : (Y))
93 # define min(X, Y) ((X) < (Y) ? (X) : (Y))
100 INT32_T w
= GETC(fh
);
101 w
|= (INT32_T
)GETC(fh
) << 8l;
102 w
|= (INT32_T
)GETC(fh
) << 16l;
103 w
|= (INT32_T
)GETC(fh
) << 24l;
108 put32(long w
, FILE *fh
)
111 PUTC((char)(w
>> 8l), fh
);
112 PUTC((char)(w
>> 16l), fh
);
113 PUTC((char)(w
>> 24l), fh
);
120 w
|= (short)GETC(fh
) << 8l;
125 put16(short w
, FILE *fh
)
128 PUTC((char)(w
>> 8l), fh
);
132 baseleaf_from_fnm(const char *fnm
)
142 q
= strrchr(p
, '\\');
145 q
= strrchr(p
, FNM_SEP_EXT
);
146 if (q
) len
= (const char *)q
- p
; else len
= strlen(p
);
148 res
= (char *)xosmalloc(len
+ 1);
149 if (!res
) return NULL
;
156 static char * my_strdup(const char *str
);
159 mktime_with_tz(struct tm
* tm
, const char * tz
)
162 char * old_tz
= getenv("TZ");
165 old_tz
= my_strdup(old_tz
);
169 if (_putenv_s("TZ", tz
) != 0) {
173 #elif defined HAVE_SETENV
175 old_tz
= my_strdup(old_tz
);
179 if (setenv("TZ", tz
, 1) < 0) {
186 size_t len
= strlen(old_tz
) + 1;
187 p
= (char *)xosmalloc(len
+ 3);
191 memcpy(p
+ 3, tz
, len
);
194 p
= (char *)xosmalloc(strlen(tz
) + 4);
201 if (putenv(p
) != 0) {
206 #define CLEANUP() osfree(p)
212 _putenv_s("TZ", old_tz
);
213 #elif !defined HAVE_SETENV
216 setenv("TZ", old_tz
, 1);
222 #elif !defined HAVE_UNSETENV
235 static unsigned short
238 return (unsigned short)get16(fh
);
243 #if !defined HAVE_LROUND && !defined HAVE_DECL_LROUND
244 /* The autoconf tests are not in use, but C99 and C++11 both added lround(),
245 * so set HAVE_LROUND and HAVE_DECL_LROUND conservatively based on the language
246 * standard version the compiler claims to support. */
247 # if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \
248 (defined __cplusplus && __cplusplus >= 201103L)
249 # define HAVE_LROUND 1
250 # define HAVE_DECL_LROUND 1
255 # if defined HAVE_DECL_LROUND && !HAVE_DECL_LROUND
256 /* On older systems, the prototype may be missing. */
257 extern long lround(double);
259 # define my_lround lround
262 my_lround(double x
) {
263 return (x
>= 0.0) ? (long)(x
+ 0.5) : -(long)(0.5 - x
);
267 /* portable case insensitive string compare */
268 #if defined(strcasecmp) || defined(HAVE_STRCASECMP)
269 # define my_strcasecmp strcasecmp
271 static int my_strcasecmp(const char *s1
, const char *s2
) {
272 unsigned char c1
, c2
;
276 } while (c1
&& toupper(c1
) == toupper(c2
));
277 /* now calculate real difference */
282 unsigned int img_output_version
= IMG_VERSION_MAX
;
284 static img_errcode img_errno
= IMG_NONE
;
286 #define FILEID "Survex 3D Image File"
288 #define EXT_PLT "plt"
289 #define EXT_PLF "plf"
291 /* Attempt to string paste to ensure we are passed a literal string */
292 #define LITLEN(S) (sizeof(S"") - 1)
294 /* Fake "version numbers" for non-3d formats we can read. */
295 #define VERSION_CMAP_SHOT -4
296 #define VERSION_CMAP_STATION -3
297 #define VERSION_COMPASS_PLT -2
298 #define VERSION_SURVEX_POS -1
301 my_strdup(const char *str
)
304 size_t len
= strlen(str
) + 1;
305 p
= (char *)xosmalloc(len
);
306 if (p
) memcpy(p
, str
, len
);
310 #define getline_alloc(FH) getline_alloc_len(FH, NULL)
313 getline_alloc_len(FILE *fh
, size_t * p_len
)
318 char *buf
= (char *)xosmalloc(len
);
319 if (!buf
) return NULL
;
322 while (ch
!= '\n' && ch
!= '\r' && ch
!= EOF
) {
327 p
= (char *)xosrealloc(buf
, len
);
336 if (ch
== '\n' || ch
== '\r') {
337 int otherone
= ch
^ ('\n' ^ '\r');
339 /* if it's not the other eol character, put it back */
340 if (ch
!= otherone
) ungetc(ch
, fh
);
343 if (p_len
) *p_len
= i
;
354 check_label_space(img
*pimg
, size_t len
)
356 if (len
> pimg
->buf_len
) {
357 char *b
= (char *)xosrealloc(pimg
->label_buf
, len
);
359 pimg
->label
= (pimg
->label
- pimg
->label_buf
) + b
;
366 /* Check if a station name should be included. */
368 stn_included(img
*pimg
)
370 if (!pimg
->survey_len
) return 1;
371 size_t l
= pimg
->survey_len
;
372 const char *s
= pimg
->label_buf
;
373 if (strncmp(pimg
->survey
, s
, l
+ 1) != 0) {
376 pimg
->label
+= l
+ 1;
380 /* Check if a survey name should be included. */
382 survey_included(img
*pimg
)
384 if (!pimg
->survey_len
) return 1;
385 size_t l
= pimg
->survey_len
;
386 const char *s
= pimg
->label_buf
;
387 if (strncmp(pimg
->survey
, s
, l
) != 0 ||
388 !(s
[l
] == '.' || s
[l
] == '\0')) {
392 /* skip the dot if there */
393 if (*pimg
->label
) pimg
->label
++;
397 /* Check if a survey name in a buffer should be included.
399 * For "foreign" formats which just have one level of surveys.
402 buf_included(img
*pimg
, const char *buf
, size_t len
)
404 return pimg
->survey_len
== len
&& strncmp(buf
, pimg
->survey
, len
) == 0;
407 #define has_ext(F,L,E) ((L) > LITLEN(E) + 1 &&\
408 (F)[(L) - LITLEN(E) - 1] == FNM_SEP_EXT &&\
409 my_strcasecmp((F) + (L) - LITLEN(E), E) == 0)
412 img_open_survey(const char *fnm
, const char *survey
)
416 char* filename_opened
= NULL
;
418 if (fDirectory(fnm
)) {
419 img_errno
= IMG_DIRECTORY
;
423 fh
= fopenWithPthAndExt("", fnm
, EXT_SVX_3D
, "rb", &filename_opened
);
424 pimg
= img_read_stream_survey(fh
, fclose
, filename_opened
, survey
);
426 pimg
->filename_opened
= filename_opened
;
428 osfree(filename_opened
);
434 img_read_stream_survey(FILE *stream
, int (*close_func
)(FILE*),
440 char buf
[LITLEN(FILEID
) + 9];
443 if (stream
== NULL
) {
444 img_errno
= IMG_FILENOTFOUND
;
450 img_errno
= IMG_OUTOFMEMORY
;
451 if (close_func
) close_func(stream
);
456 pimg
->close_func
= close_func
;
459 pimg
->label_buf
= (char *)xosmalloc(pimg
->buf_len
);
460 if (!pimg
->label_buf
) {
461 if (pimg
->close_func
) pimg
->close_func(pimg
->fh
);
463 img_errno
= IMG_OUTOFMEMORY
;
467 pimg
->fRead
= 1; /* reading from this file */
468 img_errno
= IMG_NONE
;
471 pimg
->filename_opened
= NULL
;
473 /* for version >= 3 we use label_buf to store the prefix for reuse */
474 /* for VERSION_COMPASS_PLT, 0 value indicates we haven't
475 * entered a survey yet */
476 /* for VERSION_CMAP_SHOT, we store the last station here
477 * to detect whether we MOVE or LINE */
479 pimg
->label_buf
[0] = '\0';
482 pimg
->survey_len
= 0;
483 pimg
->separator
= '.';
484 #if IMG_API_VERSION == 0
485 pimg
->date1
= pimg
->date2
= 0;
486 #else /* IMG_API_VERSION == 1 */
487 pimg
->days1
= pimg
->days2
= -1;
489 pimg
->is_extended_elevation
= 0;
491 pimg
->style
= pimg
->oldstyle
= img_STYLE_UNKNOWN
;
493 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
495 pimg
->title
= pimg
->datestamp
= pimg
->cs
= NULL
;
496 pimg
->datestamp_numeric
= (time_t)-1;
499 len
= strlen(survey
);
501 if (survey
[len
- 1] == '.') len
--;
504 pimg
->survey
= (char *)xosmalloc(len
+ 2);
506 img_errno
= IMG_OUTOFMEMORY
;
509 memcpy(pimg
->survey
, survey
, len
);
510 /* Set title to leaf survey name */
511 pimg
->survey
[len
] = '\0';
512 p
= strrchr(pimg
->survey
, '.');
513 if (p
) p
++; else p
= pimg
->survey
;
514 pimg
->title
= my_strdup(p
);
516 img_errno
= IMG_OUTOFMEMORY
;
519 pimg
->survey
[len
] = '.';
520 pimg
->survey
[len
+ 1] = '\0';
523 pimg
->survey_len
= len
;
526 /* [VERSION_COMPASS_PLT, VERSION_CMAP_STATION, VERSION_CMAP_SHOT] pending
527 * IMG_LINE or IMG_MOVE - both have 4 added.
528 * [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
529 * [version 0] not in the middle of a 'LINE' command
530 * [version >= 3] not in the middle of turning a LINE into a MOVE
535 if (has_ext(fnm
, len
, EXT_SVX_POS
)) {
537 pimg
->version
= VERSION_SURVEX_POS
;
538 if (!pimg
->survey
) pimg
->title
= baseleaf_from_fnm(fnm
);
539 pimg
->datestamp
= my_strdup(TIMENA
);
540 if (!pimg
->datestamp
) {
541 img_errno
= IMG_OUTOFMEMORY
;
548 if (has_ext(fnm
, len
, EXT_PLT
) || has_ext(fnm
, len
, EXT_PLF
)) {
551 pimg
->version
= VERSION_COMPASS_PLT
;
552 /* Spaces aren't legal in Compass station names, but dots are, so
553 * use space as the level separator */
554 pimg
->separator
= ' ';
556 if (!pimg
->survey
) pimg
->title
= baseleaf_from_fnm(fnm
);
557 pimg
->datestamp
= my_strdup(TIMENA
);
558 if (!pimg
->datestamp
) {
559 img_errno
= IMG_OUTOFMEMORY
;
566 fseek(pimg
->fh
, -1, SEEK_CUR
);
569 pimg
->start
= ftell(pimg
->fh
);
573 fpos
= ftell(pimg
->fh
) - 1;
575 /* FIXME : if there's only one survey in the file, it'd be nice
576 * to use its description as the title here...
578 ungetc('N', pimg
->fh
);
582 line
= getline_alloc(pimg
->fh
);
584 img_errno
= IMG_OUTOFMEMORY
;
588 while (line
[len
] > 32) ++len
;
589 if (!buf_included(pimg
, line
, len
)) {
593 q
= strchr(line
+ len
, 'C');
596 pimg
->title
= my_strdup(q
+ 1);
597 } else if (!pimg
->title
) {
598 pimg
->title
= my_strdup(pimg
->label
);
602 img_errno
= IMG_OUTOFMEMORY
;
605 if (!pimg
->start
) pimg
->start
= fpos
;
606 fseek(pimg
->fh
, pimg
->start
, SEEK_SET
);
610 pimg
->start
= ftell(pimg
->fh
) - 1;
613 while (ch
!= '\n' && ch
!= '\r') {
619 /* Although these are often referred to as "CMAP .XYZ files", it seems
620 * that actually, the extension .XYZ isn't used, rather .SHT (shot
621 * variant, produced by CMAP v16 and later), .UNA (unadjusted) and
622 * .ADJ (adjusted) extensions are. Since img has long checked for
623 * .XYZ, we continue to do so in case anyone is relying on it.
625 if (has_ext(fnm
, len
, "sht") ||
626 has_ext(fnm
, len
, "adj") ||
627 has_ext(fnm
, len
, "una") ||
628 has_ext(fnm
, len
, "xyz")) {
631 /* Spaces aren't legal in CMAP station names, but dots are, so
632 * use space as the level separator. */
633 pimg
->separator
= ' ';
634 line
= getline_alloc(pimg
->fh
);
636 img_errno
= IMG_OUTOFMEMORY
;
639 /* There doesn't seem to be a spec for what happens after 1999 with cmap
640 * files, so this code allows for:
641 * * 21xx -> xx (up to 2150)
642 * * 21xx -> 1xx (up to 2199)
643 * * full year being specified instead of 2 digits
647 /* Don't just truncate at column 59, allow for a > 2 digit year. */
648 char * p
= strstr(line
+ len
, "Page");
650 while (p
> line
&& p
[-1] == ' ')
663 pimg
->datestamp
= my_strdup(line
+ 45);
665 v
= strtoul(p
, &p
, 10);
667 /* In the absence of a spec for cmap files, assume <= 50 means 21st
670 } else if (v
< 200) {
671 /* Map 100-199 to 21st century. */
674 if (v
== ULONG_MAX
|| *p
++ != '/')
676 tm
.tm_year
= v
- 1900;
677 v
= strtoul(p
, &p
, 10);
678 if (v
< 1 || v
> 12 || *p
++ != '/')
681 v
= strtoul(p
, &p
, 10);
682 if (v
< 1 || v
> 31 || *p
++ != ' ')
685 v
= strtoul(p
, &p
, 10);
686 if (v
>= 24 || *p
++ != ':')
689 v
= strtoul(p
, &p
, 10);
694 v
= strtoul(p
+ 1, &p
, 10);
702 /* We have no indication of what timezone this timestamp is in. It's
703 * probably local time for whoever processed the data, so just assume
704 * UTC, which is at least fairly central in the possibilities.
706 pimg
->datestamp_numeric
= mktime_with_tz(&tm
, "");
708 pimg
->datestamp
= my_strdup(TIMENA
);
711 if (strncmp(line
, " Cave Survey Data Processed by CMAP ",
712 LITLEN(" Cave Survey Data Processed by CMAP ")) == 0) {
719 while (len
> 2 && line
[len
- 1] == ' ') --len
;
722 pimg
->title
= my_strdup(line
+ 2);
725 if (len
<= 2) pimg
->title
= baseleaf_from_fnm(fnm
);
727 if (!pimg
->datestamp
|| !pimg
->title
) {
728 img_errno
= IMG_OUTOFMEMORY
;
731 line
= getline_alloc(pimg
->fh
);
733 img_errno
= IMG_OUTOFMEMORY
;
736 if (line
[0] != ' ' || (line
[1] != 'S' && line
[1] != 'O')) {
737 img_errno
= IMG_BADFORMAT
;
740 if (line
[1] == 'S') {
741 pimg
->version
= VERSION_CMAP_STATION
;
743 pimg
->version
= VERSION_CMAP_SHOT
;
746 line
= getline_alloc(pimg
->fh
);
748 img_errno
= IMG_OUTOFMEMORY
;
751 if (line
[0] != ' ' || line
[1] != '-') {
752 img_errno
= IMG_BADFORMAT
;
756 pimg
->start
= ftell(pimg
->fh
);
760 if (fread(buf
, LITLEN(FILEID
) + 1, 1, pimg
->fh
) != 1 ||
761 memcmp(buf
, FILEID
"\n", LITLEN(FILEID
) + 1) != 0) {
762 if (fread(buf
+ LITLEN(FILEID
) + 1, 8, 1, pimg
->fh
) == 1 &&
763 memcmp(buf
, FILEID
"\r\nv0.01\r\n", LITLEN(FILEID
) + 9) == 0) {
764 /* v0 3d file with DOS EOLs */
771 /* Looks like a CMAP .xyz file ... */
773 } else if (strchr("ZSNF", buf
[0])) {
774 /* Looks like a Compass .plt file ... */
775 /* Almost certainly it'll start "Z " */
780 /* Looks like a Survex .pos file ... */
783 img_errno
= IMG_BADFORMAT
;
787 /* check file format version */
790 if (tolower(ch
) == 'b') {
791 /* binary file iff B/b prefix */
796 img_errno
= IMG_BADFORMAT
;
801 if (fread(buf
, 4, 1, pimg
->fh
) != 1 || memcmp(buf
, ".01\n", 4) != 0) {
802 img_errno
= IMG_BADFORMAT
;
805 /* nothing special to do */
806 } else if (pimg
->version
== 0) {
807 if (ch
< '2' || ch
> '0' + IMG_VERSION_MAX
|| GETC(pimg
->fh
) != '\n') {
808 img_errno
= IMG_TOONEW
;
811 pimg
->version
= ch
- '0';
813 img_errno
= IMG_BADFORMAT
;
820 char * title
= getline_alloc_len(pimg
->fh
, &title_len
);
821 if (pimg
->version
== 8 && title
) {
822 /* We sneak in an extra field after a zero byte here, containing the
823 * specified coordinate system (if any). Older readers will just
824 * not see it (which is fine), and this trick avoids us having to
825 * bump the 3d format version.
827 size_t real_len
= strlen(title
);
828 if (real_len
!= title_len
) {
829 pimg
->cs
= my_strdup(title
+ real_len
+ 1);
838 pimg
->datestamp
= getline_alloc(pimg
->fh
);
839 if (!pimg
->title
|| !pimg
->datestamp
) {
840 img_errno
= IMG_OUTOFMEMORY
;
844 osfree(pimg
->datestamp
);
845 osfree(pimg
->filename_opened
);
846 if (pimg
->close_func
) pimg
->close_func(pimg
->fh
);
851 if (pimg
->version
>= 8) {
852 int flags
= GETC(pimg
->fh
);
853 if (flags
& img_FFLAG_EXTENDED
) pimg
->is_extended_elevation
= 1;
855 len
= strlen(pimg
->title
);
856 if (len
> 11 && strcmp(pimg
->title
+ len
- 11, " (extended)") == 0) {
857 pimg
->title
[len
- 11] = '\0';
858 pimg
->is_extended_elevation
= 1;
862 if (pimg
->datestamp
[0] == '@') {
866 v
= strtoul(pimg
->datestamp
+ 1, &p
, 10);
867 if (errno
== 0 && *p
== '\0')
868 pimg
->datestamp_numeric
= v
;
869 /* FIXME: We're assuming here that the C time_t epoch is 1970, which is
870 * true for Unix-like systems, Mac OS X and Windows, but isn't guaranteed
874 /* %a,%Y.%m.%d %H:%M:%S %Z */
877 char * p
= pimg
->datestamp
;
878 while (isalpha((unsigned char)*p
)) ++p
;
880 while (isspace((unsigned char)*p
)) ++p
;
881 v
= strtoul(p
, &p
, 10);
882 if (v
== ULONG_MAX
|| *p
++ != '.')
884 tm
.tm_year
= v
- 1900;
885 v
= strtoul(p
, &p
, 10);
886 if (v
< 1 || v
> 12 || *p
++ != '.')
889 v
= strtoul(p
, &p
, 10);
890 if (v
< 1 || v
> 31 || *p
++ != ' ')
893 v
= strtoul(p
, &p
, 10);
894 if (v
>= 24 || *p
++ != ':')
897 v
= strtoul(p
, &p
, 10);
898 if (v
>= 60 || *p
++ != ':')
901 v
= strtoul(p
, &p
, 10);
906 while (isspace((unsigned char)*p
)) ++p
;
907 /* p now points to the timezone string.
909 * However, it's likely to be a string like "BST", and such strings can
910 * be ambiguous (BST could be UTC+1 or UTC+6), so it is impossible to
911 * reliably convert in all cases. Just pass what we have to tzset() - if
912 * it doesn't handle it, UTC will be used.
914 pimg
->datestamp_numeric
= mktime_with_tz(&tm
, p
);
918 pimg
->start
= ftell(pimg
->fh
);
924 img_rewind(img
*pimg
)
927 img_errno
= IMG_WRITEERROR
;
930 if (fseek(pimg
->fh
, pimg
->start
, SEEK_SET
) != 0) {
931 img_errno
= IMG_READERROR
;
935 /* [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
936 * [version 0] not in the middle of a 'LINE' command
937 * [version >= 3] not in the middle of turning a LINE into a MOVE */
940 img_errno
= IMG_NONE
;
942 /* for version >= 3 we use label_buf to store the prefix for reuse */
943 /* for VERSION_COMPASS_PLT, 0 value indicates we haven't entered a survey
945 /* for VERSION_CMAP_SHOT, we store the last station here to detect whether
948 pimg
->style
= img_STYLE_UNKNOWN
;
953 img_open_write_cs(const char *fnm
, const char *title
, const char *cs
, int flags
)
955 if (fDirectory(fnm
)) {
956 img_errno
= IMG_DIRECTORY
;
960 return img_write_stream(fopen(fnm
, "wb"), fclose
, title
, cs
, flags
);
964 img_write_stream(FILE *stream
, int (*close_func
)(FILE*),
965 const char *title
, const char *cs
, int flags
)
970 if (stream
== NULL
) {
971 img_errno
= IMG_FILENOTFOUND
;
977 img_errno
= IMG_OUTOFMEMORY
;
978 if (close_func
) close_func(stream
);
983 pimg
->close_func
= close_func
;
985 pimg
->label_buf
= (char *)xosmalloc(pimg
->buf_len
);
986 if (!pimg
->label_buf
) {
987 if (pimg
->close_func
) pimg
->close_func(pimg
->fh
);
989 img_errno
= IMG_OUTOFMEMORY
;
993 pimg
->filename_opened
= NULL
;
995 /* Output image file header */
996 fputs("Survex 3D Image File\n", pimg
->fh
); /* file identifier string */
997 if (img_output_version
< 2) {
999 fputs("Bv0.01\n", pimg
->fh
); /* binary file format version number */
1001 pimg
->version
= (img_output_version
> IMG_VERSION_MAX
) ? IMG_VERSION_MAX
: img_output_version
;
1002 fprintf(pimg
->fh
, "v%d\n", pimg
->version
); /* file format version no. */
1005 fputs(title
, pimg
->fh
);
1006 if (pimg
->version
< 8 && (flags
& img_FFLAG_EXTENDED
)) {
1007 /* Older format versions append " (extended)" to the title to mark
1008 * extended elevations. */
1009 size_t len
= strlen(title
);
1010 if (len
< 11 || strcmp(title
+ len
- 11, " (extended)") != 0)
1011 fputs(" (extended)", pimg
->fh
);
1013 if (pimg
->version
== 8 && cs
&& *cs
) {
1014 /* We sneak in an extra field after a zero byte here, containing the
1015 * specified coordinate system (if any). Older readers will just not
1016 * see it (which is fine), and this trick avoids us having to bump the
1017 * 3d format version.
1019 PUTC('\0', pimg
->fh
);
1020 fputs(cs
, pimg
->fh
);
1022 PUTC('\n', pimg
->fh
);
1025 if (tm
== (time_t)-1) {
1026 fputsnl(TIMENA
, pimg
->fh
);
1027 } else if (pimg
->version
<= 7) {
1029 /* output current date and time in format specified */
1030 strftime(date
, 256, TIMEFMT
, localtime(&tm
));
1031 fputsnl(date
, pimg
->fh
);
1033 fprintf(pimg
->fh
, "@%ld\n", (long)tm
);
1036 if (pimg
->version
>= 8) {
1037 /* Clear bit one in case anyone has been passing true for fBinary. */
1039 PUTC(flags
, pimg
->fh
);
1043 if (img_output_version
>= 5) {
1044 static const unsigned char codelengths
[32] = {
1045 4, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1046 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1048 fwrite(codelengths
, 32, 1, pimg
->fh
);
1051 pimg
->fRead
= 0; /* writing to this file */
1052 img_errno
= IMG_NONE
;
1054 /* for version >= 3 we use label_buf to store the prefix for reuse */
1055 pimg
->label_buf
[0] = '\0';
1056 pimg
->label_len
= 0;
1058 #if IMG_API_VERSION == 0
1059 pimg
->date1
= pimg
->date2
= 0;
1060 pimg
->olddate1
= pimg
->olddate2
= 0;
1061 #else /* IMG_API_VERSION == 1 */
1062 pimg
->days1
= pimg
->days2
= -1;
1063 pimg
->olddays1
= pimg
->olddays2
= -1;
1065 pimg
->style
= pimg
->oldstyle
= img_STYLE_UNKNOWN
;
1067 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
1071 pimg
->E
= pimg
->H
= pimg
->V
= 0.0;
1073 /* Don't check for write errors now - let img_close() report them... */
1078 read_xyz_station_coords(img_point
*pt
, const char *line
)
1081 memcpy(num
, line
+ 6, 9);
1083 pt
->x
= atof(num
) / METRES_PER_FOOT
;
1084 memcpy(num
, line
+ 15, 9);
1085 pt
->y
= atof(num
) / METRES_PER_FOOT
;
1086 memcpy(num
, line
+ 24, 8);
1088 pt
->z
= atof(num
) / METRES_PER_FOOT
;
1092 read_xyz_shot_coords(img_point
*pt
, const char *line
)
1095 memcpy(num
, line
+ 40, 10);
1097 pt
->x
= atof(num
) / METRES_PER_FOOT
;
1098 memcpy(num
, line
+ 50, 10);
1099 pt
->y
= atof(num
) / METRES_PER_FOOT
;
1100 memcpy(num
, line
+ 60, 9);
1102 pt
->z
= atof(num
) / METRES_PER_FOOT
;
1106 subtract_xyz_shot_deltas(img_point
*pt
, const char *line
)
1109 memcpy(num
, line
+ 15, 9);
1111 pt
->x
-= atof(num
) / METRES_PER_FOOT
;
1112 memcpy(num
, line
+ 24, 8);
1114 pt
->y
-= atof(num
) / METRES_PER_FOOT
;
1115 memcpy(num
, line
+ 32, 8);
1116 pt
->z
-= atof(num
) / METRES_PER_FOOT
;
1120 read_coord(FILE *fh
, img_point
*pt
)
1124 pt
->x
= get32(fh
) / 100.0;
1125 pt
->y
= get32(fh
) / 100.0;
1126 pt
->z
= get32(fh
) / 100.0;
1127 if (ferror(fh
) || feof(fh
)) {
1128 img_errno
= feof(fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1135 skip_coord(FILE *fh
)
1137 return (fseek(fh
, 12, SEEK_CUR
) == 0);
1141 read_v3label(img
*pimg
)
1144 long len
= GETC(pimg
->fh
);
1146 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1150 len
+= get16(pimg
->fh
);
1151 if (feof(pimg
->fh
)) {
1152 img_errno
= IMG_BADFORMAT
;
1155 if (ferror(pimg
->fh
)) {
1156 img_errno
= IMG_READERROR
;
1159 } else if (len
== 0xff) {
1160 len
= get32(pimg
->fh
);
1161 if (ferror(pimg
->fh
)) {
1162 img_errno
= IMG_READERROR
;
1165 if (feof(pimg
->fh
) || len
< 0xfe + 0xffff) {
1166 img_errno
= IMG_BADFORMAT
;
1171 if (!check_label_space(pimg
, pimg
->label_len
+ len
+ 1)) {
1172 img_errno
= IMG_OUTOFMEMORY
;
1175 q
= pimg
->label_buf
+ pimg
->label_len
;
1176 pimg
->label_len
+= len
;
1177 if (len
&& fread(q
, len
, 1, pimg
->fh
) != 1) {
1178 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1186 read_v8label(img
*pimg
, int common_flag
, size_t common_val
)
1191 if (common_val
== 0) return 0;
1192 add
= del
= common_val
;
1194 int ch
= GETC(pimg
->fh
);
1196 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1203 ch
= GETC(pimg
->fh
);
1205 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1211 del
= get32(pimg
->fh
);
1212 if (ferror(pimg
->fh
)) {
1213 img_errno
= IMG_READERROR
;
1217 ch
= GETC(pimg
->fh
);
1219 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1225 add
= get32(pimg
->fh
);
1226 if (ferror(pimg
->fh
)) {
1227 img_errno
= IMG_READERROR
;
1233 if (add
> del
&& !check_label_space(pimg
, pimg
->label_len
+ add
- del
+ 1)) {
1234 img_errno
= IMG_OUTOFMEMORY
;
1238 if (del
> pimg
->label_len
) {
1239 img_errno
= IMG_BADFORMAT
;
1242 pimg
->label_len
-= del
;
1243 q
= pimg
->label_buf
+ pimg
->label_len
;
1244 pimg
->label_len
+= add
;
1245 if (add
&& fread(q
, add
, 1, pimg
->fh
) != 1) {
1246 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1253 static int img_read_item_new(img
*pimg
, img_point
*p
);
1254 static int img_read_item_v3to7(img
*pimg
, img_point
*p
);
1255 static int img_read_item_ancient(img
*pimg
, img_point
*p
);
1256 static int img_read_item_ascii_wrapper(img
*pimg
, img_point
*p
);
1257 static int img_read_item_ascii(img
*pimg
, img_point
*p
);
1260 img_read_item(img
*pimg
, img_point
*p
)
1264 if (pimg
->version
>= 8) {
1265 return img_read_item_new(pimg
, p
);
1266 } else if (pimg
->version
>= 3) {
1267 return img_read_item_v3to7(pimg
, p
);
1268 } else if (pimg
->version
>= 1) {
1269 return img_read_item_ancient(pimg
, p
);
1271 return img_read_item_ascii_wrapper(pimg
, p
);
1276 img_read_item_new(img
*pimg
, img_point
*p
)
1280 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
1281 if (pimg
->pending
>= 0x40) {
1282 if (pimg
->pending
== 256) {
1284 return img_XSECT_END
;
1287 pimg
->flags
= (int)(pimg
->pending
) & 0x3f;
1291 again3
: /* label to goto if we get a prefix, date, or lrud */
1292 pimg
->label
= pimg
->label_buf
;
1293 opt
= GETC(pimg
->fh
);
1295 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1298 if (opt
>> 6 == 0) {
1300 if (opt
== 0 && pimg
->style
== 0)
1301 return img_STOP
; /* end of data marker */
1308 case 0x10: { /* No date info */
1309 #if IMG_API_VERSION == 0
1310 pimg
->date1
= pimg
->date2
= 0;
1311 #else /* IMG_API_VERSION == 1 */
1312 pimg
->days1
= pimg
->days2
= -1;
1316 case 0x11: { /* Single date */
1317 int days1
= (int)getu16(pimg
->fh
);
1318 #if IMG_API_VERSION == 0
1319 pimg
->date2
= pimg
->date1
= (days1
- 25567) * 86400;
1320 #else /* IMG_API_VERSION == 1 */
1321 pimg
->days2
= pimg
->days1
= days1
;
1325 case 0x12: { /* Date range (short) */
1326 int days1
= (int)getu16(pimg
->fh
);
1327 int days2
= days1
+ GETC(pimg
->fh
) + 1;
1328 #if IMG_API_VERSION == 0
1329 pimg
->date1
= (days1
- 25567) * 86400;
1330 pimg
->date2
= (days2
- 25567) * 86400;
1331 #else /* IMG_API_VERSION == 1 */
1332 pimg
->days1
= days1
;
1333 pimg
->days2
= days2
;
1337 case 0x13: { /* Date range (long) */
1338 int days1
= (int)getu16(pimg
->fh
);
1339 int days2
= (int)getu16(pimg
->fh
);
1340 #if IMG_API_VERSION == 0
1341 pimg
->date1
= (days1
- 25567) * 86400;
1342 pimg
->date2
= (days2
- 25567) * 86400;
1343 #else /* IMG_API_VERSION == 1 */
1344 pimg
->days1
= days1
;
1345 pimg
->days2
= days2
;
1349 case 0x1f: /* Error info */
1350 pimg
->n_legs
= get32(pimg
->fh
);
1351 pimg
->length
= get32(pimg
->fh
) / 100.0;
1352 pimg
->E
= get32(pimg
->fh
) / 100.0;
1353 pimg
->H
= get32(pimg
->fh
) / 100.0;
1354 pimg
->V
= get32(pimg
->fh
) / 100.0;
1355 return img_ERROR_INFO
;
1356 case 0x30: case 0x31: /* LRUD */
1357 case 0x32: case 0x33: /* Big LRUD! */
1358 if (read_v8label(pimg
, 0, 0) == img_BAD
) return img_BAD
;
1359 pimg
->flags
= (int)opt
& 0x01;
1361 pimg
->l
= get16(pimg
->fh
) / 100.0;
1362 pimg
->r
= get16(pimg
->fh
) / 100.0;
1363 pimg
->u
= get16(pimg
->fh
) / 100.0;
1364 pimg
->d
= get16(pimg
->fh
) / 100.0;
1366 pimg
->l
= get32(pimg
->fh
) / 100.0;
1367 pimg
->r
= get32(pimg
->fh
) / 100.0;
1368 pimg
->u
= get32(pimg
->fh
) / 100.0;
1369 pimg
->d
= get32(pimg
->fh
) / 100.0;
1371 if (!stn_included(pimg
)) {
1372 return img_XSECT_END
;
1374 /* If this is the last cross-section in this passage, set
1375 * pending so we return img_XSECT_END next time. */
1376 if (pimg
->flags
& 0x01) {
1377 pimg
->pending
= 256;
1378 pimg
->flags
&= ~0x01;
1381 default: /* 0x25 - 0x2f and 0x34 - 0x3f are currently unallocated. */
1382 img_errno
= IMG_BADFORMAT
;
1388 /* 1-14 and 16-31 reserved */
1389 img_errno
= IMG_BADFORMAT
;
1393 } else if (opt
>= 0x80) {
1394 if (read_v8label(pimg
, 0, 0) == img_BAD
) return img_BAD
;
1398 if (!stn_included(pimg
)) {
1399 if (!skip_coord(pimg
->fh
)) return img_BAD
;
1404 pimg
->flags
= (int)opt
& 0x7f;
1405 } else if ((opt
>> 6) == 1) {
1406 if (read_v8label(pimg
, opt
& 0x20, 0) == img_BAD
) return img_BAD
;
1410 if (!survey_included(pimg
)) {
1411 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1416 if (pimg
->pending
) {
1418 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1419 pimg
->pending
= opt
;
1422 pimg
->flags
= (int)opt
& 0x1f;
1424 img_errno
= IMG_BADFORMAT
;
1427 if (!read_coord(pimg
->fh
, p
)) return img_BAD
;
1433 img_read_item_v3to7(img
*pimg
, img_point
*p
)
1437 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1.0;
1438 if (pimg
->pending
== 256) {
1440 return img_XSECT_END
;
1442 if (pimg
->pending
>= 0x80) {
1444 pimg
->flags
= (int)(pimg
->pending
) & 0x3f;
1448 again3
: /* label to goto if we get a prefix, date, or lrud */
1449 pimg
->label
= pimg
->label_buf
;
1450 opt
= GETC(pimg
->fh
);
1452 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1458 if (!pimg
->label_len
) return img_STOP
; /* end of data marker */
1459 pimg
->label_len
= 0;
1463 /* 1-14 mean trim that many levels from current prefix */
1465 if (pimg
->label_len
<= 17) {
1466 /* zero prefix using "0" */
1467 img_errno
= IMG_BADFORMAT
;
1470 /* extra - 1 because label_len points to one past the end */
1471 c
= pimg
->label_len
- 17 - 1;
1472 while (pimg
->label_buf
[c
] != '.' || --opt
> 0) {
1474 /* zero prefix using "0" */
1475 img_errno
= IMG_BADFORMAT
;
1480 pimg
->label_len
= c
;
1489 case 0x20: /* Single date */
1490 if (pimg
->version
< 7) {
1491 int date1
= get32(pimg
->fh
);
1492 #if IMG_API_VERSION == 0
1493 pimg
->date2
= pimg
->date1
= date1
;
1494 #else /* IMG_API_VERSION == 1 */
1496 pimg
->days2
= pimg
->days1
= (date1
/ 86400) + 25567;
1498 pimg
->days2
= pimg
->days1
= -1;
1502 int days1
= (int)getu16(pimg
->fh
);
1503 #if IMG_API_VERSION == 0
1504 pimg
->date2
= pimg
->date1
= (days1
- 25567) * 86400;
1505 #else /* IMG_API_VERSION == 1 */
1506 pimg
->days2
= pimg
->days1
= days1
;
1510 case 0x21: /* Date range (short for v7+) */
1511 if (pimg
->version
< 7) {
1512 INT32_T date1
= get32(pimg
->fh
);
1513 INT32_T date2
= get32(pimg
->fh
);
1514 #if IMG_API_VERSION == 0
1515 pimg
->date1
= date1
;
1516 pimg
->date2
= date2
;
1517 #else /* IMG_API_VERSION == 1 */
1518 pimg
->days1
= (date1
/ 86400) + 25567;
1519 pimg
->days2
= (date2
/ 86400) + 25567;
1522 int days1
= (int)getu16(pimg
->fh
);
1523 int days2
= days1
+ GETC(pimg
->fh
) + 1;
1524 #if IMG_API_VERSION == 0
1525 pimg
->date1
= (days1
- 25567) * 86400;
1526 pimg
->date2
= (days2
- 25567) * 86400;
1527 #else /* IMG_API_VERSION == 1 */
1528 pimg
->days1
= days1
;
1529 pimg
->days2
= days2
;
1533 case 0x22: /* Error info */
1534 pimg
->n_legs
= get32(pimg
->fh
);
1535 pimg
->length
= get32(pimg
->fh
) / 100.0;
1536 pimg
->E
= get32(pimg
->fh
) / 100.0;
1537 pimg
->H
= get32(pimg
->fh
) / 100.0;
1538 pimg
->V
= get32(pimg
->fh
) / 100.0;
1539 if (feof(pimg
->fh
)) {
1540 img_errno
= IMG_BADFORMAT
;
1543 if (ferror(pimg
->fh
)) {
1544 img_errno
= IMG_READERROR
;
1547 return img_ERROR_INFO
;
1548 case 0x23: { /* v7+: Date range (long) */
1549 if (pimg
->version
< 7) {
1550 img_errno
= IMG_BADFORMAT
;
1553 int days1
= (int)getu16(pimg
->fh
);
1554 int days2
= (int)getu16(pimg
->fh
);
1555 if (feof(pimg
->fh
)) {
1556 img_errno
= IMG_BADFORMAT
;
1559 if (ferror(pimg
->fh
)) {
1560 img_errno
= IMG_READERROR
;
1563 #if IMG_API_VERSION == 0
1564 pimg
->date1
= (days1
- 25567) * 86400;
1565 pimg
->date2
= (days2
- 25567) * 86400;
1566 #else /* IMG_API_VERSION == 1 */
1567 pimg
->days1
= days1
;
1568 pimg
->days2
= days2
;
1572 case 0x24: { /* v7+: No date info */
1573 #if IMG_API_VERSION == 0
1574 pimg
->date1
= pimg
->date2
= 0;
1575 #else /* IMG_API_VERSION == 1 */
1576 pimg
->days1
= pimg
->days2
= -1;
1580 case 0x30: case 0x31: /* LRUD */
1581 case 0x32: case 0x33: /* Big LRUD! */
1582 if (read_v3label(pimg
) == img_BAD
) return img_BAD
;
1583 pimg
->flags
= (int)opt
& 0x01;
1585 pimg
->l
= get16(pimg
->fh
) / 100.0;
1586 pimg
->r
= get16(pimg
->fh
) / 100.0;
1587 pimg
->u
= get16(pimg
->fh
) / 100.0;
1588 pimg
->d
= get16(pimg
->fh
) / 100.0;
1590 pimg
->l
= get32(pimg
->fh
) / 100.0;
1591 pimg
->r
= get32(pimg
->fh
) / 100.0;
1592 pimg
->u
= get32(pimg
->fh
) / 100.0;
1593 pimg
->d
= get32(pimg
->fh
) / 100.0;
1595 if (feof(pimg
->fh
)) {
1596 img_errno
= IMG_BADFORMAT
;
1599 if (ferror(pimg
->fh
)) {
1600 img_errno
= IMG_READERROR
;
1603 if (!stn_included(pimg
)) {
1604 return img_XSECT_END
;
1606 /* If this is the last cross-section in this passage, set
1607 * pending so we return img_XSECT_END next time. */
1608 if (pimg
->flags
& 0x01) {
1609 pimg
->pending
= 256;
1610 pimg
->flags
&= ~0x01;
1613 default: /* 0x25 - 0x2f and 0x34 - 0x3f are currently unallocated. */
1614 img_errno
= IMG_BADFORMAT
;
1617 if (feof(pimg
->fh
)) {
1618 img_errno
= IMG_BADFORMAT
;
1621 if (ferror(pimg
->fh
)) {
1622 img_errno
= IMG_READERROR
;
1627 /* 16-31 mean remove (n - 15) characters from the prefix */
1628 /* zero prefix using 0 */
1629 if (pimg
->label_len
<= (size_t)(opt
- 15)) {
1630 img_errno
= IMG_BADFORMAT
;
1633 pimg
->label_len
-= (opt
- 15);
1636 if (read_v3label(pimg
) == img_BAD
) return img_BAD
;
1640 if (!stn_included(pimg
)) {
1641 if (!skip_coord(pimg
->fh
)) return img_BAD
;
1646 pimg
->flags
= (int)opt
& 0x3f;
1649 if (read_v3label(pimg
) == img_BAD
) return img_BAD
;
1653 if (!survey_included(pimg
)) {
1654 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1659 if (pimg
->pending
) {
1661 if (!read_coord(pimg
->fh
, &(pimg
->mv
))) return img_BAD
;
1662 pimg
->pending
= opt
;
1665 pimg
->flags
= (int)opt
& 0x3f;
1668 img_errno
= IMG_BADFORMAT
;
1671 if (!read_coord(pimg
->fh
, p
)) return img_BAD
;
1677 img_read_item_ancient(img
*pimg
, img_point
*p
)
1680 static long opt_lookahead
= 0;
1681 static img_point pt
= { 0.0, 0.0, 0.0 };
1684 again
: /* label to goto if we get a cross */
1685 pimg
->label
= pimg
->label_buf
;
1686 pimg
->label
[0] = '\0';
1688 if (pimg
->version
== 1) {
1689 if (opt_lookahead
) {
1690 opt
= opt_lookahead
;
1693 opt
= get32(pimg
->fh
);
1696 opt
= GETC(pimg
->fh
);
1699 if (feof(pimg
->fh
)) {
1700 img_errno
= IMG_BADFORMAT
;
1703 if (ferror(pimg
->fh
)) {
1704 img_errno
= IMG_READERROR
;
1710 return img_STOP
; /* end of data marker */
1712 /* skip coordinates */
1713 if (!skip_coord(pimg
->fh
)) {
1714 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1721 if (!fgets(pimg
->label_buf
, pimg
->buf_len
, pimg
->fh
)) {
1722 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1725 if (pimg
->label
[0] == '\\') pimg
->label
++;
1726 len
= strlen(pimg
->label
);
1727 if (len
== 0 || pimg
->label
[len
- 1] != '\n') {
1728 img_errno
= IMG_BADFORMAT
;
1731 /* Ignore empty labels in some .3d files (caused by a bug) */
1732 if (len
== 1) goto again
;
1733 pimg
->label
[len
- 1] = '\0';
1734 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* no flags given... */
1735 if (opt
== 2) goto done
;
1743 pimg
->flags
= GETC(pimg
->fh
);
1745 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* no flags given... */
1747 len
= get32(pimg
->fh
);
1749 if (feof(pimg
->fh
)) {
1750 img_errno
= IMG_BADFORMAT
;
1753 if (ferror(pimg
->fh
)) {
1754 img_errno
= IMG_READERROR
;
1758 /* Ignore empty labels in some .3d files (caused by a bug) */
1759 if (len
== 0) goto again
;
1760 if (!check_label_space(pimg
, len
+ 1)) {
1761 img_errno
= IMG_OUTOFMEMORY
;
1764 if (fread(pimg
->label_buf
, len
, 1, pimg
->fh
) != 1) {
1765 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1768 pimg
->label_buf
[len
] = '\0';
1778 switch ((int)opt
& 0xc0) {
1780 pimg
->flags
= (int)opt
& 0x3f;
1785 pimg
->flags
= (int)opt
& 0x3f;
1787 if (!fgets(pimg
->label_buf
, pimg
->buf_len
, pimg
->fh
)) {
1788 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1791 q
= pimg
->label_buf
+ strlen(pimg
->label_buf
) - 1;
1792 /* Ignore empty-labels in some .3d files (caused by a bug) */
1793 if (q
== pimg
->label_buf
) goto again
;
1795 img_errno
= IMG_BADFORMAT
;
1802 img_errno
= IMG_BADFORMAT
;
1808 if (!read_coord(pimg
->fh
, &pt
)) return img_BAD
;
1810 if (result
== img_LABEL
&& !stn_included(pimg
)) {
1817 if (result
== img_MOVE
&& pimg
->version
== 1) {
1818 /* peek at next code and see if it's an old-style label */
1819 opt_lookahead
= get32(pimg
->fh
);
1821 if (feof(pimg
->fh
)) {
1822 img_errno
= IMG_BADFORMAT
;
1825 if (ferror(pimg
->fh
)) {
1826 img_errno
= IMG_READERROR
;
1830 if (opt_lookahead
== 2) return img_read_item_ancient(pimg
, p
);
1837 img_read_item_ascii_wrapper(img
*pimg
, img_point
*p
)
1839 /* We need to set the default locale for fscanf() to work on
1840 * numbers with "." as decimal point. */
1842 char * current_locale
= my_strdup(setlocale(LC_NUMERIC
, NULL
));
1843 setlocale(LC_NUMERIC
, "C");
1844 result
= img_read_item_ascii(pimg
, p
);
1845 setlocale(LC_NUMERIC
, current_locale
);
1846 free(current_locale
);
1850 /* Handle all ASCII formats. */
1852 img_read_item_ascii(img
*pimg
, img_point
*p
)
1855 pimg
->label
= pimg
->label_buf
;
1856 if (pimg
->version
== 0) {
1858 pimg
->label
[0] = '\0';
1859 if (feof(pimg
->fh
)) return img_STOP
;
1860 if (pimg
->pending
) {
1865 /* Stop if nothing found */
1866 if (fscanf(pimg
->fh
, "%6s", cmd
) < 1) return img_STOP
;
1867 if (strcmp(cmd
, "move") == 0)
1869 else if (strcmp(cmd
, "draw") == 0)
1871 else if (strcmp(cmd
, "line") == 0) {
1872 /* set flag to indicate to process second triplet as LINE */
1875 } else if (strcmp(cmd
, "cross") == 0) {
1876 if (fscanf(pimg
->fh
, "%lf%lf%lf", &p
->x
, &p
->y
, &p
->z
) < 3) {
1877 img_errno
= feof(pimg
->fh
) ? IMG_BADFORMAT
: IMG_READERROR
;
1881 } else if (strcmp(cmd
, "name") == 0) {
1883 int ch
= GETC(pimg
->fh
);
1884 if (ch
== ' ') ch
= GETC(pimg
->fh
);
1886 if (ch
== '\n' || ch
== EOF
) {
1887 img_errno
= ferror(pimg
->fh
) ? IMG_READERROR
: IMG_BADFORMAT
;
1890 if (off
== pimg
->buf_len
) {
1891 if (!check_label_space(pimg
, pimg
->buf_len
* 2)) {
1892 img_errno
= IMG_OUTOFMEMORY
;
1896 pimg
->label_buf
[off
++] = ch
;
1897 ch
= GETC(pimg
->fh
);
1899 pimg
->label_buf
[off
] = '\0';
1901 pimg
->label
= pimg
->label_buf
;
1902 if (pimg
->label
[0] == '\\') pimg
->label
++;
1904 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* default flags */
1908 img_errno
= IMG_BADFORMAT
;
1909 return img_BAD
; /* unknown keyword */
1913 if (fscanf(pimg
->fh
, "%lf%lf%lf", &p
->x
, &p
->y
, &p
->z
) < 3) {
1914 img_errno
= ferror(pimg
->fh
) ? IMG_READERROR
: IMG_BADFORMAT
;
1918 if (result
== img_LABEL
&& !stn_included(pimg
)) {
1923 } else if (pimg
->version
== VERSION_SURVEX_POS
) {
1924 /* Survex .pos file */
1927 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* default flags */
1929 while (fscanf(pimg
->fh
, "(%lf,%lf,%lf )", &p
->x
, &p
->y
, &p
->z
) != 3) {
1930 if (ferror(pimg
->fh
)) {
1931 img_errno
= IMG_READERROR
;
1934 if (feof(pimg
->fh
)) return img_STOP
;
1935 if (pimg
->pending
) {
1936 img_errno
= IMG_BADFORMAT
;
1940 /* ignore rest of line */
1942 ch
= GETC(pimg
->fh
);
1943 } while (ch
!= '\n' && ch
!= '\r' && ch
!= EOF
);
1946 pimg
->label_buf
[0] = '\0';
1948 ch
= GETC(pimg
->fh
);
1949 } while (ch
== ' ' || ch
== '\t');
1950 if (ch
== '\n' || ch
== EOF
) {
1951 /* If there's no label, set img_SFLAG_ANON. */
1952 pimg
->flags
|= img_SFLAG_ANON
;
1955 pimg
->label_buf
[0] = ch
;
1957 while (!feof(pimg
->fh
)) {
1958 if (!fgets(pimg
->label_buf
+ off
, pimg
->buf_len
- off
, pimg
->fh
)) {
1959 img_errno
= IMG_READERROR
;
1963 off
+= strlen(pimg
->label_buf
+ off
);
1964 if (off
&& pimg
->label_buf
[off
- 1] == '\n') {
1965 pimg
->label_buf
[off
- 1] = '\0';
1968 if (!check_label_space(pimg
, pimg
->buf_len
* 2)) {
1969 img_errno
= IMG_OUTOFMEMORY
;
1974 pimg
->label
= pimg
->label_buf
;
1976 if (pimg
->label
[0] == '\\') pimg
->label
++;
1978 if (!stn_included(pimg
)) goto againpos
;
1981 } else if (pimg
->version
== VERSION_COMPASS_PLT
) {
1982 /* Compass .plt file */
1983 if (pimg
->pending
> 0) {
1984 /* -1 signals we've entered the first survey we want to
1985 * read, and need to fudge lots if the first action is 'D'...
1987 /* pending MOVE or LINE */
1988 int r
= pimg
->pending
- 4;
1991 pimg
->label
[pimg
->label_len
] = '\0';
1999 int ch
= GETC(pimg
->fh
);
2002 case '\x1a': case EOF
: /* Don't insist on ^Z at end of file */
2004 case 'X': case 'F': case 'S':
2005 /* bounding boX (marks end of survey), Feature survey, or
2006 * new Section - skip to next survey */
2007 if (pimg
->survey
) return img_STOP
;
2011 ch
= GETC(pimg
->fh
);
2012 } while (ch
!= '\n' && ch
!= '\r' && ch
!= EOF
);
2013 while (ch
== '\n' || ch
== '\r') ch
= GETC(pimg
->fh
);
2014 if (ch
== 'N') break;
2015 if (ch
== '\x1a' || ch
== EOF
) return img_STOP
;
2019 line
= getline_alloc(pimg
->fh
);
2021 img_errno
= IMG_OUTOFMEMORY
;
2024 while (line
[len
] > 32) ++len
;
2025 if (pimg
->label_len
== 0) pimg
->pending
= -1;
2026 if (!check_label_space(pimg
, len
+ 1)) {
2028 img_errno
= IMG_OUTOFMEMORY
;
2031 pimg
->label_len
= len
;
2032 pimg
->label
= pimg
->label_buf
;
2033 memcpy(pimg
->label
, line
, len
);
2034 pimg
->label
[len
] = '\0';
2037 case 'M': case 'D': {
2040 if (pimg
->survey
&& pimg
->label_len
== 0) {
2041 /* We're only holding onto this line in case the first line
2042 * of the 'N' is a 'D', so skip it for now...
2046 if (ch
== 'D' && pimg
->pending
== -1) {
2048 fpos
= ftell(pimg
->fh
) - 1;
2049 fseek(pimg
->fh
, pimg
->start
, SEEK_SET
);
2050 ch
= GETC(pimg
->fh
);
2053 /* If a file actually has a 'D' before any 'M', then
2054 * pretend the 'D' is an 'M' - one of the examples
2055 * in the docs was like this! */
2059 line
= getline_alloc(pimg
->fh
);
2061 img_errno
= IMG_OUTOFMEMORY
;
2064 /* Compass stores coordinates as North, East, Up = (y,x,z)! */
2065 if (sscanf(line
, "%lf%lf%lf", &p
->y
, &p
->x
, &p
->z
) != 3) {
2067 if (ferror(pimg
->fh
)) {
2068 img_errno
= IMG_READERROR
;
2070 img_errno
= IMG_BADFORMAT
;
2074 p
->x
*= METRES_PER_FOOT
;
2075 p
->y
*= METRES_PER_FOOT
;
2076 p
->z
*= METRES_PER_FOOT
;
2077 q
= strchr(line
, 'S');
2080 img_errno
= IMG_BADFORMAT
;
2085 while (q
[len
] > ' ') ++len
;
2087 len
+= 2; /* ' ' and '\0' */
2088 if (!check_label_space(pimg
, pimg
->label_len
+ len
)) {
2089 img_errno
= IMG_OUTOFMEMORY
;
2092 pimg
->label
= pimg
->label_buf
;
2093 if (pimg
->label_len
) {
2094 pimg
->label
[pimg
->label_len
] = ' ';
2095 memcpy(pimg
->label
+ pimg
->label_len
+ 1, q
, len
- 1);
2097 memcpy(pimg
->label
, q
, len
- 1);
2100 /* Now read LRUD. Technically, this is optional but virtually
2101 * all PLT files have it (with dummy negative values if no LRUD
2102 * was measured) and some versions of Compass can't read PLT
2105 while (*q
&& *q
<= ' ') q
++;
2107 if (sscanf(q
+ 1, "%lf%lf%lf%lf",
2108 &pimg
->l
, &pimg
->r
, &pimg
->u
, &pimg
->d
) != 4) {
2110 if (ferror(pimg
->fh
)) {
2111 img_errno
= IMG_READERROR
;
2113 img_errno
= IMG_BADFORMAT
;
2117 pimg
->l
*= METRES_PER_FOOT
;
2118 pimg
->r
*= METRES_PER_FOOT
;
2119 pimg
->u
*= METRES_PER_FOOT
;
2120 pimg
->d
*= METRES_PER_FOOT
;
2122 pimg
->l
= pimg
->r
= pimg
->u
= pimg
->d
= -1;
2125 pimg
->flags
= img_SFLAG_UNDERGROUND
; /* default flags */
2127 fseek(pimg
->fh
, fpos
, SEEK_SET
);
2129 pimg
->pending
= (ch
== 'M' ? img_MOVE
: img_LINE
) + 4;
2134 img_errno
= IMG_BADFORMAT
;
2139 /* CMAP .xyz file */
2144 if (pimg
->pending
) {
2145 /* pending MOVE or LINE or LABEL or STOP */
2146 int r
= pimg
->pending
- 4;
2147 /* Set label to empty - don't use "" as we adjust label relative
2148 * to label_buf when label_buf is reallocated. */
2149 pimg
->label
= pimg
->label_buf
+ strlen(pimg
->label_buf
);
2151 if (r
== img_LABEL
) {
2153 read_xyz_shot_coords(p
, pimg
->label_buf
+ 16);
2154 subtract_xyz_shot_deltas(p
, pimg
->label_buf
+ 16);
2155 pimg
->pending
= img_STOP
+ 4;
2161 if (r
== img_STOP
) {
2163 read_xyz_shot_coords(p
, pimg
->label_buf
+ 16);
2170 pimg
->label
= pimg
->label_buf
;
2173 if (feof(pimg
->fh
)) return img_STOP
;
2174 line
= getline_alloc(pimg
->fh
);
2176 img_errno
= IMG_OUTOFMEMORY
;
2179 } while (line
[0] == ' ' || line
[0] == '\0');
2180 if (line
[0] == '\x1a') return img_STOP
;
2183 if (pimg
->version
== VERSION_CMAP_STATION
) {
2184 /* station variant */
2187 img_errno
= IMG_BADFORMAT
;
2190 memcpy(pimg
->label
, line
, 6);
2191 q
= (char *)memchr(pimg
->label
, ' ', 6);
2192 if (!q
) q
= pimg
->label
+ 6;
2195 read_xyz_station_coords(p
, line
);
2197 /* FIXME: look at prev for lines (line + 32, 5) */
2198 /* FIXME: duplicate stations... */
2201 /* Shot variant (VERSION_CMAP_SHOT) */
2202 char old
[8], new_
[8];
2205 img_errno
= IMG_BADFORMAT
;
2209 memcpy(old
, line
, 7);
2210 q
= (char *)memchr(old
, ' ', 7);
2211 if (!q
) q
= old
+ 7;
2214 memcpy(new_
, line
+ 7, 7);
2215 q
= (char *)memchr(new_
, ' ', 7);
2216 if (!q
) q
= new_
+ 7;
2219 pimg
->flags
= img_SFLAG_UNDERGROUND
;
2221 if (strcmp(old
, new_
) == 0) {
2222 pimg
->pending
= img_MOVE
+ 4;
2223 read_xyz_shot_coords(p
, line
);
2224 strcpy(pimg
->label
, new_
);
2229 if (strcmp(old
, pimg
->label
) == 0) {
2230 pimg
->pending
= img_LINE
+ 4;
2231 read_xyz_shot_coords(p
, line
);
2232 strcpy(pimg
->label
, new_
);
2237 pimg
->pending
= img_LABEL
+ 4;
2238 read_xyz_shot_coords(p
, line
);
2239 strcpy(pimg
->label
, new_
);
2240 memcpy(pimg
->label
+ 16, line
, 70);
2249 write_coord(FILE *fh
, double x
, double y
, double z
)
2253 static INT32_T X_
, Y_
, Z_
;
2254 INT32_T X
= my_lround(x
* 100.0);
2255 INT32_T Y
= my_lround(y
* 100.0);
2256 INT32_T Z
= my_lround(z
* 100.0);
2264 X_
= X
; Y_
= Y
; Z_
= Z
;
2268 write_v3label(img
*pimg
, int opt
, const char *s
)
2272 /* find length of common prefix */
2274 for (len
= 0; s
[len
] == pimg
->label_buf
[len
] && s
[len
] != '\0'; len
++) {
2275 if (s
[len
] == '.') dot
= len
+ 1;
2278 SVX_ASSERT(len
<= pimg
->label_len
);
2279 n
= pimg
->label_len
- len
;
2281 if (pimg
->label_len
) PUTC(0, pimg
->fh
);
2282 } else if (n
<= 16) {
2283 if (n
) PUTC(n
+ 15, pimg
->fh
);
2284 } else if (dot
== 0) {
2285 if (pimg
->label_len
) PUTC(0, pimg
->fh
);
2288 const char *p
= pimg
->label_buf
+ dot
;
2290 for (len
= pimg
->label_len
- dot
- 17; len
; len
--) {
2291 if (*p
++ == '.') n
++;
2297 if (pimg
->label_len
) PUTC(0, pimg
->fh
);
2302 n
= strlen(s
+ len
);
2303 PUTC(opt
, pimg
->fh
);
2306 } else if (n
< 0xffff + 0xfe) {
2307 PUTC(0xfe, pimg
->fh
);
2308 put16((short)(n
- 0xfe), pimg
->fh
);
2310 PUTC(0xff, pimg
->fh
);
2313 fwrite(s
+ len
, n
, 1, pimg
->fh
);
2316 pimg
->label_len
= n
;
2317 if (!check_label_space(pimg
, n
+ 1))
2318 return 0; /* FIXME: distinguish out of memory... */
2319 memcpy(pimg
->label_buf
+ len
, s
+ len
, n
- len
+ 1);
2321 return !ferror(pimg
->fh
);
2325 write_v8label(img
*pimg
, int opt
, int common_flag
, size_t common_val
,
2328 size_t len
, del
, add
;
2330 /* find length of common prefix */
2331 for (len
= 0; s
[len
] == pimg
->label_buf
[len
] && s
[len
] != '\0'; len
++) {
2334 SVX_ASSERT(len
<= pimg
->label_len
);
2335 del
= pimg
->label_len
- len
;
2336 add
= strlen(s
+ len
);
2338 if (add
== common_val
&& del
== common_val
) {
2339 PUTC(opt
| common_flag
, pimg
->fh
);
2341 PUTC(opt
, pimg
->fh
);
2342 if (del
<= 15 && add
<= 15 && (del
|| add
)) {
2343 PUTC((del
<< 4) | add
, pimg
->fh
);
2345 PUTC(0x00, pimg
->fh
);
2347 PUTC(del
, pimg
->fh
);
2349 PUTC(0xff, pimg
->fh
);
2350 put32(del
, pimg
->fh
);
2353 PUTC(add
, pimg
->fh
);
2355 PUTC(0xff, pimg
->fh
);
2356 put32(add
, pimg
->fh
);
2362 fwrite(s
+ len
, add
, 1, pimg
->fh
);
2364 pimg
->label_len
= len
+ add
;
2365 if (add
> del
&& !check_label_space(pimg
, pimg
->label_len
+ 1))
2366 return 0; /* FIXME: distinguish out of memory... */
2368 memcpy(pimg
->label_buf
+ len
, s
+ len
, add
+ 1);
2370 return !ferror(pimg
->fh
);
2374 img_write_item_date_new(img
*pimg
)
2377 /* Only write dates when they've changed. */
2378 #if IMG_API_VERSION == 0
2379 if (pimg
->date1
== pimg
->olddate1
&& pimg
->date2
== pimg
->olddate2
)
2382 same
= (pimg
->date1
== pimg
->date2
);
2383 unset
= (pimg
->date1
== 0);
2384 #else /* IMG_API_VERSION == 1 */
2385 if (pimg
->days1
== pimg
->olddays1
&& pimg
->days2
== pimg
->olddays2
)
2388 same
= (pimg
->days1
== pimg
->days2
);
2389 unset
= (pimg
->days1
== -1);
2394 PUTC(0x10, pimg
->fh
);
2396 PUTC(0x11, pimg
->fh
);
2397 #if IMG_API_VERSION == 0
2398 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2399 #else /* IMG_API_VERSION == 1 */
2400 put16(pimg
->days1
, pimg
->fh
);
2404 #if IMG_API_VERSION == 0
2405 int diff
= (pimg
->date2
- pimg
->date1
) / 86400;
2406 if (diff
> 0 && diff
<= 256) {
2407 PUTC(0x12, pimg
->fh
);
2408 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2409 PUTC(diff
- 1, pimg
->fh
);
2411 PUTC(0x13, pimg
->fh
);
2412 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2413 put16(pimg
->date2
/ 86400 + 25567, pimg
->fh
);
2415 #else /* IMG_API_VERSION == 1 */
2416 int diff
= pimg
->days2
- pimg
->days1
;
2417 if (diff
> 0 && diff
<= 256) {
2418 PUTC(0x12, pimg
->fh
);
2419 put16(pimg
->days1
, pimg
->fh
);
2420 PUTC(diff
- 1, pimg
->fh
);
2422 PUTC(0x13, pimg
->fh
);
2423 put16(pimg
->days1
, pimg
->fh
);
2424 put16(pimg
->days2
, pimg
->fh
);
2428 #if IMG_API_VERSION == 0
2429 pimg
->olddate1
= pimg
->date1
;
2430 pimg
->olddate2
= pimg
->date2
;
2431 #else /* IMG_API_VERSION == 1 */
2432 pimg
->olddays1
= pimg
->days1
;
2433 pimg
->olddays2
= pimg
->days2
;
2438 img_write_item_date(img
*pimg
)
2441 /* Only write dates when they've changed. */
2442 #if IMG_API_VERSION == 0
2443 if (pimg
->date1
== pimg
->olddate1
&& pimg
->date2
== pimg
->olddate2
)
2446 same
= (pimg
->date1
== pimg
->date2
);
2447 unset
= (pimg
->date1
== 0);
2448 #else /* IMG_API_VERSION == 1 */
2449 if (pimg
->days1
== pimg
->olddays1
&& pimg
->days2
== pimg
->olddays2
)
2452 same
= (pimg
->days1
== pimg
->days2
);
2453 unset
= (pimg
->days1
== -1);
2457 if (img_output_version
< 7) {
2458 PUTC(0x20, pimg
->fh
);
2459 #if IMG_API_VERSION == 0
2460 put32(pimg
->date1
, pimg
->fh
);
2461 #else /* IMG_API_VERSION == 1 */
2462 put32((pimg
->days1
- 25567) * 86400, pimg
->fh
);
2466 PUTC(0x24, pimg
->fh
);
2468 PUTC(0x20, pimg
->fh
);
2469 #if IMG_API_VERSION == 0
2470 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2471 #else /* IMG_API_VERSION == 1 */
2472 put16(pimg
->days1
, pimg
->fh
);
2477 if (img_output_version
< 7) {
2478 PUTC(0x21, pimg
->fh
);
2479 #if IMG_API_VERSION == 0
2480 put32(pimg
->date1
, pimg
->fh
);
2481 put32(pimg
->date2
, pimg
->fh
);
2482 #else /* IMG_API_VERSION == 1 */
2483 put32((pimg
->days1
- 25567) * 86400, pimg
->fh
);
2484 put32((pimg
->days2
- 25567) * 86400, pimg
->fh
);
2487 #if IMG_API_VERSION == 0
2488 int diff
= (pimg
->date2
- pimg
->date1
) / 86400;
2489 if (diff
> 0 && diff
<= 256) {
2490 PUTC(0x21, pimg
->fh
);
2491 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2492 PUTC(diff
- 1, pimg
->fh
);
2494 PUTC(0x23, pimg
->fh
);
2495 put16(pimg
->date1
/ 86400 + 25567, pimg
->fh
);
2496 put16(pimg
->date2
/ 86400 + 25567, pimg
->fh
);
2498 #else /* IMG_API_VERSION == 1 */
2499 int diff
= pimg
->days2
- pimg
->days1
;
2500 if (diff
> 0 && diff
<= 256) {
2501 PUTC(0x21, pimg
->fh
);
2502 put16(pimg
->days1
, pimg
->fh
);
2503 PUTC(diff
- 1, pimg
->fh
);
2505 PUTC(0x23, pimg
->fh
);
2506 put16(pimg
->days1
, pimg
->fh
);
2507 put16(pimg
->days2
, pimg
->fh
);
2512 #if IMG_API_VERSION == 0
2513 pimg
->olddate1
= pimg
->date1
;
2514 pimg
->olddate2
= pimg
->date2
;
2515 #else /* IMG_API_VERSION == 1 */
2516 pimg
->olddays1
= pimg
->days1
;
2517 pimg
->olddays2
= pimg
->days2
;
2522 img_write_item_new(img
*pimg
, int code
, int flags
, const char *s
,
2523 double x
, double y
, double z
);
2525 img_write_item_v3to7(img
*pimg
, int code
, int flags
, const char *s
,
2526 double x
, double y
, double z
);
2528 img_write_item_ancient(img
*pimg
, int code
, int flags
, const char *s
,
2529 double x
, double y
, double z
);
2532 img_write_item(img
*pimg
, int code
, int flags
, const char *s
,
2533 double x
, double y
, double z
)
2536 if (pimg
->version
>= 8) {
2537 img_write_item_new(pimg
, code
, flags
, s
, x
, y
, z
);
2538 } else if (pimg
->version
>= 3) {
2539 img_write_item_v3to7(pimg
, code
, flags
, s
, x
, y
, z
);
2541 img_write_item_ancient(pimg
, code
, flags
, s
, x
, y
, z
);
2546 img_write_item_new(img
*pimg
, int code
, int flags
, const char *s
,
2547 double x
, double y
, double z
)
2551 write_v8label(pimg
, 0x80 | flags
, 0, -1, s
);
2554 INT32_T l
, r
, u
, d
, max_dim
;
2555 img_write_item_date_new(pimg
);
2556 l
= (INT32_T
)my_lround(pimg
->l
* 100.0);
2557 r
= (INT32_T
)my_lround(pimg
->r
* 100.0);
2558 u
= (INT32_T
)my_lround(pimg
->u
* 100.0);
2559 d
= (INT32_T
)my_lround(pimg
->d
* 100.0);
2564 max_dim
= max(max(l
, r
), max(u
, d
));
2565 flags
= (flags
& img_XFLAG_END
) ? 1 : 0;
2566 if (max_dim
>= 32768) flags
|= 2;
2567 write_v8label(pimg
, 0x30 | flags
, 0, -1, s
);
2569 /* Big passage! Need to use 4 bytes. */
2586 img_write_item_date_new(pimg
);
2587 if (pimg
->style
!= pimg
->oldstyle
) {
2588 switch (pimg
->style
) {
2589 case img_STYLE_NORMAL
:
2590 case img_STYLE_DIVING
:
2591 case img_STYLE_CARTESIAN
:
2592 case img_STYLE_CYLPOLAR
:
2593 case img_STYLE_NOSURVEY
:
2594 PUTC(pimg
->style
, pimg
->fh
);
2597 pimg
->oldstyle
= pimg
->style
;
2599 write_v8label(pimg
, 0x40 | flags
, 0x20, 0x00, s
? s
: "");
2601 default: /* ignore for now */
2604 write_coord(pimg
->fh
, x
, y
, z
);
2608 img_write_item_v3to7(img
*pimg
, int code
, int flags
, const char *s
,
2609 double x
, double y
, double z
)
2613 write_v3label(pimg
, 0x40 | flags
, s
);
2616 INT32_T l
, r
, u
, d
, max_dim
;
2617 /* Need at least version 5 for img_XSECT. */
2618 if (pimg
->version
< 5) return;
2619 img_write_item_date(pimg
);
2620 l
= (INT32_T
)my_lround(pimg
->l
* 100.0);
2621 r
= (INT32_T
)my_lround(pimg
->r
* 100.0);
2622 u
= (INT32_T
)my_lround(pimg
->u
* 100.0);
2623 d
= (INT32_T
)my_lround(pimg
->d
* 100.0);
2628 max_dim
= max(max(l
, r
), max(u
, d
));
2629 flags
= (flags
& img_XFLAG_END
) ? 1 : 0;
2630 if (max_dim
>= 32768) flags
|= 2;
2631 write_v3label(pimg
, 0x30 | flags
, s
);
2633 /* Big passage! Need to use 4 bytes. */
2650 if (pimg
->version
>= 4) {
2651 img_write_item_date(pimg
);
2653 write_v3label(pimg
, 0x80 | flags
, s
? s
: "");
2655 default: /* ignore for now */
2658 write_coord(pimg
->fh
, x
, y
, z
);
2662 img_write_item_ancient(img
*pimg
, int code
, int flags
, const char *s
,
2663 double x
, double y
, double z
)
2667 SVX_ASSERT(pimg
->version
> 0);
2670 if (pimg
->version
== 1) {
2671 /* put a move before each label */
2672 img_write_item_ancient(pimg
, img_MOVE
, 0, NULL
, x
, y
, z
);
2674 fputsnl(s
, pimg
->fh
);
2678 if (len
> 255 || strchr(s
, '\n')) {
2679 /* long label - not in early incarnations of v2 format, but few
2680 * 3d files will need these, so better not to force incompatibility
2681 * with a new version I think... */
2683 PUTC(flags
, pimg
->fh
);
2684 put32(len
, pimg
->fh
);
2687 PUTC(0x40 | (flags
& 0x3f), pimg
->fh
);
2688 fputsnl(s
, pimg
->fh
);
2696 if (pimg
->version
> 1) {
2697 opt
= 0x80 | (flags
& 0x3f);
2702 default: /* ignore for now */
2705 if (pimg
->version
== 1) {
2706 put32(opt
, pimg
->fh
);
2708 if (opt
) PUTC(opt
, pimg
->fh
);
2710 write_coord(pimg
->fh
, x
, y
, z
);
2713 /* Write error information for the current traverse
2714 * n_legs is the number of legs in the traverse
2715 * length is the traverse length (in m)
2716 * E is the ratio of the observed misclosure to the theoretical one
2717 * H is the ratio of the observed horizontal misclosure to the theoretical one
2718 * V is the ratio of the observed vertical misclosure to the theoretical one
2721 img_write_errors(img
*pimg
, int n_legs
, double length
,
2722 double E
, double H
, double V
)
2724 PUTC((pimg
->version
>= 8 ? 0x1f : 0x22), pimg
->fh
);
2725 put32(n_legs
, pimg
->fh
);
2726 put32((INT32_T
)my_lround(length
* 100.0), pimg
->fh
);
2727 put32((INT32_T
)my_lround(E
* 100.0), pimg
->fh
);
2728 put32((INT32_T
)my_lround(H
* 100.0), pimg
->fh
);
2729 put32((INT32_T
)my_lround(V
* 100.0), pimg
->fh
);
2733 img_close(img
*pimg
)
2739 osfree(pimg
->survey
);
2740 osfree(pimg
->title
);
2742 osfree(pimg
->datestamp
);
2744 /* write end of data marker */
2745 switch (pimg
->version
) {
2747 put32((INT32_T
)-1, pimg
->fh
);
2750 if (pimg
->version
<= 7 ?
2751 (pimg
->label_len
!= 0) :
2752 (pimg
->style
!= img_STYLE_NORMAL
)) {
2761 if (ferror(pimg
->fh
)) result
= 0;
2762 if (pimg
->close_func
&& pimg
->close_func(pimg
->fh
))
2764 if (!result
) img_errno
= pimg
->fRead
? IMG_READERROR
: IMG_WRITEERROR
;
2766 osfree(pimg
->label_buf
);
2767 osfree(pimg
->filename_opened
);