2779 lines
70 KiB
C
2779 lines
70 KiB
C
|
/* img.c
|
||
|
* Routines for reading and writing Survex ".3d" image files
|
||
|
* Copyright (C) 1993-2021 Olly Betts
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
# include <config.h>
|
||
|
#endif
|
||
|
|
||
|
#include <ctype.h>
|
||
|
#include <errno.h>
|
||
|
#include <limits.h>
|
||
|
#include <locale.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
#include "img.h"
|
||
|
|
||
|
#define TIMENA "?"
|
||
|
#ifdef IMG_HOSTED
|
||
|
# define INT32_T int32_t
|
||
|
# define UINT32_T uint32_t
|
||
|
# include "debug.h"
|
||
|
# include "filelist.h"
|
||
|
# include "filename.h"
|
||
|
# include "message.h"
|
||
|
# include "useful.h"
|
||
|
# define TIMEFMT msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107)
|
||
|
#else
|
||
|
# ifdef HAVE_STDINT_H
|
||
|
# include <stdint.h>
|
||
|
# define INT32_T int32_t
|
||
|
# define UINT32_T uint32_t
|
||
|
# else
|
||
|
# include <limits.h>
|
||
|
# if INT_MAX >= 2147483647
|
||
|
# define INT32_T int
|
||
|
# define UINT32_T unsigned
|
||
|
# else
|
||
|
# define INT32_T long
|
||
|
# define UINT32_T unsigned long
|
||
|
# endif
|
||
|
# endif
|
||
|
# define TIMEFMT "%a,%Y.%m.%d %H:%M:%S %Z"
|
||
|
# define EXT_SVX_3D "3d"
|
||
|
# define EXT_SVX_POS "pos"
|
||
|
# define FNM_SEP_EXT '.'
|
||
|
# define METRES_PER_FOOT 0.3048 /* exact value */
|
||
|
# define xosmalloc(L) malloc((L))
|
||
|
# define xosrealloc(L,S) realloc((L),(S))
|
||
|
# define osfree(P) free((P))
|
||
|
# define osnew(T) (T*)malloc(sizeof(T))
|
||
|
|
||
|
/* in IMG_HOSTED mode, this tests if a filename refers to a directory */
|
||
|
# define fDirectory(X) 0
|
||
|
/* open file FNM with mode MODE, maybe using path PTH and/or extension EXT */
|
||
|
/* path isn't used in img.c, but EXT is */
|
||
|
# define fopenWithPthAndExt(PTH,FNM,EXT,MODE,X) \
|
||
|
((*(X) = NULL), fopen(FNM,MODE))
|
||
|
# ifndef PUTC
|
||
|
# define PUTC(C, FH) putc(C, FH)
|
||
|
# endif
|
||
|
# ifndef GETC
|
||
|
# define GETC(FH) getc(FH)
|
||
|
# endif
|
||
|
# define fputsnl(S, FH) (fputs((S), (FH)) == EOF ? EOF : putc('\n', (FH)))
|
||
|
# define SVX_ASSERT(X)
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
# include <algorithm>
|
||
|
using std::max;
|
||
|
using std::min;
|
||
|
#else
|
||
|
/* Return max/min of two numbers. */
|
||
|
/* May be defined already (e.g. by Borland C in stdlib.h) */
|
||
|
/* NB Bad news if X or Y has side-effects... */
|
||
|
# ifndef max
|
||
|
# define max(X, Y) ((X) > (Y) ? (X) : (Y))
|
||
|
# endif
|
||
|
# ifndef min
|
||
|
# define min(X, Y) ((X) < (Y) ? (X) : (Y))
|
||
|
# endif
|
||
|
#endif
|
||
|
|
||
|
static INT32_T
|
||
|
get32(FILE *fh)
|
||
|
{
|
||
|
UINT32_T w = GETC(fh);
|
||
|
w |= (UINT32_T)GETC(fh) << 8l;
|
||
|
w |= (UINT32_T)GETC(fh) << 16l;
|
||
|
w |= (UINT32_T)GETC(fh) << 24l;
|
||
|
return (INT32_T)w;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
put32(UINT32_T w, FILE *fh)
|
||
|
{
|
||
|
PUTC((char)(w), fh);
|
||
|
PUTC((char)(w >> 8l), fh);
|
||
|
PUTC((char)(w >> 16l), fh);
|
||
|
PUTC((char)(w >> 24l), fh);
|
||
|
}
|
||
|
|
||
|
static short
|
||
|
get16(FILE *fh)
|
||
|
{
|
||
|
UINT32_T w = GETC(fh);
|
||
|
w |= (UINT32_T)GETC(fh) << 8l;
|
||
|
return (short)w;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
put16(short word, FILE *fh)
|
||
|
{
|
||
|
unsigned short w = (unsigned short)word;
|
||
|
PUTC((char)(w), fh);
|
||
|
PUTC((char)(w >> 8l), fh);
|
||
|
}
|
||
|
|
||
|
static char *
|
||
|
baseleaf_from_fnm(const char *fnm)
|
||
|
{
|
||
|
const char *p;
|
||
|
const char *q;
|
||
|
char * res;
|
||
|
size_t len;
|
||
|
|
||
|
p = fnm;
|
||
|
q = strrchr(p, '/');
|
||
|
if (q) p = q + 1;
|
||
|
q = strrchr(p, '\\');
|
||
|
if (q) p = q + 1;
|
||
|
|
||
|
q = strrchr(p, FNM_SEP_EXT);
|
||
|
if (q) len = (const char *)q - p; else len = strlen(p);
|
||
|
|
||
|
res = (char *)xosmalloc(len + 1);
|
||
|
if (!res) return NULL;
|
||
|
memcpy(res, p, len);
|
||
|
res[len] = '\0';
|
||
|
return res;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static char * my_strdup(const char *str);
|
||
|
|
||
|
static time_t
|
||
|
mktime_with_tz(struct tm * tm, const char * tz)
|
||
|
{
|
||
|
time_t r;
|
||
|
char * old_tz = getenv("TZ");
|
||
|
#ifdef _MSC_VER
|
||
|
if (old_tz) {
|
||
|
old_tz = my_strdup(old_tz);
|
||
|
if (!old_tz)
|
||
|
return (time_t)-1;
|
||
|
}
|
||
|
if (_putenv_s("TZ", tz) != 0) {
|
||
|
osfree(old_tz);
|
||
|
return (time_t)-1;
|
||
|
}
|
||
|
#elif defined HAVE_SETENV
|
||
|
if (old_tz) {
|
||
|
old_tz = my_strdup(old_tz);
|
||
|
if (!old_tz)
|
||
|
return (time_t)-1;
|
||
|
}
|
||
|
if (setenv("TZ", tz, 1) < 0) {
|
||
|
osfree(old_tz);
|
||
|
return (time_t)-1;
|
||
|
}
|
||
|
#else
|
||
|
char * p;
|
||
|
if (old_tz) {
|
||
|
size_t len = strlen(old_tz) + 1;
|
||
|
p = (char *)xosmalloc(len + 3);
|
||
|
if (!p)
|
||
|
return (time_t)-1;
|
||
|
memcpy(p, "TZ=", 3);
|
||
|
memcpy(p + 3, tz, len);
|
||
|
old_tz = p;
|
||
|
}
|
||
|
p = (char *)xosmalloc(strlen(tz) + 4);
|
||
|
if (!p) {
|
||
|
osfree(old_tz);
|
||
|
return (time_t)-1;
|
||
|
}
|
||
|
memcpy(p, "TZ=", 3);
|
||
|
strcpy(p + 3, tz);
|
||
|
if (putenv(p) != 0) {
|
||
|
osfree(p);
|
||
|
osfree(old_tz);
|
||
|
return (time_t)-1;
|
||
|
}
|
||
|
#define CLEANUP() osfree(p)
|
||
|
#endif
|
||
|
tzset();
|
||
|
r = mktime(tm);
|
||
|
if (old_tz) {
|
||
|
#ifdef _MSC_VER
|
||
|
_putenv_s("TZ", old_tz);
|
||
|
#elif !defined HAVE_SETENV
|
||
|
putenv(old_tz);
|
||
|
#else
|
||
|
setenv("TZ", old_tz, 1);
|
||
|
#endif
|
||
|
osfree(old_tz);
|
||
|
} else {
|
||
|
#ifdef _MSC_VER
|
||
|
_putenv_s("TZ", "");
|
||
|
#elif !defined HAVE_UNSETENV
|
||
|
putenv((char*)"TZ");
|
||
|
#else
|
||
|
unsetenv("TZ");
|
||
|
#endif
|
||
|
}
|
||
|
#ifdef CLEANUP
|
||
|
CLEANUP();
|
||
|
#undef CLEANUP
|
||
|
#endif
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
static unsigned short
|
||
|
getu16(FILE *fh)
|
||
|
{
|
||
|
return (unsigned short)get16(fh);
|
||
|
}
|
||
|
|
||
|
#include <math.h>
|
||
|
|
||
|
#if !defined HAVE_LROUND && !defined HAVE_DECL_LROUND
|
||
|
/* The autoconf tests are not in use, but C99 and C++11 both added lround(),
|
||
|
* so set HAVE_LROUND and HAVE_DECL_LROUND conservatively based on the language
|
||
|
* standard version the compiler claims to support. */
|
||
|
# if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \
|
||
|
(defined __cplusplus && __cplusplus >= 201103L)
|
||
|
# define HAVE_LROUND 1
|
||
|
# define HAVE_DECL_LROUND 1
|
||
|
# endif
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_LROUND
|
||
|
# if defined HAVE_DECL_LROUND && !HAVE_DECL_LROUND
|
||
|
/* On older systems, the prototype may be missing. */
|
||
|
extern long lround(double);
|
||
|
# endif
|
||
|
# define my_lround lround
|
||
|
#else
|
||
|
static long
|
||
|
my_lround(double x) {
|
||
|
return (x >= 0.0) ? (long)(x + 0.5) : -(long)(0.5 - x);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* portable case insensitive string compare */
|
||
|
#if defined(strcasecmp) || defined(HAVE_STRCASECMP)
|
||
|
# define my_strcasecmp strcasecmp
|
||
|
#else
|
||
|
static int my_strcasecmp(const char *s1, const char *s2) {
|
||
|
unsigned char c1, c2;
|
||
|
do {
|
||
|
c1 = *s1++;
|
||
|
c2 = *s2++;
|
||
|
} while (c1 && toupper(c1) == toupper(c2));
|
||
|
/* now calculate real difference */
|
||
|
return c1 - c2;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
unsigned int img_output_version = IMG_VERSION_MAX;
|
||
|
|
||
|
static img_errcode img_errno = IMG_NONE;
|
||
|
|
||
|
#define FILEID "Survex 3D Image File"
|
||
|
|
||
|
#define EXT_PLT "plt"
|
||
|
#define EXT_PLF "plf"
|
||
|
|
||
|
/* Attempt to string paste to ensure we are passed a literal string */
|
||
|
#define LITLEN(S) (sizeof(S"") - 1)
|
||
|
|
||
|
/* Fake "version numbers" for non-3d formats we can read. */
|
||
|
#define VERSION_CMAP_SHOT -4
|
||
|
#define VERSION_CMAP_STATION -3
|
||
|
#define VERSION_COMPASS_PLT -2
|
||
|
#define VERSION_SURVEX_POS -1
|
||
|
|
||
|
static char *
|
||
|
my_strdup(const char *str)
|
||
|
{
|
||
|
char *p;
|
||
|
size_t len = strlen(str) + 1;
|
||
|
p = (char *)xosmalloc(len);
|
||
|
if (p) memcpy(p, str, len);
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
#define getline_alloc(FH) getline_alloc_len(FH, NULL)
|
||
|
|
||
|
static char *
|
||
|
getline_alloc_len(FILE *fh, size_t * p_len)
|
||
|
{
|
||
|
int ch;
|
||
|
size_t i = 0;
|
||
|
size_t len = 16;
|
||
|
char *buf = (char *)xosmalloc(len);
|
||
|
if (!buf) return NULL;
|
||
|
|
||
|
ch = GETC(fh);
|
||
|
while (ch != '\n' && ch != '\r' && ch != EOF) {
|
||
|
buf[i++] = ch;
|
||
|
if (i == len - 1) {
|
||
|
char *p;
|
||
|
len += len;
|
||
|
p = (char *)xosrealloc(buf, len);
|
||
|
if (!p) {
|
||
|
osfree(buf);
|
||
|
return NULL;
|
||
|
}
|
||
|
buf = p;
|
||
|
}
|
||
|
ch = GETC(fh);
|
||
|
}
|
||
|
if (ch == '\n' || ch == '\r') {
|
||
|
int otherone = ch ^ ('\n' ^ '\r');
|
||
|
ch = GETC(fh);
|
||
|
/* if it's not the other eol character, put it back */
|
||
|
if (ch != otherone) ungetc(ch, fh);
|
||
|
}
|
||
|
buf[i] = '\0';
|
||
|
if (p_len) *p_len = i;
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
img_errcode
|
||
|
img_error(void)
|
||
|
{
|
||
|
return img_errno;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
check_label_space(img *pimg, size_t len)
|
||
|
{
|
||
|
if (len > pimg->buf_len) {
|
||
|
char *b = (char *)xosrealloc(pimg->label_buf, len);
|
||
|
if (!b) return 0;
|
||
|
pimg->label = (pimg->label - pimg->label_buf) + b;
|
||
|
pimg->label_buf = b;
|
||
|
pimg->buf_len = len;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Check if a station name should be included. */
|
||
|
static int
|
||
|
stn_included(img *pimg)
|
||
|
{
|
||
|
if (!pimg->survey_len) return 1;
|
||
|
size_t l = pimg->survey_len;
|
||
|
const char *s = pimg->label_buf;
|
||
|
if (strncmp(pimg->survey, s, l + 1) != 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
pimg->label += l + 1;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Check if a survey name should be included. */
|
||
|
static int
|
||
|
survey_included(img *pimg)
|
||
|
{
|
||
|
if (!pimg->survey_len) return 1;
|
||
|
size_t l = pimg->survey_len;
|
||
|
const char *s = pimg->label_buf;
|
||
|
if (strncmp(pimg->survey, s, l) != 0 ||
|
||
|
!(s[l] == '.' || s[l] == '\0')) {
|
||
|
return 0;
|
||
|
}
|
||
|
pimg->label += l;
|
||
|
/* skip the dot if there */
|
||
|
if (*pimg->label) pimg->label++;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Check if a survey name in a buffer should be included.
|
||
|
*
|
||
|
* For "foreign" formats which just have one level of surveys.
|
||
|
*/
|
||
|
static int
|
||
|
buf_included(img *pimg, const char *buf, size_t len)
|
||
|
{
|
||
|
return pimg->survey_len == len && strncmp(buf, pimg->survey, len) == 0;
|
||
|
}
|
||
|
|
||
|
#define has_ext(F,L,E) ((L) > LITLEN(E) + 1 &&\
|
||
|
(F)[(L) - LITLEN(E) - 1] == FNM_SEP_EXT &&\
|
||
|
my_strcasecmp((F) + (L) - LITLEN(E), E) == 0)
|
||
|
|
||
|
img *
|
||
|
img_open_survey(const char *fnm, const char *survey)
|
||
|
{
|
||
|
img *pimg;
|
||
|
FILE *fh;
|
||
|
char* filename_opened = NULL;
|
||
|
|
||
|
if (fDirectory(fnm)) {
|
||
|
img_errno = IMG_DIRECTORY;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
fh = fopenWithPthAndExt("", fnm, EXT_SVX_3D, "rb", &filename_opened);
|
||
|
pimg = img_read_stream_survey(fh, fclose,
|
||
|
filename_opened ? filename_opened : fnm,
|
||
|
survey);
|
||
|
if (pimg) {
|
||
|
pimg->filename_opened = filename_opened;
|
||
|
} else {
|
||
|
osfree(filename_opened);
|
||
|
}
|
||
|
return pimg;
|
||
|
}
|
||
|
|
||
|
img *
|
||
|
img_read_stream_survey(FILE *stream, int (*close_func)(FILE*),
|
||
|
const char *fnm,
|
||
|
const char *survey)
|
||
|
{
|
||
|
img *pimg;
|
||
|
size_t len;
|
||
|
char buf[LITLEN(FILEID) + 9];
|
||
|
int ch;
|
||
|
|
||
|
if (stream == NULL) {
|
||
|
img_errno = IMG_FILENOTFOUND;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pimg = osnew(img);
|
||
|
if (pimg == NULL) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
if (close_func) close_func(stream);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pimg->fh = stream;
|
||
|
pimg->close_func = close_func;
|
||
|
|
||
|
pimg->buf_len = 257;
|
||
|
pimg->label_buf = (char *)xosmalloc(pimg->buf_len);
|
||
|
if (!pimg->label_buf) {
|
||
|
if (pimg->close_func) pimg->close_func(pimg->fh);
|
||
|
osfree(pimg);
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pimg->fRead = 1; /* reading from this file */
|
||
|
img_errno = IMG_NONE;
|
||
|
|
||
|
pimg->flags = 0;
|
||
|
pimg->filename_opened = NULL;
|
||
|
|
||
|
/* for version >= 3 we use label_buf to store the prefix for reuse */
|
||
|
/* for VERSION_COMPASS_PLT, 0 value indicates we haven't
|
||
|
* entered a survey yet */
|
||
|
/* for VERSION_CMAP_SHOT, we store the last station here
|
||
|
* to detect whether we MOVE or LINE */
|
||
|
pimg->label_len = 0;
|
||
|
pimg->label_buf[0] = '\0';
|
||
|
|
||
|
pimg->survey = NULL;
|
||
|
pimg->survey_len = 0;
|
||
|
pimg->separator = '.';
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date1 = pimg->date2 = 0;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days1 = pimg->days2 = -1;
|
||
|
#endif
|
||
|
pimg->is_extended_elevation = 0;
|
||
|
|
||
|
pimg->style = pimg->oldstyle = img_STYLE_UNKNOWN;
|
||
|
|
||
|
pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
|
||
|
|
||
|
pimg->title = pimg->datestamp = pimg->cs = NULL;
|
||
|
pimg->datestamp_numeric = (time_t)-1;
|
||
|
|
||
|
if (survey) {
|
||
|
len = strlen(survey);
|
||
|
if (len) {
|
||
|
if (survey[len - 1] == '.') len--;
|
||
|
if (len) {
|
||
|
char *p;
|
||
|
pimg->survey = (char *)xosmalloc(len + 2);
|
||
|
if (!pimg->survey) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
memcpy(pimg->survey, survey, len);
|
||
|
/* Set title to leaf survey name */
|
||
|
pimg->survey[len] = '\0';
|
||
|
p = strrchr(pimg->survey, '.');
|
||
|
if (p) p++; else p = pimg->survey;
|
||
|
pimg->title = my_strdup(p);
|
||
|
if (!pimg->title) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
pimg->survey[len] = '.';
|
||
|
pimg->survey[len + 1] = '\0';
|
||
|
}
|
||
|
}
|
||
|
pimg->survey_len = len;
|
||
|
}
|
||
|
|
||
|
/* [VERSION_COMPASS_PLT, VERSION_CMAP_STATION, VERSION_CMAP_SHOT] pending
|
||
|
* IMG_LINE or IMG_MOVE - both have 4 added.
|
||
|
* [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
|
||
|
* [version 0] not in the middle of a 'LINE' command
|
||
|
* [version >= 3] not in the middle of turning a LINE into a MOVE
|
||
|
*/
|
||
|
pimg->pending = 0;
|
||
|
|
||
|
len = strlen(fnm);
|
||
|
if (has_ext(fnm, len, EXT_SVX_POS)) {
|
||
|
pos_file:
|
||
|
pimg->version = VERSION_SURVEX_POS;
|
||
|
if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
|
||
|
pimg->datestamp = my_strdup(TIMENA);
|
||
|
if (!pimg->datestamp) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
pimg->start = 0;
|
||
|
return pimg;
|
||
|
}
|
||
|
|
||
|
if (has_ext(fnm, len, EXT_PLT) || has_ext(fnm, len, EXT_PLF)) {
|
||
|
long fpos;
|
||
|
plt_file:
|
||
|
pimg->version = VERSION_COMPASS_PLT;
|
||
|
/* Spaces aren't legal in Compass station names, but dots are, so
|
||
|
* use space as the level separator */
|
||
|
pimg->separator = ' ';
|
||
|
pimg->start = 0;
|
||
|
if (!pimg->survey) pimg->title = baseleaf_from_fnm(fnm);
|
||
|
pimg->datestamp = my_strdup(TIMENA);
|
||
|
if (!pimg->datestamp) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
while (1) {
|
||
|
ch = GETC(pimg->fh);
|
||
|
switch (ch) {
|
||
|
case '\x1a':
|
||
|
fseek(pimg->fh, -1, SEEK_CUR);
|
||
|
/* FALL THRU */
|
||
|
case EOF:
|
||
|
pimg->start = ftell(pimg->fh);
|
||
|
return pimg;
|
||
|
case 'N': {
|
||
|
char *line, *q;
|
||
|
fpos = ftell(pimg->fh) - 1;
|
||
|
if (!pimg->survey) {
|
||
|
/* FIXME : if there's only one survey in the file, it'd be nice
|
||
|
* to use its description as the title here...
|
||
|
*/
|
||
|
ungetc('N', pimg->fh);
|
||
|
pimg->start = fpos;
|
||
|
return pimg;
|
||
|
}
|
||
|
line = getline_alloc(pimg->fh);
|
||
|
if (!line) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
len = 0;
|
||
|
while (line[len] > 32) ++len;
|
||
|
if (!buf_included(pimg, line, len)) {
|
||
|
osfree(line);
|
||
|
continue;
|
||
|
}
|
||
|
q = strchr(line + len, 'C');
|
||
|
if (q && q[1]) {
|
||
|
osfree(pimg->title);
|
||
|
pimg->title = my_strdup(q + 1);
|
||
|
} else if (!pimg->title) {
|
||
|
pimg->title = my_strdup(pimg->label);
|
||
|
}
|
||
|
osfree(line);
|
||
|
if (!pimg->title) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
if (!pimg->start) pimg->start = fpos;
|
||
|
fseek(pimg->fh, pimg->start, SEEK_SET);
|
||
|
return pimg;
|
||
|
}
|
||
|
case 'M': case 'D':
|
||
|
pimg->start = ftell(pimg->fh) - 1;
|
||
|
break;
|
||
|
}
|
||
|
while (ch != '\n' && ch != '\r') {
|
||
|
ch = GETC(pimg->fh);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Although these are often referred to as "CMAP .XYZ files", it seems
|
||
|
* that actually, the extension .XYZ isn't used, rather .SHT (shot
|
||
|
* variant, produced by CMAP v16 and later), .UNA (unadjusted) and
|
||
|
* .ADJ (adjusted) extensions are. Since img has long checked for
|
||
|
* .XYZ, we continue to do so in case anyone is relying on it.
|
||
|
*/
|
||
|
if (has_ext(fnm, len, "sht") ||
|
||
|
has_ext(fnm, len, "adj") ||
|
||
|
has_ext(fnm, len, "una") ||
|
||
|
has_ext(fnm, len, "xyz")) {
|
||
|
char *line;
|
||
|
xyz_file:
|
||
|
/* Spaces aren't legal in CMAP station names, but dots are, so
|
||
|
* use space as the level separator. */
|
||
|
pimg->separator = ' ';
|
||
|
line = getline_alloc(pimg->fh);
|
||
|
if (!line) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
/* There doesn't seem to be a spec for what happens after 1999 with cmap
|
||
|
* files, so this code allows for:
|
||
|
* * 21xx -> xx (up to 2150)
|
||
|
* * 21xx -> 1xx (up to 2199)
|
||
|
* * full year being specified instead of 2 digits
|
||
|
*/
|
||
|
len = strlen(line);
|
||
|
if (len > 59) {
|
||
|
/* Don't just truncate at column 59, allow for a > 2 digit year. */
|
||
|
char * p = strstr(line + len, "Page");
|
||
|
if (p) {
|
||
|
while (p > line && p[-1] == ' ')
|
||
|
--p;
|
||
|
*p = '\0';
|
||
|
len = p - line;
|
||
|
} else {
|
||
|
line[59] = '\0';
|
||
|
}
|
||
|
}
|
||
|
if (len > 45) {
|
||
|
/* YY/MM/DD HH:MM */
|
||
|
struct tm tm;
|
||
|
unsigned long v;
|
||
|
char * p;
|
||
|
pimg->datestamp = my_strdup(line + 45);
|
||
|
p = pimg->datestamp;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v <= 50) {
|
||
|
/* In the absence of a spec for cmap files, assume <= 50 means 21st
|
||
|
* century. */
|
||
|
v += 2000;
|
||
|
} else if (v < 200) {
|
||
|
/* Map 100-199 to 21st century. */
|
||
|
v += 1900;
|
||
|
}
|
||
|
if (v == ULONG_MAX || *p++ != '/')
|
||
|
goto bad_cmap_date;
|
||
|
tm.tm_year = v - 1900;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v < 1 || v > 12 || *p++ != '/')
|
||
|
goto bad_cmap_date;
|
||
|
tm.tm_mon = v - 1;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v < 1 || v > 31 || *p++ != ' ')
|
||
|
goto bad_cmap_date;
|
||
|
tm.tm_mday = v;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v >= 24 || *p++ != ':')
|
||
|
goto bad_cmap_date;
|
||
|
tm.tm_hour = v;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v >= 60)
|
||
|
goto bad_cmap_date;
|
||
|
tm.tm_min = v;
|
||
|
if (*p == ':') {
|
||
|
v = strtoul(p + 1, &p, 10);
|
||
|
if (v > 60)
|
||
|
goto bad_cmap_date;
|
||
|
tm.tm_sec = v;
|
||
|
} else {
|
||
|
tm.tm_sec = 0;
|
||
|
}
|
||
|
tm.tm_isdst = 0;
|
||
|
/* We have no indication of what timezone this timestamp is in. It's
|
||
|
* probably local time for whoever processed the data, so just assume
|
||
|
* UTC, which is at least fairly central in the possibilities.
|
||
|
*/
|
||
|
pimg->datestamp_numeric = mktime_with_tz(&tm, "");
|
||
|
} else {
|
||
|
pimg->datestamp = my_strdup(TIMENA);
|
||
|
}
|
||
|
bad_cmap_date:
|
||
|
if (strncmp(line, " Cave Survey Data Processed by CMAP ",
|
||
|
LITLEN(" Cave Survey Data Processed by CMAP ")) == 0) {
|
||
|
len = 0;
|
||
|
} else {
|
||
|
if (len > 45) {
|
||
|
line[45] = '\0';
|
||
|
len = 45;
|
||
|
}
|
||
|
while (len > 2 && line[len - 1] == ' ') --len;
|
||
|
if (len > 2) {
|
||
|
line[len] = '\0';
|
||
|
pimg->title = my_strdup(line + 2);
|
||
|
}
|
||
|
}
|
||
|
if (len <= 2) pimg->title = baseleaf_from_fnm(fnm);
|
||
|
osfree(line);
|
||
|
if (!pimg->datestamp || !pimg->title) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
line = getline_alloc(pimg->fh);
|
||
|
if (!line) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
if (line[0] != ' ' || (line[1] != 'S' && line[1] != 'O')) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
goto error;
|
||
|
}
|
||
|
if (line[1] == 'S') {
|
||
|
pimg->version = VERSION_CMAP_STATION;
|
||
|
} else {
|
||
|
pimg->version = VERSION_CMAP_SHOT;
|
||
|
}
|
||
|
osfree(line);
|
||
|
line = getline_alloc(pimg->fh);
|
||
|
if (!line) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
if (line[0] != ' ' || line[1] != '-') {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
goto error;
|
||
|
}
|
||
|
osfree(line);
|
||
|
pimg->start = ftell(pimg->fh);
|
||
|
return pimg;
|
||
|
}
|
||
|
|
||
|
if (fread(buf, LITLEN(FILEID) + 1, 1, pimg->fh) != 1 ||
|
||
|
memcmp(buf, FILEID"\n", LITLEN(FILEID) + 1) != 0) {
|
||
|
if (fread(buf + LITLEN(FILEID) + 1, 8, 1, pimg->fh) == 1 &&
|
||
|
memcmp(buf, FILEID"\r\nv0.01\r\n", LITLEN(FILEID) + 9) == 0) {
|
||
|
/* v0 3d file with DOS EOLs */
|
||
|
pimg->version = 0;
|
||
|
goto v03d;
|
||
|
}
|
||
|
rewind(pimg->fh);
|
||
|
if (buf[1] == ' ') {
|
||
|
if (buf[0] == ' ') {
|
||
|
/* Looks like a CMAP .xyz file ... */
|
||
|
goto xyz_file;
|
||
|
} else if (strchr("ZSNF", buf[0])) {
|
||
|
/* Looks like a Compass .plt file ... */
|
||
|
/* Almost certainly it'll start "Z " */
|
||
|
goto plt_file;
|
||
|
}
|
||
|
}
|
||
|
if (buf[0] == '(') {
|
||
|
/* Looks like a Survex .pos file ... */
|
||
|
goto pos_file;
|
||
|
}
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/* check file format version */
|
||
|
ch = GETC(pimg->fh);
|
||
|
pimg->version = 0;
|
||
|
if (tolower(ch) == 'b') {
|
||
|
/* binary file iff B/b prefix */
|
||
|
pimg->version = 1;
|
||
|
ch = GETC(pimg->fh);
|
||
|
}
|
||
|
if (ch != 'v') {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
goto error;
|
||
|
}
|
||
|
ch = GETC(pimg->fh);
|
||
|
if (ch == '0') {
|
||
|
if (fread(buf, 4, 1, pimg->fh) != 1 || memcmp(buf, ".01\n", 4) != 0) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
goto error;
|
||
|
}
|
||
|
/* nothing special to do */
|
||
|
} else if (pimg->version == 0) {
|
||
|
if (ch < '2' || ch > '0' + IMG_VERSION_MAX || GETC(pimg->fh) != '\n') {
|
||
|
img_errno = IMG_TOONEW;
|
||
|
goto error;
|
||
|
}
|
||
|
pimg->version = ch - '0';
|
||
|
} else {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
v03d:
|
||
|
{
|
||
|
size_t title_len;
|
||
|
char * title = getline_alloc_len(pimg->fh, &title_len);
|
||
|
if (pimg->version == 8 && title) {
|
||
|
/* We sneak in an extra field after a zero byte here, containing the
|
||
|
* specified coordinate system (if any). Older readers will just
|
||
|
* not see it (which is fine), and this trick avoids us having to
|
||
|
* bump the 3d format version.
|
||
|
*/
|
||
|
size_t real_len = strlen(title);
|
||
|
if (real_len != title_len) {
|
||
|
pimg->cs = my_strdup(title + real_len + 1);
|
||
|
}
|
||
|
}
|
||
|
if (!pimg->title) {
|
||
|
pimg->title = title;
|
||
|
} else {
|
||
|
osfree(title);
|
||
|
}
|
||
|
}
|
||
|
pimg->datestamp = getline_alloc(pimg->fh);
|
||
|
if (!pimg->title || !pimg->datestamp) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
error:
|
||
|
osfree(pimg->title);
|
||
|
osfree(pimg->cs);
|
||
|
osfree(pimg->datestamp);
|
||
|
osfree(pimg->filename_opened);
|
||
|
if (pimg->close_func) pimg->close_func(pimg->fh);
|
||
|
osfree(pimg);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (pimg->version >= 8) {
|
||
|
int flags = GETC(pimg->fh);
|
||
|
if (flags & img_FFLAG_EXTENDED) pimg->is_extended_elevation = 1;
|
||
|
} else {
|
||
|
len = strlen(pimg->title);
|
||
|
if (len > 11 && strcmp(pimg->title + len - 11, " (extended)") == 0) {
|
||
|
pimg->title[len - 11] = '\0';
|
||
|
pimg->is_extended_elevation = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pimg->datestamp[0] == '@') {
|
||
|
unsigned long v;
|
||
|
char * p;
|
||
|
errno = 0;
|
||
|
v = strtoul(pimg->datestamp + 1, &p, 10);
|
||
|
if (errno == 0 && *p == '\0')
|
||
|
pimg->datestamp_numeric = v;
|
||
|
/* FIXME: We're assuming here that the C time_t epoch is 1970, which is
|
||
|
* true for Unix-like systems, macOS and Windows, but isn't guaranteed
|
||
|
* by ISO C.
|
||
|
*/
|
||
|
} else {
|
||
|
/* %a,%Y.%m.%d %H:%M:%S %Z */
|
||
|
struct tm tm;
|
||
|
unsigned long v;
|
||
|
char * p = pimg->datestamp;
|
||
|
while (isalpha((unsigned char)*p)) ++p;
|
||
|
if (*p == ',') ++p;
|
||
|
while (isspace((unsigned char)*p)) ++p;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v == ULONG_MAX || *p++ != '.')
|
||
|
goto bad_3d_date;
|
||
|
tm.tm_year = v - 1900;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v < 1 || v > 12 || *p++ != '.')
|
||
|
goto bad_3d_date;
|
||
|
tm.tm_mon = v - 1;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v < 1 || v > 31 || *p++ != ' ')
|
||
|
goto bad_3d_date;
|
||
|
tm.tm_mday = v;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v >= 24 || *p++ != ':')
|
||
|
goto bad_3d_date;
|
||
|
tm.tm_hour = v;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v >= 60 || *p++ != ':')
|
||
|
goto bad_3d_date;
|
||
|
tm.tm_min = v;
|
||
|
v = strtoul(p, &p, 10);
|
||
|
if (v > 60)
|
||
|
goto bad_3d_date;
|
||
|
tm.tm_sec = v;
|
||
|
tm.tm_isdst = 0;
|
||
|
while (isspace((unsigned char)*p)) ++p;
|
||
|
/* p now points to the timezone string.
|
||
|
*
|
||
|
* However, it's likely to be a string like "BST", and such strings can
|
||
|
* be ambiguous (BST could be UTC+1 or UTC+6), so it is impossible to
|
||
|
* reliably convert in all cases. Just pass what we have to tzset() - if
|
||
|
* it doesn't handle it, UTC will be used.
|
||
|
*/
|
||
|
pimg->datestamp_numeric = mktime_with_tz(&tm, p);
|
||
|
}
|
||
|
bad_3d_date:
|
||
|
|
||
|
pimg->start = ftell(pimg->fh);
|
||
|
|
||
|
return pimg;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
img_rewind(img *pimg)
|
||
|
{
|
||
|
if (!pimg->fRead) {
|
||
|
img_errno = IMG_WRITEERROR;
|
||
|
return 0;
|
||
|
}
|
||
|
if (fseek(pimg->fh, pimg->start, SEEK_SET) != 0) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return 0;
|
||
|
}
|
||
|
clearerr(pimg->fh);
|
||
|
/* [VERSION_SURVEX_POS] already skipped heading line, or there wasn't one
|
||
|
* [version 0] not in the middle of a 'LINE' command
|
||
|
* [version >= 3] not in the middle of turning a LINE into a MOVE */
|
||
|
pimg->pending = 0;
|
||
|
|
||
|
img_errno = IMG_NONE;
|
||
|
|
||
|
/* for version >= 3 we use label_buf to store the prefix for reuse */
|
||
|
/* for VERSION_COMPASS_PLT, 0 value indicates we haven't entered a survey
|
||
|
* yet */
|
||
|
/* for VERSION_CMAP_SHOT, we store the last station here to detect whether
|
||
|
* we MOVE or LINE */
|
||
|
pimg->label_len = 0;
|
||
|
pimg->style = img_STYLE_UNKNOWN;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
img *
|
||
|
img_open_write_cs(const char *fnm, const char *title, const char *cs, int flags)
|
||
|
{
|
||
|
if (fDirectory(fnm)) {
|
||
|
img_errno = IMG_DIRECTORY;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return img_write_stream(fopen(fnm, "wb"), fclose, title, cs, flags);
|
||
|
}
|
||
|
|
||
|
img *
|
||
|
img_write_stream(FILE *stream, int (*close_func)(FILE*),
|
||
|
const char *title, const char *cs, int flags)
|
||
|
{
|
||
|
time_t tm;
|
||
|
img *pimg;
|
||
|
|
||
|
if (stream == NULL) {
|
||
|
img_errno = IMG_FILENOTFOUND;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pimg = osnew(img);
|
||
|
if (pimg == NULL) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
if (close_func) close_func(stream);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pimg->fh = stream;
|
||
|
pimg->close_func = close_func;
|
||
|
pimg->buf_len = 257;
|
||
|
pimg->label_buf = (char *)xosmalloc(pimg->buf_len);
|
||
|
if (!pimg->label_buf) {
|
||
|
if (pimg->close_func) pimg->close_func(pimg->fh);
|
||
|
osfree(pimg);
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pimg->filename_opened = NULL;
|
||
|
|
||
|
/* Output image file header */
|
||
|
fputs("Survex 3D Image File\n", pimg->fh); /* file identifier string */
|
||
|
if (img_output_version < 2) {
|
||
|
pimg->version = 1;
|
||
|
fputs("Bv0.01\n", pimg->fh); /* binary file format version number */
|
||
|
} else {
|
||
|
pimg->version = (img_output_version > IMG_VERSION_MAX) ? IMG_VERSION_MAX : img_output_version;
|
||
|
fprintf(pimg->fh, "v%d\n", pimg->version); /* file format version no. */
|
||
|
}
|
||
|
|
||
|
fputs(title, pimg->fh);
|
||
|
if (pimg->version < 8 && (flags & img_FFLAG_EXTENDED)) {
|
||
|
/* Older format versions append " (extended)" to the title to mark
|
||
|
* extended elevations. */
|
||
|
size_t len = strlen(title);
|
||
|
if (len < 11 || strcmp(title + len - 11, " (extended)") != 0)
|
||
|
fputs(" (extended)", pimg->fh);
|
||
|
}
|
||
|
if (pimg->version == 8 && cs && *cs) {
|
||
|
/* We sneak in an extra field after a zero byte here, containing the
|
||
|
* specified coordinate system (if any). Older readers will just not
|
||
|
* see it (which is fine), and this trick avoids us having to bump the
|
||
|
* 3d format version.
|
||
|
*/
|
||
|
PUTC('\0', pimg->fh);
|
||
|
fputs(cs, pimg->fh);
|
||
|
}
|
||
|
PUTC('\n', pimg->fh);
|
||
|
|
||
|
tm = time(NULL);
|
||
|
if (tm == (time_t)-1) {
|
||
|
fputsnl(TIMENA, pimg->fh);
|
||
|
} else if (pimg->version <= 7) {
|
||
|
char date[256];
|
||
|
/* output current date and time in format specified */
|
||
|
strftime(date, 256, TIMEFMT, localtime(&tm));
|
||
|
fputsnl(date, pimg->fh);
|
||
|
} else {
|
||
|
fprintf(pimg->fh, "@%ld\n", (long)tm);
|
||
|
}
|
||
|
|
||
|
if (pimg->version >= 8) {
|
||
|
/* Clear bit one in case anyone has been passing true for fBinary. */
|
||
|
flags &=~ 1;
|
||
|
PUTC(flags, pimg->fh);
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
if (img_output_version >= 5) {
|
||
|
static const unsigned char codelengths[32] = {
|
||
|
4, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||
|
};
|
||
|
fwrite(codelengths, 32, 1, pimg->fh);
|
||
|
}
|
||
|
#endif
|
||
|
pimg->fRead = 0; /* writing to this file */
|
||
|
img_errno = IMG_NONE;
|
||
|
|
||
|
/* for version >= 3 we use label_buf to store the prefix for reuse */
|
||
|
pimg->label_buf[0] = '\0';
|
||
|
pimg->label_len = 0;
|
||
|
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date1 = pimg->date2 = 0;
|
||
|
pimg->olddate1 = pimg->olddate2 = 0;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days1 = pimg->days2 = -1;
|
||
|
pimg->olddays1 = pimg->olddays2 = -1;
|
||
|
#endif
|
||
|
pimg->style = pimg->oldstyle = img_STYLE_UNKNOWN;
|
||
|
|
||
|
pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
|
||
|
|
||
|
pimg->n_legs = 0;
|
||
|
pimg->length = 0.0;
|
||
|
pimg->E = pimg->H = pimg->V = 0.0;
|
||
|
|
||
|
/* Don't check for write errors now - let img_close() report them... */
|
||
|
return pimg;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
read_xyz_station_coords(img_point *pt, const char *line)
|
||
|
{
|
||
|
char num[12];
|
||
|
memcpy(num, line + 6, 9);
|
||
|
num[9] = '\0';
|
||
|
pt->x = atof(num) / METRES_PER_FOOT;
|
||
|
memcpy(num, line + 15, 9);
|
||
|
pt->y = atof(num) / METRES_PER_FOOT;
|
||
|
memcpy(num, line + 24, 8);
|
||
|
num[8] = '\0';
|
||
|
pt->z = atof(num) / METRES_PER_FOOT;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
read_xyz_shot_coords(img_point *pt, const char *line)
|
||
|
{
|
||
|
char num[12];
|
||
|
memcpy(num, line + 40, 10);
|
||
|
num[10] = '\0';
|
||
|
pt->x = atof(num) / METRES_PER_FOOT;
|
||
|
memcpy(num, line + 50, 10);
|
||
|
pt->y = atof(num) / METRES_PER_FOOT;
|
||
|
memcpy(num, line + 60, 9);
|
||
|
num[9] = '\0';
|
||
|
pt->z = atof(num) / METRES_PER_FOOT;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
subtract_xyz_shot_deltas(img_point *pt, const char *line)
|
||
|
{
|
||
|
char num[12];
|
||
|
memcpy(num, line + 15, 9);
|
||
|
num[9] = '\0';
|
||
|
pt->x -= atof(num) / METRES_PER_FOOT;
|
||
|
memcpy(num, line + 24, 8);
|
||
|
num[8] = '\0';
|
||
|
pt->y -= atof(num) / METRES_PER_FOOT;
|
||
|
memcpy(num, line + 32, 8);
|
||
|
pt->z -= atof(num) / METRES_PER_FOOT;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
read_coord(FILE *fh, img_point *pt)
|
||
|
{
|
||
|
SVX_ASSERT(fh);
|
||
|
SVX_ASSERT(pt);
|
||
|
pt->x = get32(fh) / 100.0;
|
||
|
pt->y = get32(fh) / 100.0;
|
||
|
pt->z = get32(fh) / 100.0;
|
||
|
if (ferror(fh) || feof(fh)) {
|
||
|
img_errno = feof(fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
skip_coord(FILE *fh)
|
||
|
{
|
||
|
return (fseek(fh, 12, SEEK_CUR) == 0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
read_v3label(img *pimg)
|
||
|
{
|
||
|
char *q;
|
||
|
long len = GETC(pimg->fh);
|
||
|
if (len == EOF) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (len == 0xfe) {
|
||
|
len += get16(pimg->fh);
|
||
|
if (feof(pimg->fh)) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
} else if (len == 0xff) {
|
||
|
len = get32(pimg->fh);
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (feof(pimg->fh) || len < 0xfe + 0xffff) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!check_label_space(pimg, pimg->label_len + len + 1)) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
q = pimg->label_buf + pimg->label_len;
|
||
|
pimg->label_len += len;
|
||
|
if (len && fread(q, len, 1, pimg->fh) != 1) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
q[len] = '\0';
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
read_v8label(img *pimg, int common_flag, size_t common_val)
|
||
|
{
|
||
|
char *q;
|
||
|
size_t del, add;
|
||
|
if (common_flag) {
|
||
|
if (common_val == 0) return 0;
|
||
|
add = del = common_val;
|
||
|
} else {
|
||
|
int ch = GETC(pimg->fh);
|
||
|
if (ch == EOF) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ch != 0x00) {
|
||
|
del = ch >> 4;
|
||
|
add = ch & 0x0f;
|
||
|
} else {
|
||
|
ch = GETC(pimg->fh);
|
||
|
if (ch == EOF) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ch != 0xff) {
|
||
|
del = ch;
|
||
|
} else {
|
||
|
del = get32(pimg->fh);
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
}
|
||
|
ch = GETC(pimg->fh);
|
||
|
if (ch == EOF) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ch != 0xff) {
|
||
|
add = ch;
|
||
|
} else {
|
||
|
add = get32(pimg->fh);
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (add > del && !check_label_space(pimg, pimg->label_len + add - del + 1)) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
}
|
||
|
if (del > pimg->label_len) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
pimg->label_len -= del;
|
||
|
q = pimg->label_buf + pimg->label_len;
|
||
|
pimg->label_len += add;
|
||
|
if (add && fread(q, add, 1, pimg->fh) != 1) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
q[add] = '\0';
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int img_read_item_new(img *pimg, img_point *p);
|
||
|
static int img_read_item_v3to7(img *pimg, img_point *p);
|
||
|
static int img_read_item_ancient(img *pimg, img_point *p);
|
||
|
static int img_read_item_ascii_wrapper(img *pimg, img_point *p);
|
||
|
static int img_read_item_ascii(img *pimg, img_point *p);
|
||
|
|
||
|
int
|
||
|
img_read_item(img *pimg, img_point *p)
|
||
|
{
|
||
|
pimg->flags = 0;
|
||
|
|
||
|
if (pimg->version >= 8) {
|
||
|
return img_read_item_new(pimg, p);
|
||
|
} else if (pimg->version >= 3) {
|
||
|
return img_read_item_v3to7(pimg, p);
|
||
|
} else if (pimg->version >= 1) {
|
||
|
return img_read_item_ancient(pimg, p);
|
||
|
} else {
|
||
|
return img_read_item_ascii_wrapper(pimg, p);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
img_read_item_new(img *pimg, img_point *p)
|
||
|
{
|
||
|
int result;
|
||
|
int opt;
|
||
|
pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
|
||
|
if (pimg->pending >= 0x40) {
|
||
|
if (pimg->pending == 256) {
|
||
|
pimg->pending = 0;
|
||
|
return img_XSECT_END;
|
||
|
}
|
||
|
*p = pimg->mv;
|
||
|
pimg->flags = (int)(pimg->pending) & 0x3f;
|
||
|
pimg->pending = 0;
|
||
|
return img_LINE;
|
||
|
}
|
||
|
again3: /* label to goto if we get a prefix, date, or lrud */
|
||
|
pimg->label = pimg->label_buf;
|
||
|
opt = GETC(pimg->fh);
|
||
|
if (opt == EOF) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (opt >> 6 == 0) {
|
||
|
if (opt <= 4) {
|
||
|
if (opt == 0 && pimg->style == 0)
|
||
|
return img_STOP; /* end of data marker */
|
||
|
/* STYLE */
|
||
|
pimg->style = opt;
|
||
|
goto again3;
|
||
|
}
|
||
|
if (opt >= 0x10) {
|
||
|
switch (opt) {
|
||
|
case 0x10: { /* No date info */
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date1 = pimg->date2 = 0;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days1 = pimg->days2 = -1;
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
case 0x11: { /* Single date */
|
||
|
int days1 = (int)getu16(pimg->fh);
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date2 = pimg->date1 = (days1 - 25567) * 86400;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days2 = pimg->days1 = days1;
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
case 0x12: { /* Date range (short) */
|
||
|
int days1 = (int)getu16(pimg->fh);
|
||
|
int days2 = days1 + GETC(pimg->fh) + 1;
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date1 = (days1 - 25567) * 86400;
|
||
|
pimg->date2 = (days2 - 25567) * 86400;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days1 = days1;
|
||
|
pimg->days2 = days2;
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
case 0x13: { /* Date range (long) */
|
||
|
int days1 = (int)getu16(pimg->fh);
|
||
|
int days2 = (int)getu16(pimg->fh);
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date1 = (days1 - 25567) * 86400;
|
||
|
pimg->date2 = (days2 - 25567) * 86400;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days1 = days1;
|
||
|
pimg->days2 = days2;
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
case 0x1f: /* Error info */
|
||
|
pimg->n_legs = get32(pimg->fh);
|
||
|
pimg->length = get32(pimg->fh) / 100.0;
|
||
|
pimg->E = get32(pimg->fh) / 100.0;
|
||
|
pimg->H = get32(pimg->fh) / 100.0;
|
||
|
pimg->V = get32(pimg->fh) / 100.0;
|
||
|
return img_ERROR_INFO;
|
||
|
case 0x30: case 0x31: /* LRUD */
|
||
|
case 0x32: case 0x33: /* Big LRUD! */
|
||
|
if (read_v8label(pimg, 0, 0) == img_BAD) return img_BAD;
|
||
|
pimg->flags = (int)opt & 0x01;
|
||
|
if (opt < 0x32) {
|
||
|
pimg->l = get16(pimg->fh) / 100.0;
|
||
|
pimg->r = get16(pimg->fh) / 100.0;
|
||
|
pimg->u = get16(pimg->fh) / 100.0;
|
||
|
pimg->d = get16(pimg->fh) / 100.0;
|
||
|
} else {
|
||
|
pimg->l = get32(pimg->fh) / 100.0;
|
||
|
pimg->r = get32(pimg->fh) / 100.0;
|
||
|
pimg->u = get32(pimg->fh) / 100.0;
|
||
|
pimg->d = get32(pimg->fh) / 100.0;
|
||
|
}
|
||
|
if (!stn_included(pimg)) {
|
||
|
return img_XSECT_END;
|
||
|
}
|
||
|
/* If this is the last cross-section in this passage, set
|
||
|
* pending so we return img_XSECT_END next time. */
|
||
|
if (pimg->flags & 0x01) {
|
||
|
pimg->pending = 256;
|
||
|
pimg->flags &= ~0x01;
|
||
|
}
|
||
|
return img_XSECT;
|
||
|
default: /* 0x25 - 0x2f and 0x34 - 0x3f are currently unallocated. */
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
goto again3;
|
||
|
}
|
||
|
if (opt != 15) {
|
||
|
/* 1-14 and 16-31 reserved */
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
result = img_MOVE;
|
||
|
} else if (opt >= 0x80) {
|
||
|
if (read_v8label(pimg, 0, 0) == img_BAD) return img_BAD;
|
||
|
|
||
|
result = img_LABEL;
|
||
|
|
||
|
if (!stn_included(pimg)) {
|
||
|
if (!skip_coord(pimg->fh)) return img_BAD;
|
||
|
pimg->pending = 0;
|
||
|
goto again3;
|
||
|
}
|
||
|
|
||
|
pimg->flags = (int)opt & 0x7f;
|
||
|
} else if ((opt >> 6) == 1) {
|
||
|
if (read_v8label(pimg, opt & 0x20, 0) == img_BAD) return img_BAD;
|
||
|
|
||
|
result = img_LINE;
|
||
|
|
||
|
if (!survey_included(pimg)) {
|
||
|
if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
|
||
|
pimg->pending = 15;
|
||
|
goto again3;
|
||
|
}
|
||
|
|
||
|
if (pimg->pending) {
|
||
|
*p = pimg->mv;
|
||
|
if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
|
||
|
pimg->pending = opt;
|
||
|
return img_MOVE;
|
||
|
}
|
||
|
pimg->flags = (int)opt & 0x1f;
|
||
|
} else {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (!read_coord(pimg->fh, p)) return img_BAD;
|
||
|
pimg->pending = 0;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
img_read_item_v3to7(img *pimg, img_point *p)
|
||
|
{
|
||
|
int result;
|
||
|
int opt;
|
||
|
pimg->l = pimg->r = pimg->u = pimg->d = -1.0;
|
||
|
if (pimg->pending == 256) {
|
||
|
pimg->pending = 0;
|
||
|
return img_XSECT_END;
|
||
|
}
|
||
|
if (pimg->pending >= 0x80) {
|
||
|
*p = pimg->mv;
|
||
|
pimg->flags = (int)(pimg->pending) & 0x3f;
|
||
|
pimg->pending = 0;
|
||
|
return img_LINE;
|
||
|
}
|
||
|
again3: /* label to goto if we get a prefix, date, or lrud */
|
||
|
pimg->label = pimg->label_buf;
|
||
|
opt = GETC(pimg->fh);
|
||
|
if (opt == EOF) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
switch (opt >> 6) {
|
||
|
case 0:
|
||
|
if (opt == 0) {
|
||
|
if (!pimg->label_len) return img_STOP; /* end of data marker */
|
||
|
pimg->label_len = 0;
|
||
|
goto again3;
|
||
|
}
|
||
|
if (opt < 15) {
|
||
|
/* 1-14 mean trim that many levels from current prefix */
|
||
|
int c;
|
||
|
if (pimg->label_len <= 17) {
|
||
|
/* zero prefix using "0" */
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
/* extra - 1 because label_len points to one past the end */
|
||
|
c = pimg->label_len - 17 - 1;
|
||
|
while (pimg->label_buf[c] != '.' || --opt > 0) {
|
||
|
if (--c < 0) {
|
||
|
/* zero prefix using "0" */
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
}
|
||
|
c++;
|
||
|
pimg->label_len = c;
|
||
|
goto again3;
|
||
|
}
|
||
|
if (opt == 15) {
|
||
|
result = img_MOVE;
|
||
|
break;
|
||
|
}
|
||
|
if (opt >= 0x20) {
|
||
|
switch (opt) {
|
||
|
case 0x20: /* Single date */
|
||
|
if (pimg->version < 7) {
|
||
|
int date1 = get32(pimg->fh);
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date2 = pimg->date1 = date1;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
if (date1 != 0) {
|
||
|
pimg->days2 = pimg->days1 = (date1 / 86400) + 25567;
|
||
|
} else {
|
||
|
pimg->days2 = pimg->days1 = -1;
|
||
|
}
|
||
|
#endif
|
||
|
} else {
|
||
|
int days1 = (int)getu16(pimg->fh);
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date2 = pimg->date1 = (days1 - 25567) * 86400;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days2 = pimg->days1 = days1;
|
||
|
#endif
|
||
|
}
|
||
|
break;
|
||
|
case 0x21: /* Date range (short for v7+) */
|
||
|
if (pimg->version < 7) {
|
||
|
INT32_T date1 = get32(pimg->fh);
|
||
|
INT32_T date2 = get32(pimg->fh);
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date1 = date1;
|
||
|
pimg->date2 = date2;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days1 = (date1 / 86400) + 25567;
|
||
|
pimg->days2 = (date2 / 86400) + 25567;
|
||
|
#endif
|
||
|
} else {
|
||
|
int days1 = (int)getu16(pimg->fh);
|
||
|
int days2 = days1 + GETC(pimg->fh) + 1;
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date1 = (days1 - 25567) * 86400;
|
||
|
pimg->date2 = (days2 - 25567) * 86400;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days1 = days1;
|
||
|
pimg->days2 = days2;
|
||
|
#endif
|
||
|
}
|
||
|
break;
|
||
|
case 0x22: /* Error info */
|
||
|
pimg->n_legs = get32(pimg->fh);
|
||
|
pimg->length = get32(pimg->fh) / 100.0;
|
||
|
pimg->E = get32(pimg->fh) / 100.0;
|
||
|
pimg->H = get32(pimg->fh) / 100.0;
|
||
|
pimg->V = get32(pimg->fh) / 100.0;
|
||
|
if (feof(pimg->fh)) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
return img_ERROR_INFO;
|
||
|
case 0x23: { /* v7+: Date range (long) */
|
||
|
if (pimg->version < 7) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
int days1 = (int)getu16(pimg->fh);
|
||
|
int days2 = (int)getu16(pimg->fh);
|
||
|
if (feof(pimg->fh)) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date1 = (days1 - 25567) * 86400;
|
||
|
pimg->date2 = (days2 - 25567) * 86400;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days1 = days1;
|
||
|
pimg->days2 = days2;
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
case 0x24: { /* v7+: No date info */
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->date1 = pimg->date2 = 0;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->days1 = pimg->days2 = -1;
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
case 0x30: case 0x31: /* LRUD */
|
||
|
case 0x32: case 0x33: /* Big LRUD! */
|
||
|
if (read_v3label(pimg) == img_BAD) return img_BAD;
|
||
|
pimg->flags = (int)opt & 0x01;
|
||
|
if (opt < 0x32) {
|
||
|
pimg->l = get16(pimg->fh) / 100.0;
|
||
|
pimg->r = get16(pimg->fh) / 100.0;
|
||
|
pimg->u = get16(pimg->fh) / 100.0;
|
||
|
pimg->d = get16(pimg->fh) / 100.0;
|
||
|
} else {
|
||
|
pimg->l = get32(pimg->fh) / 100.0;
|
||
|
pimg->r = get32(pimg->fh) / 100.0;
|
||
|
pimg->u = get32(pimg->fh) / 100.0;
|
||
|
pimg->d = get32(pimg->fh) / 100.0;
|
||
|
}
|
||
|
if (feof(pimg->fh)) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (!stn_included(pimg)) {
|
||
|
return img_XSECT_END;
|
||
|
}
|
||
|
/* If this is the last cross-section in this passage, set
|
||
|
* pending so we return img_XSECT_END next time. */
|
||
|
if (pimg->flags & 0x01) {
|
||
|
pimg->pending = 256;
|
||
|
pimg->flags &= ~0x01;
|
||
|
}
|
||
|
return img_XSECT;
|
||
|
default: /* 0x25 - 0x2f and 0x34 - 0x3f are currently unallocated. */
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (feof(pimg->fh)) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
goto again3;
|
||
|
}
|
||
|
/* 16-31 mean remove (n - 15) characters from the prefix */
|
||
|
/* zero prefix using 0 */
|
||
|
if (pimg->label_len <= (size_t)(opt - 15)) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
pimg->label_len -= (opt - 15);
|
||
|
goto again3;
|
||
|
case 1:
|
||
|
if (read_v3label(pimg) == img_BAD) return img_BAD;
|
||
|
|
||
|
result = img_LABEL;
|
||
|
|
||
|
if (!stn_included(pimg)) {
|
||
|
if (!skip_coord(pimg->fh)) return img_BAD;
|
||
|
pimg->pending = 0;
|
||
|
goto again3;
|
||
|
}
|
||
|
|
||
|
pimg->flags = (int)opt & 0x3f;
|
||
|
break;
|
||
|
case 2:
|
||
|
if (read_v3label(pimg) == img_BAD) return img_BAD;
|
||
|
|
||
|
result = img_LINE;
|
||
|
|
||
|
if (!survey_included(pimg)) {
|
||
|
if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
|
||
|
pimg->pending = 15;
|
||
|
goto again3;
|
||
|
}
|
||
|
|
||
|
if (pimg->pending) {
|
||
|
*p = pimg->mv;
|
||
|
if (!read_coord(pimg->fh, &(pimg->mv))) return img_BAD;
|
||
|
pimg->pending = opt;
|
||
|
return img_MOVE;
|
||
|
}
|
||
|
pimg->flags = (int)opt & 0x3f;
|
||
|
break;
|
||
|
default:
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (!read_coord(pimg->fh, p)) return img_BAD;
|
||
|
pimg->pending = 0;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
img_read_item_ancient(img *pimg, img_point *p)
|
||
|
{
|
||
|
int result;
|
||
|
static long opt_lookahead = 0;
|
||
|
static img_point pt = { 0.0, 0.0, 0.0 };
|
||
|
long opt;
|
||
|
|
||
|
again: /* label to goto if we get a cross */
|
||
|
pimg->label = pimg->label_buf;
|
||
|
pimg->label[0] = '\0';
|
||
|
|
||
|
if (pimg->version == 1) {
|
||
|
if (opt_lookahead) {
|
||
|
opt = opt_lookahead;
|
||
|
opt_lookahead = 0;
|
||
|
} else {
|
||
|
opt = get32(pimg->fh);
|
||
|
}
|
||
|
} else {
|
||
|
opt = GETC(pimg->fh);
|
||
|
}
|
||
|
|
||
|
if (feof(pimg->fh)) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
|
||
|
switch (opt) {
|
||
|
case -1: case 0:
|
||
|
return img_STOP; /* end of data marker */
|
||
|
case 1:
|
||
|
/* skip coordinates */
|
||
|
if (!skip_coord(pimg->fh)) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
goto again;
|
||
|
case 2: case 3: {
|
||
|
size_t len;
|
||
|
result = img_LABEL;
|
||
|
if (!fgets(pimg->label_buf, pimg->buf_len, pimg->fh)) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (pimg->label[0] == '\\') pimg->label++;
|
||
|
len = strlen(pimg->label);
|
||
|
if (len == 0 || pimg->label[len - 1] != '\n') {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
/* Ignore empty labels in some .3d files (caused by a bug) */
|
||
|
if (len == 1) goto again;
|
||
|
pimg->label[len - 1] = '\0';
|
||
|
pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
|
||
|
if (opt == 2) goto done;
|
||
|
break;
|
||
|
}
|
||
|
case 6: case 7: {
|
||
|
long len;
|
||
|
result = img_LABEL;
|
||
|
|
||
|
if (opt == 7)
|
||
|
pimg->flags = GETC(pimg->fh);
|
||
|
else
|
||
|
pimg->flags = img_SFLAG_UNDERGROUND; /* no flags given... */
|
||
|
|
||
|
len = get32(pimg->fh);
|
||
|
|
||
|
if (feof(pimg->fh)) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
|
||
|
/* Ignore empty labels in some .3d files (caused by a bug) */
|
||
|
if (len == 0) goto again;
|
||
|
if (!check_label_space(pimg, len + 1)) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (fread(pimg->label_buf, len, 1, pimg->fh) != 1) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
pimg->label_buf[len] = '\0';
|
||
|
break;
|
||
|
}
|
||
|
case 4:
|
||
|
result = img_MOVE;
|
||
|
break;
|
||
|
case 5:
|
||
|
result = img_LINE;
|
||
|
break;
|
||
|
default:
|
||
|
switch ((int)opt & 0xc0) {
|
||
|
case 0x80:
|
||
|
pimg->flags = (int)opt & 0x3f;
|
||
|
result = img_LINE;
|
||
|
break;
|
||
|
case 0x40: {
|
||
|
char *q;
|
||
|
pimg->flags = (int)opt & 0x3f;
|
||
|
result = img_LABEL;
|
||
|
if (!fgets(pimg->label_buf, pimg->buf_len, pimg->fh)) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
q = pimg->label_buf + strlen(pimg->label_buf) - 1;
|
||
|
/* Ignore empty-labels in some .3d files (caused by a bug) */
|
||
|
if (q == pimg->label_buf) goto again;
|
||
|
if (*q != '\n') {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
*q = '\0';
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!read_coord(pimg->fh, &pt)) return img_BAD;
|
||
|
|
||
|
if (result == img_LABEL && !stn_included(pimg)) {
|
||
|
goto again;
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
*p = pt;
|
||
|
|
||
|
if (result == img_MOVE && pimg->version == 1) {
|
||
|
/* peek at next code and see if it's an old-style label */
|
||
|
opt_lookahead = get32(pimg->fh);
|
||
|
|
||
|
if (feof(pimg->fh)) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
|
||
|
if (opt_lookahead == 2) return img_read_item_ancient(pimg, p);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
img_read_item_ascii_wrapper(img *pimg, img_point *p)
|
||
|
{
|
||
|
/* We need to set the default locale for fscanf() to work on
|
||
|
* numbers with "." as decimal point. */
|
||
|
int result;
|
||
|
char * current_locale = my_strdup(setlocale(LC_NUMERIC, NULL));
|
||
|
setlocale(LC_NUMERIC, "C");
|
||
|
result = img_read_item_ascii(pimg, p);
|
||
|
setlocale(LC_NUMERIC, current_locale);
|
||
|
free(current_locale);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/* Handle all ASCII formats. */
|
||
|
static int
|
||
|
img_read_item_ascii(img *pimg, img_point *p)
|
||
|
{
|
||
|
int result;
|
||
|
pimg->label = pimg->label_buf;
|
||
|
if (pimg->version == 0) {
|
||
|
ascii_again:
|
||
|
pimg->label[0] = '\0';
|
||
|
if (feof(pimg->fh)) return img_STOP;
|
||
|
if (pimg->pending) {
|
||
|
pimg->pending = 0;
|
||
|
result = img_LINE;
|
||
|
} else {
|
||
|
char cmd[7];
|
||
|
/* Stop if nothing found */
|
||
|
if (fscanf(pimg->fh, "%6s", cmd) < 1) return img_STOP;
|
||
|
if (strcmp(cmd, "move") == 0)
|
||
|
result = img_MOVE;
|
||
|
else if (strcmp(cmd, "draw") == 0)
|
||
|
result = img_LINE;
|
||
|
else if (strcmp(cmd, "line") == 0) {
|
||
|
/* set flag to indicate to process second triplet as LINE */
|
||
|
pimg->pending = 1;
|
||
|
result = img_MOVE;
|
||
|
} else if (strcmp(cmd, "cross") == 0) {
|
||
|
if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
|
||
|
img_errno = feof(pimg->fh) ? IMG_BADFORMAT : IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
goto ascii_again;
|
||
|
} else if (strcmp(cmd, "name") == 0) {
|
||
|
size_t off = 0;
|
||
|
int ch = GETC(pimg->fh);
|
||
|
if (ch == ' ') ch = GETC(pimg->fh);
|
||
|
while (ch != ' ') {
|
||
|
if (ch == '\n' || ch == EOF) {
|
||
|
img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (off == pimg->buf_len) {
|
||
|
if (!check_label_space(pimg, pimg->buf_len * 2)) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
}
|
||
|
pimg->label_buf[off++] = ch;
|
||
|
ch = GETC(pimg->fh);
|
||
|
}
|
||
|
pimg->label_buf[off] = '\0';
|
||
|
|
||
|
pimg->label = pimg->label_buf;
|
||
|
if (pimg->label[0] == '\\') pimg->label++;
|
||
|
|
||
|
pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
|
||
|
|
||
|
result = img_LABEL;
|
||
|
} else {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD; /* unknown keyword */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (fscanf(pimg->fh, "%lf%lf%lf", &p->x, &p->y, &p->z) < 3) {
|
||
|
img_errno = ferror(pimg->fh) ? IMG_READERROR : IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
|
||
|
if (result == img_LABEL && !stn_included(pimg)) {
|
||
|
goto ascii_again;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
} else if (pimg->version == VERSION_SURVEX_POS) {
|
||
|
/* Survex .pos file */
|
||
|
int ch;
|
||
|
size_t off;
|
||
|
pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
|
||
|
againpos:
|
||
|
while (fscanf(pimg->fh, "(%lf,%lf,%lf )", &p->x, &p->y, &p->z) != 3) {
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
if (feof(pimg->fh)) return img_STOP;
|
||
|
if (pimg->pending) {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
pimg->pending = 1;
|
||
|
/* ignore rest of line */
|
||
|
do {
|
||
|
ch = GETC(pimg->fh);
|
||
|
} while (ch != '\n' && ch != '\r' && ch != EOF);
|
||
|
}
|
||
|
|
||
|
pimg->label_buf[0] = '\0';
|
||
|
do {
|
||
|
ch = GETC(pimg->fh);
|
||
|
} while (ch == ' ' || ch == '\t');
|
||
|
if (ch == '\n' || ch == EOF) {
|
||
|
/* If there's no label, set img_SFLAG_ANON. */
|
||
|
pimg->flags |= img_SFLAG_ANON;
|
||
|
return img_LABEL;
|
||
|
}
|
||
|
pimg->label_buf[0] = ch;
|
||
|
off = 1;
|
||
|
while (!feof(pimg->fh)) {
|
||
|
if (!fgets(pimg->label_buf + off, pimg->buf_len - off, pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
|
||
|
off += strlen(pimg->label_buf + off);
|
||
|
if (off && pimg->label_buf[off - 1] == '\n') {
|
||
|
pimg->label_buf[off - 1] = '\0';
|
||
|
break;
|
||
|
}
|
||
|
if (!check_label_space(pimg, pimg->buf_len * 2)) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pimg->label = pimg->label_buf;
|
||
|
|
||
|
if (pimg->label[0] == '\\') pimg->label++;
|
||
|
|
||
|
if (!stn_included(pimg)) goto againpos;
|
||
|
|
||
|
return img_LABEL;
|
||
|
} else if (pimg->version == VERSION_COMPASS_PLT) {
|
||
|
/* Compass .plt file */
|
||
|
if (pimg->pending > 0) {
|
||
|
/* -1 signals we've entered the first survey we want to
|
||
|
* read, and need to fudge lots if the first action is 'D'...
|
||
|
*/
|
||
|
/* pending MOVE or LINE */
|
||
|
int r = pimg->pending - 4;
|
||
|
pimg->pending = 0;
|
||
|
pimg->flags = 0;
|
||
|
pimg->label[pimg->label_len] = '\0';
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
while (1) {
|
||
|
char *line;
|
||
|
char *q;
|
||
|
size_t len = 0;
|
||
|
int ch = GETC(pimg->fh);
|
||
|
|
||
|
switch (ch) {
|
||
|
case '\x1a': case EOF: /* Don't insist on ^Z at end of file */
|
||
|
return img_STOP;
|
||
|
case 'X': case 'F': case 'S':
|
||
|
/* bounding boX (marks end of survey), Feature survey, or
|
||
|
* new Section - skip to next survey */
|
||
|
if (pimg->survey) return img_STOP;
|
||
|
skip_to_N:
|
||
|
while (1) {
|
||
|
do {
|
||
|
ch = GETC(pimg->fh);
|
||
|
} while (ch != '\n' && ch != '\r' && ch != EOF);
|
||
|
while (ch == '\n' || ch == '\r') ch = GETC(pimg->fh);
|
||
|
if (ch == 'N') break;
|
||
|
if (ch == '\x1a' || ch == EOF) return img_STOP;
|
||
|
}
|
||
|
/* FALLTHRU */
|
||
|
case 'N':
|
||
|
line = getline_alloc(pimg->fh);
|
||
|
if (!line) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
while (line[len] > 32) ++len;
|
||
|
if (pimg->label_len == 0) pimg->pending = -1;
|
||
|
if (!check_label_space(pimg, len + 1)) {
|
||
|
osfree(line);
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
pimg->label_len = len;
|
||
|
pimg->label = pimg->label_buf;
|
||
|
memcpy(pimg->label, line, len);
|
||
|
pimg->label[len] = '\0';
|
||
|
osfree(line);
|
||
|
break;
|
||
|
case 'M': case 'D': {
|
||
|
/* Move or Draw */
|
||
|
long fpos = -1;
|
||
|
if (pimg->survey && pimg->label_len == 0) {
|
||
|
/* We're only holding onto this line in case the first line
|
||
|
* of the 'N' is a 'D', so skip it for now...
|
||
|
*/
|
||
|
goto skip_to_N;
|
||
|
}
|
||
|
if (ch == 'D' && pimg->pending == -1) {
|
||
|
if (pimg->survey) {
|
||
|
fpos = ftell(pimg->fh) - 1;
|
||
|
fseek(pimg->fh, pimg->start, SEEK_SET);
|
||
|
ch = GETC(pimg->fh);
|
||
|
pimg->pending = 0;
|
||
|
} else {
|
||
|
/* If a file actually has a 'D' before any 'M', then
|
||
|
* pretend the 'D' is an 'M' - one of the examples
|
||
|
* in the docs was like this! */
|
||
|
ch = 'M';
|
||
|
}
|
||
|
}
|
||
|
line = getline_alloc(pimg->fh);
|
||
|
if (!line) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
/* Compass stores coordinates as North, East, Up = (y,x,z)! */
|
||
|
if (sscanf(line, "%lf%lf%lf", &p->y, &p->x, &p->z) != 3) {
|
||
|
osfree(line);
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
} else {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
}
|
||
|
return img_BAD;
|
||
|
}
|
||
|
p->x *= METRES_PER_FOOT;
|
||
|
p->y *= METRES_PER_FOOT;
|
||
|
p->z *= METRES_PER_FOOT;
|
||
|
q = strchr(line, 'S');
|
||
|
if (!q) {
|
||
|
osfree(line);
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
++q;
|
||
|
len = 0;
|
||
|
while (q[len] > ' ') ++len;
|
||
|
q[len] = '\0';
|
||
|
len += 2; /* ' ' and '\0' */
|
||
|
if (!check_label_space(pimg, pimg->label_len + len)) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
pimg->label = pimg->label_buf;
|
||
|
if (pimg->label_len) {
|
||
|
pimg->label[pimg->label_len] = ' ';
|
||
|
memcpy(pimg->label + pimg->label_len + 1, q, len - 1);
|
||
|
} else {
|
||
|
memcpy(pimg->label, q, len - 1);
|
||
|
}
|
||
|
q += len - 1;
|
||
|
/* Now read LRUD. Technically, this is optional but virtually
|
||
|
* all PLT files have it (with dummy negative values if no LRUD
|
||
|
* was measured) and some versions of Compass can't read PLT
|
||
|
* files without it!
|
||
|
*/
|
||
|
while (*q && *q <= ' ') q++;
|
||
|
if (*q == 'P') {
|
||
|
if (sscanf(q + 1, "%lf%lf%lf%lf",
|
||
|
&pimg->l, &pimg->r, &pimg->u, &pimg->d) != 4) {
|
||
|
osfree(line);
|
||
|
if (ferror(pimg->fh)) {
|
||
|
img_errno = IMG_READERROR;
|
||
|
} else {
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
}
|
||
|
return img_BAD;
|
||
|
}
|
||
|
pimg->l *= METRES_PER_FOOT;
|
||
|
pimg->r *= METRES_PER_FOOT;
|
||
|
pimg->u *= METRES_PER_FOOT;
|
||
|
pimg->d *= METRES_PER_FOOT;
|
||
|
} else {
|
||
|
pimg->l = pimg->r = pimg->u = pimg->d = -1;
|
||
|
}
|
||
|
osfree(line);
|
||
|
pimg->flags = img_SFLAG_UNDERGROUND; /* default flags */
|
||
|
if (fpos != -1) {
|
||
|
fseek(pimg->fh, fpos, SEEK_SET);
|
||
|
} else {
|
||
|
pimg->pending = (ch == 'M' ? img_MOVE : img_LINE) + 4;
|
||
|
}
|
||
|
return img_LABEL;
|
||
|
}
|
||
|
default:
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
/* CMAP .xyz file */
|
||
|
char *line = NULL;
|
||
|
char *q;
|
||
|
size_t len;
|
||
|
|
||
|
if (pimg->pending) {
|
||
|
/* pending MOVE or LINE or LABEL or STOP */
|
||
|
int r = pimg->pending - 4;
|
||
|
/* Set label to empty - don't use "" as we adjust label relative
|
||
|
* to label_buf when label_buf is reallocated. */
|
||
|
pimg->label = pimg->label_buf + strlen(pimg->label_buf);
|
||
|
pimg->flags = 0;
|
||
|
if (r == img_LABEL) {
|
||
|
/* nasty magic */
|
||
|
read_xyz_shot_coords(p, pimg->label_buf + 16);
|
||
|
subtract_xyz_shot_deltas(p, pimg->label_buf + 16);
|
||
|
pimg->pending = img_STOP + 4;
|
||
|
return img_MOVE;
|
||
|
}
|
||
|
|
||
|
pimg->pending = 0;
|
||
|
|
||
|
if (r == img_STOP) {
|
||
|
/* nasty magic */
|
||
|
read_xyz_shot_coords(p, pimg->label_buf + 16);
|
||
|
return img_LINE;
|
||
|
}
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
pimg->label = pimg->label_buf;
|
||
|
do {
|
||
|
osfree(line);
|
||
|
if (feof(pimg->fh)) return img_STOP;
|
||
|
line = getline_alloc(pimg->fh);
|
||
|
if (!line) {
|
||
|
img_errno = IMG_OUTOFMEMORY;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
} while (line[0] == ' ' || line[0] == '\0');
|
||
|
if (line[0] == '\x1a') return img_STOP;
|
||
|
|
||
|
len = strlen(line);
|
||
|
if (pimg->version == VERSION_CMAP_STATION) {
|
||
|
/* station variant */
|
||
|
if (len < 37) {
|
||
|
osfree(line);
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
memcpy(pimg->label, line, 6);
|
||
|
q = (char *)memchr(pimg->label, ' ', 6);
|
||
|
if (!q) q = pimg->label + 6;
|
||
|
*q = '\0';
|
||
|
|
||
|
read_xyz_station_coords(p, line);
|
||
|
|
||
|
/* FIXME: look at prev for lines (line + 32, 5) */
|
||
|
/* FIXME: duplicate stations... */
|
||
|
return img_LABEL;
|
||
|
} else {
|
||
|
/* Shot variant (VERSION_CMAP_SHOT) */
|
||
|
char old[8], new_[8];
|
||
|
if (len < 61) {
|
||
|
osfree(line);
|
||
|
img_errno = IMG_BADFORMAT;
|
||
|
return img_BAD;
|
||
|
}
|
||
|
|
||
|
memcpy(old, line, 7);
|
||
|
q = (char *)memchr(old, ' ', 7);
|
||
|
if (!q) q = old + 7;
|
||
|
*q = '\0';
|
||
|
|
||
|
memcpy(new_, line + 7, 7);
|
||
|
q = (char *)memchr(new_, ' ', 7);
|
||
|
if (!q) q = new_ + 7;
|
||
|
*q = '\0';
|
||
|
|
||
|
pimg->flags = img_SFLAG_UNDERGROUND;
|
||
|
|
||
|
if (strcmp(old, new_) == 0) {
|
||
|
pimg->pending = img_MOVE + 4;
|
||
|
read_xyz_shot_coords(p, line);
|
||
|
strcpy(pimg->label, new_);
|
||
|
osfree(line);
|
||
|
return img_LABEL;
|
||
|
}
|
||
|
|
||
|
if (strcmp(old, pimg->label) == 0) {
|
||
|
pimg->pending = img_LINE + 4;
|
||
|
read_xyz_shot_coords(p, line);
|
||
|
strcpy(pimg->label, new_);
|
||
|
osfree(line);
|
||
|
return img_LABEL;
|
||
|
}
|
||
|
|
||
|
pimg->pending = img_LABEL + 4;
|
||
|
read_xyz_shot_coords(p, line);
|
||
|
strcpy(pimg->label, new_);
|
||
|
memcpy(pimg->label + 16, line, 70);
|
||
|
|
||
|
osfree(line);
|
||
|
return img_LABEL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
write_coord(FILE *fh, double x, double y, double z)
|
||
|
{
|
||
|
SVX_ASSERT(fh);
|
||
|
/* Output in cm */
|
||
|
static INT32_T X_, Y_, Z_;
|
||
|
INT32_T X = my_lround(x * 100.0);
|
||
|
INT32_T Y = my_lround(y * 100.0);
|
||
|
INT32_T Z = my_lround(z * 100.0);
|
||
|
|
||
|
X_ -= X;
|
||
|
Y_ -= Y;
|
||
|
Z_ -= Z;
|
||
|
put32(X, fh);
|
||
|
put32(Y, fh);
|
||
|
put32(Z, fh);
|
||
|
X_ = X; Y_ = Y; Z_ = Z;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
write_v3label(img *pimg, int opt, const char *s)
|
||
|
{
|
||
|
size_t len, n, dot;
|
||
|
|
||
|
/* find length of common prefix */
|
||
|
dot = 0;
|
||
|
for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
|
||
|
if (s[len] == '.') dot = len + 1;
|
||
|
}
|
||
|
|
||
|
SVX_ASSERT(len <= pimg->label_len);
|
||
|
n = pimg->label_len - len;
|
||
|
if (len == 0) {
|
||
|
if (pimg->label_len) PUTC(0, pimg->fh);
|
||
|
} else if (n <= 16) {
|
||
|
if (n) PUTC(n + 15, pimg->fh);
|
||
|
} else if (dot == 0) {
|
||
|
if (pimg->label_len) PUTC(0, pimg->fh);
|
||
|
len = 0;
|
||
|
} else {
|
||
|
const char *p = pimg->label_buf + dot;
|
||
|
n = 1;
|
||
|
for (len = pimg->label_len - dot - 17; len; len--) {
|
||
|
if (*p++ == '.') n++;
|
||
|
}
|
||
|
if (n <= 14) {
|
||
|
PUTC(n, pimg->fh);
|
||
|
len = dot;
|
||
|
} else {
|
||
|
if (pimg->label_len) PUTC(0, pimg->fh);
|
||
|
len = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
n = strlen(s + len);
|
||
|
PUTC(opt, pimg->fh);
|
||
|
if (n < 0xfe) {
|
||
|
PUTC(n, pimg->fh);
|
||
|
} else if (n < 0xffff + 0xfe) {
|
||
|
PUTC(0xfe, pimg->fh);
|
||
|
put16((short)(n - 0xfe), pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0xff, pimg->fh);
|
||
|
put32(n, pimg->fh);
|
||
|
}
|
||
|
fwrite(s + len, n, 1, pimg->fh);
|
||
|
|
||
|
n += len;
|
||
|
pimg->label_len = n;
|
||
|
if (!check_label_space(pimg, n + 1))
|
||
|
return 0; /* FIXME: distinguish out of memory... */
|
||
|
memcpy(pimg->label_buf + len, s + len, n - len + 1);
|
||
|
|
||
|
return !ferror(pimg->fh);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
write_v8label(img *pimg, int opt, int common_flag, size_t common_val,
|
||
|
const char *s)
|
||
|
{
|
||
|
size_t len, del, add;
|
||
|
|
||
|
/* find length of common prefix */
|
||
|
for (len = 0; s[len] == pimg->label_buf[len] && s[len] != '\0'; len++) {
|
||
|
}
|
||
|
|
||
|
SVX_ASSERT(len <= pimg->label_len);
|
||
|
del = pimg->label_len - len;
|
||
|
add = strlen(s + len);
|
||
|
|
||
|
if (add == common_val && del == common_val) {
|
||
|
PUTC(opt | common_flag, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(opt, pimg->fh);
|
||
|
if (del <= 15 && add <= 15 && (del || add)) {
|
||
|
PUTC((del << 4) | add, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0x00, pimg->fh);
|
||
|
if (del < 0xff) {
|
||
|
PUTC(del, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0xff, pimg->fh);
|
||
|
put32(del, pimg->fh);
|
||
|
}
|
||
|
if (add < 0xff) {
|
||
|
PUTC(add, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0xff, pimg->fh);
|
||
|
put32(add, pimg->fh);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (add)
|
||
|
fwrite(s + len, add, 1, pimg->fh);
|
||
|
|
||
|
pimg->label_len = len + add;
|
||
|
if (add > del && !check_label_space(pimg, pimg->label_len + 1))
|
||
|
return 0; /* FIXME: distinguish out of memory... */
|
||
|
|
||
|
memcpy(pimg->label_buf + len, s + len, add + 1);
|
||
|
|
||
|
return !ferror(pimg->fh);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
img_write_item_date_new(img *pimg)
|
||
|
{
|
||
|
int same, unset;
|
||
|
/* Only write dates when they've changed. */
|
||
|
#if IMG_API_VERSION == 0
|
||
|
if (pimg->date1 == pimg->olddate1 && pimg->date2 == pimg->olddate2)
|
||
|
return;
|
||
|
|
||
|
same = (pimg->date1 == pimg->date2);
|
||
|
unset = (pimg->date1 == 0);
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
if (pimg->days1 == pimg->olddays1 && pimg->days2 == pimg->olddays2)
|
||
|
return;
|
||
|
|
||
|
same = (pimg->days1 == pimg->days2);
|
||
|
unset = (pimg->days1 == -1);
|
||
|
#endif
|
||
|
|
||
|
if (same) {
|
||
|
if (unset) {
|
||
|
PUTC(0x10, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0x11, pimg->fh);
|
||
|
#if IMG_API_VERSION == 0
|
||
|
put16(pimg->date1 / 86400 + 25567, pimg->fh);
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
put16(pimg->days1, pimg->fh);
|
||
|
#endif
|
||
|
}
|
||
|
} else {
|
||
|
#if IMG_API_VERSION == 0
|
||
|
int diff = (pimg->date2 - pimg->date1) / 86400;
|
||
|
if (diff > 0 && diff <= 256) {
|
||
|
PUTC(0x12, pimg->fh);
|
||
|
put16(pimg->date1 / 86400 + 25567, pimg->fh);
|
||
|
PUTC(diff - 1, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0x13, pimg->fh);
|
||
|
put16(pimg->date1 / 86400 + 25567, pimg->fh);
|
||
|
put16(pimg->date2 / 86400 + 25567, pimg->fh);
|
||
|
}
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
int diff = pimg->days2 - pimg->days1;
|
||
|
if (diff > 0 && diff <= 256) {
|
||
|
PUTC(0x12, pimg->fh);
|
||
|
put16(pimg->days1, pimg->fh);
|
||
|
PUTC(diff - 1, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0x13, pimg->fh);
|
||
|
put16(pimg->days1, pimg->fh);
|
||
|
put16(pimg->days2, pimg->fh);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->olddate1 = pimg->date1;
|
||
|
pimg->olddate2 = pimg->date2;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->olddays1 = pimg->days1;
|
||
|
pimg->olddays2 = pimg->days2;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
img_write_item_date(img *pimg)
|
||
|
{
|
||
|
int same, unset;
|
||
|
/* Only write dates when they've changed. */
|
||
|
#if IMG_API_VERSION == 0
|
||
|
if (pimg->date1 == pimg->olddate1 && pimg->date2 == pimg->olddate2)
|
||
|
return;
|
||
|
|
||
|
same = (pimg->date1 == pimg->date2);
|
||
|
unset = (pimg->date1 == 0);
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
if (pimg->days1 == pimg->olddays1 && pimg->days2 == pimg->olddays2)
|
||
|
return;
|
||
|
|
||
|
same = (pimg->days1 == pimg->days2);
|
||
|
unset = (pimg->days1 == -1);
|
||
|
#endif
|
||
|
|
||
|
if (same) {
|
||
|
if (img_output_version < 7) {
|
||
|
PUTC(0x20, pimg->fh);
|
||
|
#if IMG_API_VERSION == 0
|
||
|
put32(pimg->date1, pimg->fh);
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
put32((pimg->days1 - 25567) * 86400, pimg->fh);
|
||
|
#endif
|
||
|
} else {
|
||
|
if (unset) {
|
||
|
PUTC(0x24, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0x20, pimg->fh);
|
||
|
#if IMG_API_VERSION == 0
|
||
|
put16(pimg->date1 / 86400 + 25567, pimg->fh);
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
put16(pimg->days1, pimg->fh);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if (img_output_version < 7) {
|
||
|
PUTC(0x21, pimg->fh);
|
||
|
#if IMG_API_VERSION == 0
|
||
|
put32(pimg->date1, pimg->fh);
|
||
|
put32(pimg->date2, pimg->fh);
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
put32((pimg->days1 - 25567) * 86400, pimg->fh);
|
||
|
put32((pimg->days2 - 25567) * 86400, pimg->fh);
|
||
|
#endif
|
||
|
} else {
|
||
|
#if IMG_API_VERSION == 0
|
||
|
int diff = (pimg->date2 - pimg->date1) / 86400;
|
||
|
if (diff > 0 && diff <= 256) {
|
||
|
PUTC(0x21, pimg->fh);
|
||
|
put16(pimg->date1 / 86400 + 25567, pimg->fh);
|
||
|
PUTC(diff - 1, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0x23, pimg->fh);
|
||
|
put16(pimg->date1 / 86400 + 25567, pimg->fh);
|
||
|
put16(pimg->date2 / 86400 + 25567, pimg->fh);
|
||
|
}
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
int diff = pimg->days2 - pimg->days1;
|
||
|
if (diff > 0 && diff <= 256) {
|
||
|
PUTC(0x21, pimg->fh);
|
||
|
put16(pimg->days1, pimg->fh);
|
||
|
PUTC(diff - 1, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0x23, pimg->fh);
|
||
|
put16(pimg->days1, pimg->fh);
|
||
|
put16(pimg->days2, pimg->fh);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
#if IMG_API_VERSION == 0
|
||
|
pimg->olddate1 = pimg->date1;
|
||
|
pimg->olddate2 = pimg->date2;
|
||
|
#else /* IMG_API_VERSION == 1 */
|
||
|
pimg->olddays1 = pimg->days1;
|
||
|
pimg->olddays2 = pimg->days2;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
img_write_item_new(img *pimg, int code, int flags, const char *s,
|
||
|
double x, double y, double z);
|
||
|
static void
|
||
|
img_write_item_v3to7(img *pimg, int code, int flags, const char *s,
|
||
|
double x, double y, double z);
|
||
|
static void
|
||
|
img_write_item_ancient(img *pimg, int code, int flags, const char *s,
|
||
|
double x, double y, double z);
|
||
|
|
||
|
void
|
||
|
img_write_item(img *pimg, int code, int flags, const char *s,
|
||
|
double x, double y, double z)
|
||
|
{
|
||
|
if (!pimg) return;
|
||
|
if (pimg->version >= 8) {
|
||
|
img_write_item_new(pimg, code, flags, s, x, y, z);
|
||
|
} else if (pimg->version >= 3) {
|
||
|
img_write_item_v3to7(pimg, code, flags, s, x, y, z);
|
||
|
} else {
|
||
|
img_write_item_ancient(pimg, code, flags, s, x, y, z);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
img_write_item_new(img *pimg, int code, int flags, const char *s,
|
||
|
double x, double y, double z)
|
||
|
{
|
||
|
switch (code) {
|
||
|
case img_LABEL:
|
||
|
write_v8label(pimg, 0x80 | flags, 0, -1, s);
|
||
|
break;
|
||
|
case img_XSECT: {
|
||
|
INT32_T l, r, u, d, max_dim;
|
||
|
img_write_item_date_new(pimg);
|
||
|
l = (INT32_T)my_lround(pimg->l * 100.0);
|
||
|
r = (INT32_T)my_lround(pimg->r * 100.0);
|
||
|
u = (INT32_T)my_lround(pimg->u * 100.0);
|
||
|
d = (INT32_T)my_lround(pimg->d * 100.0);
|
||
|
if (l < 0) l = -1;
|
||
|
if (r < 0) r = -1;
|
||
|
if (u < 0) u = -1;
|
||
|
if (d < 0) d = -1;
|
||
|
max_dim = max(max(l, r), max(u, d));
|
||
|
flags = (flags & img_XFLAG_END) ? 1 : 0;
|
||
|
if (max_dim >= 32768) flags |= 2;
|
||
|
write_v8label(pimg, 0x30 | flags, 0, -1, s);
|
||
|
if (flags & 2) {
|
||
|
/* Big passage! Need to use 4 bytes. */
|
||
|
put32(l, pimg->fh);
|
||
|
put32(r, pimg->fh);
|
||
|
put32(u, pimg->fh);
|
||
|
put32(d, pimg->fh);
|
||
|
} else {
|
||
|
put16(l, pimg->fh);
|
||
|
put16(r, pimg->fh);
|
||
|
put16(u, pimg->fh);
|
||
|
put16(d, pimg->fh);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
case img_MOVE:
|
||
|
PUTC(15, pimg->fh);
|
||
|
break;
|
||
|
case img_LINE:
|
||
|
img_write_item_date_new(pimg);
|
||
|
if (pimg->style != pimg->oldstyle) {
|
||
|
switch (pimg->style) {
|
||
|
case img_STYLE_NORMAL:
|
||
|
case img_STYLE_DIVING:
|
||
|
case img_STYLE_CARTESIAN:
|
||
|
case img_STYLE_CYLPOLAR:
|
||
|
case img_STYLE_NOSURVEY:
|
||
|
PUTC(pimg->style, pimg->fh);
|
||
|
break;
|
||
|
}
|
||
|
pimg->oldstyle = pimg->style;
|
||
|
}
|
||
|
write_v8label(pimg, 0x40 | flags, 0x20, 0x00, s ? s : "");
|
||
|
break;
|
||
|
default: /* ignore for now */
|
||
|
return;
|
||
|
}
|
||
|
write_coord(pimg->fh, x, y, z);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
img_write_item_v3to7(img *pimg, int code, int flags, const char *s,
|
||
|
double x, double y, double z)
|
||
|
{
|
||
|
switch (code) {
|
||
|
case img_LABEL:
|
||
|
write_v3label(pimg, 0x40 | flags, s);
|
||
|
break;
|
||
|
case img_XSECT: {
|
||
|
INT32_T l, r, u, d, max_dim;
|
||
|
/* Need at least version 5 for img_XSECT. */
|
||
|
if (pimg->version < 5) return;
|
||
|
img_write_item_date(pimg);
|
||
|
l = (INT32_T)my_lround(pimg->l * 100.0);
|
||
|
r = (INT32_T)my_lround(pimg->r * 100.0);
|
||
|
u = (INT32_T)my_lround(pimg->u * 100.0);
|
||
|
d = (INT32_T)my_lround(pimg->d * 100.0);
|
||
|
if (l < 0) l = -1;
|
||
|
if (r < 0) r = -1;
|
||
|
if (u < 0) u = -1;
|
||
|
if (d < 0) d = -1;
|
||
|
max_dim = max(max(l, r), max(u, d));
|
||
|
flags = (flags & img_XFLAG_END) ? 1 : 0;
|
||
|
if (max_dim >= 32768) flags |= 2;
|
||
|
write_v3label(pimg, 0x30 | flags, s);
|
||
|
if (flags & 2) {
|
||
|
/* Big passage! Need to use 4 bytes. */
|
||
|
put32(l, pimg->fh);
|
||
|
put32(r, pimg->fh);
|
||
|
put32(u, pimg->fh);
|
||
|
put32(d, pimg->fh);
|
||
|
} else {
|
||
|
put16(l, pimg->fh);
|
||
|
put16(r, pimg->fh);
|
||
|
put16(u, pimg->fh);
|
||
|
put16(d, pimg->fh);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
case img_MOVE:
|
||
|
PUTC(15, pimg->fh);
|
||
|
break;
|
||
|
case img_LINE:
|
||
|
if (pimg->version >= 4) {
|
||
|
img_write_item_date(pimg);
|
||
|
}
|
||
|
write_v3label(pimg, 0x80 | flags, s ? s : "");
|
||
|
break;
|
||
|
default: /* ignore for now */
|
||
|
return;
|
||
|
}
|
||
|
write_coord(pimg->fh, x, y, z);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
img_write_item_ancient(img *pimg, int code, int flags, const char *s,
|
||
|
double x, double y, double z)
|
||
|
{
|
||
|
size_t len;
|
||
|
INT32_T opt = 0;
|
||
|
SVX_ASSERT(pimg->version > 0);
|
||
|
switch (code) {
|
||
|
case img_LABEL:
|
||
|
if (pimg->version == 1) {
|
||
|
/* put a move before each label */
|
||
|
img_write_item_ancient(pimg, img_MOVE, 0, NULL, x, y, z);
|
||
|
put32(2, pimg->fh);
|
||
|
fputsnl(s, pimg->fh);
|
||
|
return;
|
||
|
}
|
||
|
len = strlen(s);
|
||
|
if (len > 255 || strchr(s, '\n')) {
|
||
|
/* long label - not in early incarnations of v2 format, but few
|
||
|
* 3d files will need these, so better not to force incompatibility
|
||
|
* with a new version I think... */
|
||
|
PUTC(7, pimg->fh);
|
||
|
PUTC(flags, pimg->fh);
|
||
|
put32(len, pimg->fh);
|
||
|
fputs(s, pimg->fh);
|
||
|
} else {
|
||
|
PUTC(0x40 | (flags & 0x3f), pimg->fh);
|
||
|
fputsnl(s, pimg->fh);
|
||
|
}
|
||
|
opt = 0;
|
||
|
break;
|
||
|
case img_MOVE:
|
||
|
opt = 4;
|
||
|
break;
|
||
|
case img_LINE:
|
||
|
if (pimg->version > 1) {
|
||
|
opt = 0x80 | (flags & 0x3f);
|
||
|
break;
|
||
|
}
|
||
|
opt = 5;
|
||
|
break;
|
||
|
default: /* ignore for now */
|
||
|
return;
|
||
|
}
|
||
|
if (pimg->version == 1) {
|
||
|
put32(opt, pimg->fh);
|
||
|
} else {
|
||
|
if (opt) PUTC(opt, pimg->fh);
|
||
|
}
|
||
|
write_coord(pimg->fh, x, y, z);
|
||
|
}
|
||
|
|
||
|
/* Write error information for the current traverse
|
||
|
* n_legs is the number of legs in the traverse
|
||
|
* length is the traverse length (in m)
|
||
|
* E is the ratio of the observed misclosure to the theoretical one
|
||
|
* H is the ratio of the observed horizontal misclosure to the theoretical one
|
||
|
* V is the ratio of the observed vertical misclosure to the theoretical one
|
||
|
*/
|
||
|
void
|
||
|
img_write_errors(img *pimg, int n_legs, double length,
|
||
|
double E, double H, double V)
|
||
|
{
|
||
|
PUTC((pimg->version >= 8 ? 0x1f : 0x22), pimg->fh);
|
||
|
put32(n_legs, pimg->fh);
|
||
|
put32((INT32_T)my_lround(length * 100.0), pimg->fh);
|
||
|
put32((INT32_T)my_lround(E * 100.0), pimg->fh);
|
||
|
put32((INT32_T)my_lround(H * 100.0), pimg->fh);
|
||
|
put32((INT32_T)my_lround(V * 100.0), pimg->fh);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
img_close(img *pimg)
|
||
|
{
|
||
|
int result = 1;
|
||
|
if (pimg) {
|
||
|
if (pimg->fh) {
|
||
|
if (pimg->fRead) {
|
||
|
osfree(pimg->survey);
|
||
|
osfree(pimg->title);
|
||
|
osfree(pimg->cs);
|
||
|
osfree(pimg->datestamp);
|
||
|
} else {
|
||
|
/* write end of data marker */
|
||
|
switch (pimg->version) {
|
||
|
case 1:
|
||
|
put32((INT32_T)-1, pimg->fh);
|
||
|
break;
|
||
|
default:
|
||
|
if (pimg->version <= 7 ?
|
||
|
(pimg->label_len != 0) :
|
||
|
(pimg->style != img_STYLE_NORMAL)) {
|
||
|
PUTC(0, pimg->fh);
|
||
|
}
|
||
|
/* FALL THROUGH */
|
||
|
case 2:
|
||
|
PUTC(0, pimg->fh);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (ferror(pimg->fh)) result = 0;
|
||
|
if (pimg->close_func && pimg->close_func(pimg->fh))
|
||
|
result = 0;
|
||
|
if (!result) img_errno = pimg->fRead ? IMG_READERROR : IMG_WRITEERROR;
|
||
|
}
|
||
|
osfree(pimg->label_buf);
|
||
|
osfree(pimg->filename_opened);
|
||
|
osfree(pimg);
|
||
|
}
|
||
|
return result;
|
||
|
}
|