static void report_missing_export(prefix *pfx, int depth) { const char *filename_store = file.filename; unsigned int line_store = file.line; prefix *survey = pfx; char *s; int i; for (i = depth + 1; i; i--) { survey = survey->up; SVX_ASSERT(survey); } s = osstrdup(sprint_prefix(survey)); if (survey->filename) { file.filename = survey->filename; file.line = survey->line; } compile_error(/*Station “%s” not exported from survey “%s”*/26, sprint_prefix(pfx), s); if (survey->filename) { file.filename = filename_store; file.line = line_store; } osfree(s); }
static void add_label(point *p, const char *label, int flags) { stn *s = osnew(stn); s->label = osstrdup(label); s->flags = flags; s->next = p->stns; p->stns = s; }
static void check_node(prefix *p) { if (!p->pos) { if (!TSTBIT(p->sflags, SFLAGS_SURVEY)) { /* Could do away with the SFLAGS_SURVEY check and check * p->min_export instead of p->max_export I think ... */ if (TSTBIT(p->sflags, SFLAGS_ENTRANCE) || p->max_export) { /* p is a station which was referred to in "*entrance" and/or * "*export" but not elsewhere (otherwise it'd have a position). * p could also be a survey (SFLAGS_SURVEY) or be mentioned as * a station, but only in a line of data which was rejected * because of an error. */ warning_in_file(p->filename, p->line, /*Station “%s” referred to by *entrance or *export but never used*/190, sprint_prefix(p)); } } } else { /* Do we need to worry about export violations in hanging surveys? */ if (fExportUsed) { #if 0 printf("L min %d max %d pfx %s\n", p->min_export, p->max_export, sprint_prefix(p)); #endif if ((p->min_export > 1 && p->min_export != USHRT_MAX) || (p->min_export == 0 && p->max_export)) { char *s; prefix *where = p->up; int msgno; SVX_ASSERT(where); s = osstrdup(sprint_prefix(where)); /* Report better when station called 2.1 for example */ while (!where->filename && where->up) where = where->up; if (TSTBIT(where->sflags, SFLAGS_PREFIX_ENTERED)) { msgno = /*Station “%s” not exported from survey “%s”*/26; } else { /* TRANSLATORS: This error occurs if there's an attempt to * export a station from a survey which doesn't actually exist. */ msgno = /*Reference to station “%s” from non-existent survey “%s”*/286; } compile_error_pfx(where, msgno, sprint_prefix(p), s); osfree(s); } } if (TSTBIT(p->sflags, SFLAGS_SUSPECTTYPO)) { /* TRANSLATORS: Here "station" is a survey station, not a train station. */ warning_in_file(p->filename, p->line, /*Station “%s” referred to just once, with an explicit survey name - typo?*/70, sprint_prefix(p)); } } }
static void filename_register_output_with_fh(const char *fnm, FILE *fh) { filelist *p = osnew(filelist); SVX_ASSERT(fnm); p->fnm = osstrdup(fnm); p->fh = fh; p->next = flhead; flhead = p; }
static void tree_insert(const char *name, const img_point *pt) { int v = hash_string(name) & (TREE_SIZE - 1); station * stn = osnew(station); stn->name = osstrdup(name); stn->pt = *pt; stn->next = htab[v]; htab[v] = stn; }
void filename_register_output(const char *fnm) { filelist *p = osnew(filelist); SVX_ASSERT(fnm); p->fnm = osstrdup(fnm); p->fh = NULL; p->next = flhead; flhead = p; }
static void tree_remove(const char *name, const img_point *pt) { /* We need to handle duplicate labels - normal .3d files shouldn't have them * (though some older ones do due to a couple of bugs in earlier versions of * Survex) but extended .3d files repeat the label where a loop is broken, * and data read from foreign formats might repeat labels. */ int v = hash_string(name) & (TREE_SIZE - 1); station **prev; station *p; station **found = NULL; bool was_close_enough = fFalse; for (prev = &htab[v]; *prev; prev = &((*prev)->next)) { if (strcmp((*prev)->name, name) == 0) { /* Handle stations with the same name. Stations are inserted at the * start of the linked list, so pick the *last* matching station in * the list as then we match the first stations with the same name in * each file. */ if (close_enough(pt, &((*prev)->pt))) { found = prev; was_close_enough = fTrue; } else if (!was_close_enough) { found = prev; } } } if (!found) { added *add = osnew(added); add->name = osstrdup(name); add->next = added_list; added_list = add; c_added++; fChanged = fTrue; return; } if (!was_close_enough) { printf(msg(/*Moved by (%3.2f,%3.2f,%3.2f): %s*/500), pt->x - (*found)->pt.x, pt->y - (*found)->pt.y, pt->z - (*found)->pt.z, name); putnl(); fChanged = fTrue; } osfree((*found)->name); p = *found; *found = p->next; osfree(p); }
static void do_range(int d, int msgno) { /* sprint_prefix uses a single buffer, so to report two stations in one * message we need to make a temporary copy of the string for one of them. */ char * pfx_hi = osstrdup(sprint_prefix(pfxHi[d])); char * pfx_lo = sprint_prefix(pfxLo[d]); printf(msg(msgno), max[d] - min[d], pfx_hi, max[d], pfx_lo, min[d]); osfree(pfx_hi); putnl(); }
static void do_range(int d, int msgno, real length_factor, const char * units) { /* sprint_prefix uses a single buffer, so to report two stations in one * message we need to make a temporary copy of the string for one of them. */ char * pfx_hi = osstrdup(sprint_prefix(pfxHi[d])); char * pfx_lo = sprint_prefix(pfxLo[d]); real hi = max[d] * length_factor; real lo = min[d] * length_factor; printf(msg(msgno), hi - lo, units, pfx_hi, hi, units, pfx_lo, lo, units); osfree(pfx_hi); putnl(); }
extern char * leaf_from_fnm(const char *fnm) { const char *lf; lf = strrchr(fnm, FNM_SEP_LEV); if (lf) fnm = lf + 1; #ifdef FNM_SEP_LEV2 lf = strrchr(fnm, FNM_SEP_LEV2); if (lf) fnm = lf + 1; #endif #ifdef FNM_SEP_DRV lf = strrchr(fnm, FNM_SEP_DRV); if (lf) fnm = lf + 1; #endif return osstrdup(fnm); }
/* fopen file, found using pth and fnm * fnmUsed is used to return filename used to open file (ignored if NULL) * or NULL if file didn't open */ extern FILE * fopenWithPthAndExt(const char *pth, const char *fnm, const char *ext, const char *mode, char **fnmUsed) { char *fnmFull = NULL; FILE *fh = NULL; bool fAbs; /* if no pth treat fnm as absolute */ fAbs = (pth == NULL || *pth == '\0' || fAbsoluteFnm(fnm)); /* if appropriate, try it without pth */ if (fAbs) { fh = fopen_not_dir(fnm, mode); if (fh) { if (fnmUsed) fnmFull = osstrdup(fnm); } else { if (ext && *ext) { /* we've been given an extension so try using it */ fnmFull = add_ext(fnm, ext); fh = fopen_not_dir(fnmFull, mode); } } } else { /* try using path given - first of all without the extension */ fnmFull = use_path(pth, fnm); fh = fopen_not_dir(fnmFull, mode); if (!fh) { if (ext && *ext) { /* we've been given an extension so try using it */ char *fnmTmp; fnmTmp = fnmFull; fnmFull = add_ext(fnmFull, ext); osfree(fnmTmp); fh = fopen_not_dir(fnmFull, mode); } } } /* either it opened or didn't. If not, fh == NULL from fopen_not_dir() */ /* free name if it didn't open or name isn't wanted */ if (fh == NULL || fnmUsed == NULL) osfree(fnmFull); if (fnmUsed) *fnmUsed = (fh ? fnmFull : NULL); return fh; }
static const char * find_prefix(const char *prefix) { pfx *p; int hash; SVX_ASSERT(prefix); hash = hash_string(prefix) & (HTAB_SIZE - 1); for (p = htab[hash]; p; p = p->next) { if (strcmp(prefix, p->label) == 0) return p->label; } p = osnew(pfx); p->label = osstrdup(prefix); p->next = htab[hash]; htab[hash] = p; return p->label; }
extern char * base_from_fnm(const char *fnm) { char *p; p = strrchr(fnm, FNM_SEP_EXT); /* Trim off any leaf extension, but dirs can have extensions too */ if (p && !strchr(p, FNM_SEP_LEV) #ifdef FNM_SEP_LEV2 && !strchr(p, FNM_SEP_LEV2) #endif ) { size_t len = (const char *)p - fnm; p = osmalloc(len + 1); memcpy(p, fnm, len); p[len] = '\0'; return p; } return osstrdup(fnm); }
/* Like fopenWithPthAndExt except that "foreign" paths are translated to * native ones (e.g. on Unix dir\file.ext -> dir/file.ext) */ FILE * fopen_portable(const char *pth, const char *fnm, const char *ext, const char *mode, char **fnmUsed) { FILE *fh = fopenWithPthAndExt(pth, fnm, ext, mode, fnmUsed); if (fh == NULL) { #if OS_UNIX int f_changed = 0; char *fnm_trans, *p; fnm_trans = osstrdup(fnm); for (p = fnm_trans; *p; p++) { switch (*p) { case '\\': /* swap a backslash to a forward slash */ *p = '/'; f_changed = 1; break; } } if (f_changed) fh = fopenWithPthAndExt(pth, fnm_trans, ext, mode, fnmUsed); /* as a last ditch measure, try lowercasing the filename */ if (fh == NULL) { f_changed = 0; for (p = fnm_trans; *p ; p++) { unsigned char ch = *p; if (isupper(ch)) { *p = tolower(ch); f_changed = 1; } } if (f_changed) fh = fopenWithPthAndExt(pth, fnm_trans, ext, mode, fnmUsed); } osfree(fnm_trans); #endif } return fh; }
void msg_init(char * const *argv) { char *p; SVX_ASSERT(argv); /* Point to argv[0] itself so we report a more helpful error if the * code to work out the clean appname generates a signal */ appname_copy = argv[0]; #if OS_UNIX /* use name as-is on Unix - programs run from path get name as supplied */ appname_copy = osstrdup(argv[0]); #else /* use the lower-cased leafname on other platforms */ p = leaf_from_fnm(argv[0]); appname_copy = p; while (*p) { *p = tolower(*p); ++p; } #endif /* shortcut --version so you can check the version number even when the * correct message file can't be found... */ if (argv[1] && strcmp(argv[1], "--version") == 0) { cmdline_version(); exit(0); } if (argv[0]) { exe_pth = path_from_fnm(argv[0]); #ifdef MACOSX_BUNDLE /* If we're being built into a bundle, always look relative to * the path to the binary. */ #ifdef AVEN /* Aven is packaged as an application, so we must look inside there. */ pth_cfg_files = use_path(exe_pth, "../Resources"); #else pth_cfg_files = use_path(exe_pth, "share/survex"); #endif #elif OS_UNIX && defined DATADIR && defined PACKAGE bool free_pth = fFalse; char *pth = getenv("srcdir"); if (!pth || !pth[0]) { pth = path_from_fnm(argv[0]); free_pth = fTrue; } if (pth[0]) { struct stat buf; #if OS_UNIX_MACOSX /* On MacOS X the programs may be installed anywhere, with the * share directory and the binaries in the same directory. */ p = use_path(pth, "share/survex/en.msg"); if (lstat(p, &buf) == 0 && S_ISREG(buf.st_mode)) { pth_cfg_files = use_path(pth, "share/survex"); goto macosx_got_msg; } osfree(p); /* The cavern which aven runs is a hardlinked copy alongside * the aven binary. */ p = use_path(pth, "../Resources/en.msg"); if (lstat(p, &buf) == 0 && S_ISREG(buf.st_mode)) { pth_cfg_files = use_path(pth, "../Resources"); goto macosx_got_msg; } osfree(p); #endif /* If we're run with an explicit path, check if "../lib/en.msg" * from the program's path exists, and if so look there for * support files - this allows us to test binaries in the build * tree easily. */ p = use_path(pth, "../lib/en.msg"); if (lstat(p, &buf) == 0) { #ifdef S_ISREG /* POSIX way */ if (S_ISREG(buf.st_mode)) { pth_cfg_files = use_path(pth, "../lib"); } #else /* BSD way */ if ((buf.st_mode & S_IFMT) == S_IFREG) { pth_cfg_files = use_path(pth, "../lib"); } #endif } #if defined(__GNUC__) && defined(__APPLE_CC__) macosx_got_msg: #endif osfree(p); } if (free_pth) osfree(pth); #elif OS_WIN32 DWORD len = 256; char *buf = NULL, *modname; while (1) { DWORD got; buf = osrealloc(buf, len); got = GetModuleFileName(NULL, buf, len); if (got < len) break; len += len; } modname = buf; /* Strange Win32 nastiness - strip prefix "\\?\" if present */ if (strncmp(modname, "\\\\?\\", 4) == 0) modname += 4; pth_cfg_files = path_from_fnm(modname); osfree(buf); #else /* Get the path to the support files from argv[0] */ pth_cfg_files = path_from_fnm(argv[0]); #endif } msg_lang = getenv("SURVEXLANG"); #ifdef DEBUG fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)"); #endif msg_lang_explicit = fTrue; if (!msg_lang || !*msg_lang) { msg_lang_explicit = fFalse; msg_lang = getenv("LC_ALL"); } if (!msg_lang || !*msg_lang) { msg_lang = getenv("LC_MESSAGES"); if (!msg_lang || !*msg_lang) { msg_lang = getenv("LANG"); /* Something (AutoCAD?) on Microsoft Windows sets LANG to a number. */ if (msg_lang && !isalpha(msg_lang[0])) msg_lang = NULL; } if (!msg_lang || !*msg_lang) { #if OS_WIN32 LCID locid; #endif #ifdef DEFAULTLANG msg_lang = STRING(DEFAULTLANG); #else msg_lang = "en"; #endif #if OS_WIN32 /* GetUserDefaultUILanguage() requires Microsoft Windows 2000 or * newer. For older versions, we use GetUserDefaultLCID(). */ { HMODULE win32 = GetModuleHandle(TEXT("kernel32.dll")); FARPROC f = GetProcAddress(win32, "GetUserDefaultUILanguage"); if (f) { typedef LANGID (WINAPI *func_GetUserDefaultUILanguage)(void); func_GetUserDefaultUILanguage g; g = (func_GetUserDefaultUILanguage)f; locid = g(); } else { locid = GetUserDefaultLCID(); } } if (locid) { WORD langid = LANGIDFROMLCID(locid); switch (PRIMARYLANGID(langid)) { case LANG_BULGARIAN: msg_lang = "bg"; break; /* older mingw compilers don't seem to supply this value */ #ifndef LANG_CATALAN # define LANG_CATALAN 0x03 #endif case LANG_CATALAN: msg_lang = "ca"; break; case LANG_CHINESE: msg_lang = "zh_CN"; break; case LANG_ENGLISH: if (SUBLANGID(langid) == SUBLANG_ENGLISH_US) msg_lang = "en_US"; else msg_lang = "en"; break; case LANG_FRENCH: msg_lang = "fr"; break; case LANG_GERMAN: switch (SUBLANGID(langid)) { case SUBLANG_GERMAN_SWISS: msg_lang = "de_CH"; break; default: msg_lang = "de"; } break; case LANG_GREEK: msg_lang = "el"; break; case LANG_HUNGARIAN: msg_lang = "hu"; break; case LANG_INDONESIAN: msg_lang = "id"; break; case LANG_ITALIAN: msg_lang = "it"; break; case LANG_POLISH: msg_lang = "pl"; break; case LANG_PORTUGUESE: if (SUBLANGID(langid) == SUBLANG_PORTUGUESE_BRAZILIAN) msg_lang = "pt_BR"; else msg_lang = "pt"; break; case LANG_ROMANIAN: msg_lang = "ro"; break; case LANG_RUSSIAN: msg_lang = "ru"; break; case LANG_SLOVAK: msg_lang = "sk"; break; case LANG_SPANISH: msg_lang = "es"; break; } } #endif } } #ifdef DEBUG fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)"); #endif /* On Mandrake LANG defaults to C */ if (strcmp(msg_lang, "C") == 0) msg_lang = "en"; { /* If msg_lang has a country code, snip it out to give msg_lang2. */ size_t b = 0; while (isalpha((unsigned char)msg_lang[b])) { ++b; } if (msg_lang[b] == '_') { char * tmp; size_t e = b + 1; while (isalpha((unsigned char)msg_lang[e])) { ++e; } tmp = osstrdup(msg_lang); memmove(tmp + b, tmp + e, strlen(tmp + e) + 1); msg_lang2 = tmp; } } #ifdef LC_MESSAGES /* try to setlocale() appropriately too */ if (!setlocale(LC_MESSAGES, msg_lang)) { if (msg_lang2) { (void)setlocale(LC_MESSAGES, msg_lang2); } } #endif select_charset(default_charset()); }
static void cmd_data(void) { static sztok dtab[] = { {"ALTITUDE", Dz }, {"BACKBEARING", BackComp }, {"BACKCLINO", BackClino }, /* alternative name */ {"BACKCOMPASS", BackComp }, /* alternative name */ {"BACKGRADIENT", BackClino }, {"BEARING", Comp }, {"CEILING", Up }, /* alternative name */ {"CLINO", Clino }, /* alternative name */ {"COMPASS", Comp }, /* alternative name */ {"COUNT", Count }, /* FrCount&ToCount in multiline */ {"DEPTH", Depth }, /* FrDepth&ToDepth in multiline */ {"DEPTHCHANGE", DepthChange }, {"DIRECTION", Dir }, {"DOWN", Down }, {"DX", Dx }, {"DY", Dy }, {"DZ", Dz }, {"EASTING", Dx }, {"FLOOR", Down }, /* alternative name */ {"FROM", Fr }, {"FROMCOUNT", FrCount }, {"FROMDEPTH", FrDepth }, {"GRADIENT", Clino }, {"IGNORE", Ignore }, {"IGNOREALL", IgnoreAll }, {"LEFT", Left }, {"LENGTH", Tape }, {"NEWLINE", Newline }, {"NORTHING", Dy }, {"RIGHT", Right }, {"STATION", Station }, /* Fr&To in multiline */ {"TAPE", Tape }, /* alternative name */ {"TO", To }, {"TOCOUNT", ToCount }, {"TODEPTH", ToDepth }, {"UP", Up }, {NULL, End } }; #define MASK_stns BIT(Fr) | BIT(To) | BIT(Station) #define MASK_tape BIT(Tape) | BIT(FrCount) | BIT(ToCount) | BIT(Count) #define MASK_dpth BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange) #define MASK_comp BIT(Comp) | BIT(BackComp) #define MASK_clin BIT(Clino) | BIT(BackClino) #define MASK_NORMAL MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_clin #define MASK_DIVING MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_dpth #define MASK_CARTESIAN MASK_stns | BIT(Dx) | BIT(Dy) | BIT(Dz) #define MASK_CYLPOLAR MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_dpth #define MASK_PASSAGE BIT(Station) | BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down) #define MASK_NOSURVEY MASK_stns /* readings which may be given for each style */ static const unsigned long mask[] = { MASK_NORMAL, MASK_DIVING, MASK_CARTESIAN, MASK_CYLPOLAR, MASK_NOSURVEY, MASK_PASSAGE }; /* readings which may be omitted for each style */ static const unsigned long mask_optional[] = { BIT(Dir) | BIT(Clino) | BIT(BackClino), BIT(Dir), 0, BIT(Dir), 0, 0 /* BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down), */ }; /* all valid readings */ static const unsigned long mask_all[] = { MASK_NORMAL | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), MASK_DIVING | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), MASK_CARTESIAN | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), MASK_CYLPOLAR | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), MASK_NOSURVEY | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), MASK_PASSAGE | BIT(Ignore) | BIT(IgnoreAll) | BIT(End) }; #define STYLE_DEFAULT -2 #define STYLE_UNKNOWN -1 static sztok styletab[] = { {"CARTESIAN", STYLE_CARTESIAN }, {"CYLPOLAR", STYLE_CYLPOLAR }, {"DEFAULT", STYLE_DEFAULT }, {"DIVING", STYLE_DIVING }, {"NORMAL", STYLE_NORMAL }, {"NOSURVEY", STYLE_NOSURVEY }, {"PASSAGE", STYLE_PASSAGE }, {"TOPOFIL", STYLE_NORMAL }, {NULL, STYLE_UNKNOWN } }; #define m_multi (BIT(Station) | BIT(Count) | BIT(Depth)) int style, k = 0, kMac; reading *new_order, d; unsigned long mUsed = 0; char *style_name; /* after a bad *data command ignore survey data until the next * *data command to avoid an avalanche of errors */ pcs->style = STYLE_IGNORE; kMac = 6; /* minimum for NORMAL style */ new_order = osmalloc(kMac * sizeof(reading)); get_token(); style = match_tok(styletab, TABSIZE(styletab)); if (style == STYLE_DEFAULT) { default_style(pcs); return; } if (style == STYLE_UNKNOWN) { file.lpos += strlen(buffer); compile_error_skip(-/*Data style “%s” unknown*/65, buffer); return; } skipblanks(); #ifndef NO_DEPRECATED /* Olde syntax had optional field for survey grade, so allow an omit * but issue a warning about it */ if (isOmit(ch)) { static int data_depr_count = 0; if (data_depr_count < 5) { file.lpos += strlen(buffer); compile_warning(-/*“*data %s %c …” is deprecated - use “*data %s …” instead*/104, buffer, ch, buffer); if (++data_depr_count == 5) compile_warning(/*Further uses of this deprecated feature will not be reported*/95); } nextch(); } #endif style_name = osstrdup(buffer); do { filepos fp; get_pos(&fp); get_token(); d = match_tok(dtab, TABSIZE(dtab)); /* only token allowed after IGNOREALL is NEWLINE */ if (k && new_order[k - 1] == IgnoreAll && d != Newline) { set_pos(&fp); break; } /* Note: an unknown token is reported as trailing garbage */ if (!TSTBIT(mask_all[style], d)) { file.lpos += strlen(buffer); compile_error_skip(-/*Reading “%s” not allowed in data style “%s”*/63, buffer, style_name); osfree(style_name); osfree(new_order); return; } if (TSTBIT(mUsed, Newline) && TSTBIT(m_multi, d)) { /* e.g. "*data diving station newline tape depth compass" */ file.lpos += strlen(buffer); compile_error_skip(-/*Reading “%s” must occur before NEWLINE*/225, buffer); osfree(style_name); osfree(new_order); return; } /* Check for duplicates unless it's a special reading: * IGNOREALL,IGNORE (duplicates allowed) ; END (not possible) */ if (!((BIT(Ignore) | BIT(End) | BIT(IgnoreAll)) & BIT(d))) { if (TSTBIT(mUsed, d)) { file.lpos += strlen(buffer); compile_error_skip(-/*Duplicate reading “%s”*/67, buffer); osfree(style_name); osfree(new_order); return; } else { /* Check for previously listed readings which are incompatible * with this one - e.g. Count vs FrCount */ bool fBad = fFalse; switch (d) { case Station: if (mUsed & (BIT(Fr) | BIT(To))) fBad = fTrue; break; case Fr: case To: if (TSTBIT(mUsed, Station)) fBad = fTrue; break; case Count: if (mUsed & (BIT(FrCount) | BIT(ToCount) | BIT(Tape))) fBad = fTrue; break; case FrCount: case ToCount: if (mUsed & (BIT(Count) | BIT(Tape))) fBad = fTrue; break; case Depth: if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(DepthChange))) fBad = fTrue; break; case FrDepth: case ToDepth: if (mUsed & (BIT(Depth) | BIT(DepthChange))) fBad = fTrue; break; case DepthChange: if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(Depth))) fBad = fTrue; break; case Newline: if (mUsed & ~m_multi) { /* e.g. "*data normal from to tape newline compass clino" */ file.lpos += strlen(buffer); compile_error_skip(-/*NEWLINE can only be preceded by STATION, DEPTH, and COUNT*/226); osfree(style_name); osfree(new_order); return; } if (k == 0) { file.lpos += strlen(buffer); compile_error_skip(-/*NEWLINE can’t be the first reading*/222); osfree(style_name); osfree(new_order); return; } break; default: /* avoid compiler warnings about unhandled enums */ break; } if (fBad) { /* Not entirely happy with phrasing this... */ file.lpos += strlen(buffer); compile_error_skip(-/*Reading “%s” duplicates previous reading(s)*/77, buffer); osfree(style_name); osfree(new_order); return; } mUsed |= BIT(d); /* used to catch duplicates */ } } if (k && new_order[k - 1] == IgnoreAll) { SVX_ASSERT(d == Newline); k--; d = IgnoreAllAndNewLine; } if (k >= kMac) { kMac = kMac * 2; new_order = osrealloc(new_order, kMac * sizeof(reading)); } new_order[k++] = d; } while (d != End); if (k >= 2 && new_order[k - 2] == Newline) { file.lpos += strlen(buffer); compile_error_skip(-/*NEWLINE can’t be the last reading*/223); osfree(style_name); osfree(new_order); return; } if (style == STYLE_NOSURVEY) { if (TSTBIT(mUsed, Station)) { if (k >= kMac) { kMac = kMac * 2; new_order = osrealloc(new_order, kMac * sizeof(reading)); } new_order[k - 1] = Newline; new_order[k++] = End; } } else if (style == STYLE_PASSAGE) { /* Station doesn't mean "multiline" for STYLE_PASSAGE. */ } else if (!TSTBIT(mUsed, Newline) && (m_multi & mUsed)) { /* This is for when they write * *data normal station tape compass clino * (i.e. no newline, but interleaved readings) */ compile_error_skip(/*Interleaved readings, but no NEWLINE*/224); osfree(style_name); osfree(new_order); return; } #if 0 printf("mUsed = 0x%x\n", mUsed); #endif /* Check the supplied readings form a sufficient set. */ if (style != STYLE_PASSAGE) { if (mUsed & (BIT(Fr) | BIT(To))) mUsed |= BIT(Station); else if (TSTBIT(mUsed, Station)) mUsed |= BIT(Fr) | BIT(To); } if (mUsed & (BIT(Comp) | BIT(BackComp))) mUsed |= BIT(Comp) | BIT(BackComp); if (mUsed & (BIT(Clino) | BIT(BackClino))) mUsed |= BIT(Clino) | BIT(BackClino); if (mUsed & (BIT(FrDepth) | BIT(ToDepth))) mUsed |= BIT(Depth) | BIT(DepthChange); else if (TSTBIT(mUsed, Depth)) mUsed |= BIT(FrDepth) | BIT(ToDepth) | BIT(DepthChange); else if (TSTBIT(mUsed, DepthChange)) mUsed |= BIT(FrDepth) | BIT(ToDepth) | BIT(Depth); if (mUsed & (BIT(FrCount) | BIT(ToCount))) mUsed |= BIT(Count) | BIT(Tape); else if (TSTBIT(mUsed, Count)) mUsed |= BIT(FrCount) | BIT(ToCount) | BIT(Tape); else if (TSTBIT(mUsed, Tape)) mUsed |= BIT(FrCount) | BIT(ToCount) | BIT(Count); #if 0 printf("mUsed = 0x%x, opt = 0x%x, mask = 0x%x\n", mUsed, mask_optional[style], mask[style]); #endif if (((mUsed &~ BIT(Newline)) | mask_optional[style]) != mask[style]) { /* Test should only fail with too few bits set, not too many */ SVX_ASSERT((((mUsed &~ BIT(Newline)) | mask_optional[style]) &~ mask[style]) == 0); compile_error_skip(/*Too few readings for data style “%s”*/64, style_name); osfree(style_name); osfree(new_order); return; } /* don't free default ordering or ordering used by parent */ if (pcs->ordering != default_order && !(pcs->next && pcs->next->ordering == pcs->ordering)) osfree(pcs->ordering); pcs->style = style; pcs->ordering = new_order; osfree(style_name); if (style == STYLE_PASSAGE) { lrudlist * new_psg = osnew(lrudlist); new_psg->tube = NULL; new_psg->next = model; model = new_psg; next_lrud = &(new_psg->tube); } }
/* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */ extern prefix * read_prefix(unsigned pfx_flags) { bool f_optional = !!(pfx_flags & PFX_OPT); bool fSurvey = !!(pfx_flags & PFX_SURVEY); bool fSuspectTypo = !!(pfx_flags & PFX_SUSPECT_TYPO); prefix *back_ptr, *ptr; char *name; size_t name_len = 32; size_t i; bool fNew; bool fImplicitPrefix = fTrue; int depth = -1; filepos fp_firstsep; skipblanks(); #ifndef NO_DEPRECATED if (isRoot(ch)) { if (!(pfx_flags & PFX_ALLOW_ROOT)) { compile_diagnostic(DIAG_ERR|DIAG_COL, /*ROOT is deprecated*/25); LONGJMP(file.jbSkipLine); } if (root_depr_count < 5) { compile_diagnostic(DIAG_WARN|DIAG_COL, /*ROOT is deprecated*/25); if (++root_depr_count == 5) compile_diagnostic(DIAG_WARN, /*Further uses of this deprecated feature will not be reported*/95); } nextch(); ptr = root; if (!isNames(ch)) { if (!isSep(ch)) return ptr; /* Allow optional SEPARATOR after ROOT */ get_pos(&fp_firstsep); nextch(); } fImplicitPrefix = fFalse; #else if (0) { #endif } else { if ((pfx_flags & PFX_ANON) && (isSep(ch) || (pcs->dash_for_anon_wall_station && ch == '-'))) { int first_ch = ch; filepos here; get_pos(&here); nextch(); if (isBlank(ch) || isEol(ch)) { if (!isSep(first_ch)) goto anon_wall_station; /* A single separator alone ('.' by default) is an anonymous * station which is on a point inside the passage and implies * the leg to it is a splay. */ if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) { set_pos(&here); compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Can't have a leg between two anonymous stations*/3); LONGJMP(file.jbSkipLine); } pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY); return new_anon_station(); } if (isSep(first_ch) && ch == first_ch) { nextch(); if (isBlank(ch) || isEol(ch)) { /* A double separator ('..' by default) is an anonymous station * which is on the wall and implies the leg to it is a splay. */ prefix * pfx; anon_wall_station: if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) { set_pos(&here); compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Can't have a leg between two anonymous stations*/3); LONGJMP(file.jbSkipLine); } pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY); pfx = new_anon_station(); pfx->sflags |= BIT(SFLAGS_WALL); return pfx; } if (ch == first_ch) { nextch(); if (isBlank(ch) || isEol(ch)) { /* A triple separator ('...' by default) is an anonymous * station, but otherwise not handled specially (e.g. for * a single leg down an unexplored side passage to a station * which isn't refindable). */ if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) { set_pos(&here); compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Can't have a leg between two anonymous stations*/3); LONGJMP(file.jbSkipLine); } pcs->flags |= BIT(FLAGS_ANON_ONE_END); return new_anon_station(); } } } set_pos(&here); } ptr = pcs->Prefix; } i = 0; name = NULL; do { fNew = fFalse; if (name == NULL) { /* Need a new name buffer */ name = osmalloc(name_len); } /* i==0 iff this is the first pass */ if (i) { i = 0; nextch(); } while (isNames(ch)) { if (i < pcs->Truncate) { /* truncate name */ name[i++] = (pcs->Case == LOWER ? tolower(ch) : (pcs->Case == OFF ? ch : toupper(ch))); if (i >= name_len) { name_len = name_len + name_len; name = osrealloc(name, name_len); } } nextch(); } if (isSep(ch)) { fImplicitPrefix = fFalse; get_pos(&fp_firstsep); } if (i == 0) { osfree(name); if (!f_optional) { if (isEol(ch)) { if (fSurvey) { compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting survey name*/89); } else { compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting station name*/28); } } else { /* TRANSLATORS: Here "station" is a survey station, not a train station. */ compile_diagnostic(DIAG_ERR|DIAG_COL, /*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch); } LONGJMP(file.jbSkipLine); } return (prefix *)NULL; } name[i++] = '\0'; back_ptr = ptr; ptr = ptr->down; if (ptr == NULL) { /* Special case first time around at each level */ name = osrealloc(name, i); ptr = osnew(prefix); ptr->ident = name; name = NULL; ptr->right = ptr->down = NULL; ptr->pos = NULL; ptr->shape = 0; ptr->stn = NULL; ptr->up = back_ptr; ptr->filename = file.filename; ptr->line = file.line; ptr->min_export = ptr->max_export = 0; ptr->sflags = BIT(SFLAGS_SURVEY); if (fSuspectTypo && !fImplicitPrefix) ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO); back_ptr->down = ptr; fNew = fTrue; } else { /* Use caching to speed up adding an increasing sequence to a * large survey */ static prefix *cached_survey = NULL, *cached_station = NULL; prefix *ptrPrev = NULL; int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */ if (cached_survey == back_ptr) { cmp = strcmp(cached_station->ident, name); if (cmp <= 0) ptr = cached_station; } while (ptr && (cmp = strcmp(ptr->ident, name))<0) { ptrPrev = ptr; ptr = ptr->right; } if (cmp) { /* ie we got to one that was higher, or the end */ prefix *newptr; name = osrealloc(name, i); newptr = osnew(prefix); newptr->ident = name; name = NULL; if (ptrPrev == NULL) back_ptr->down = newptr; else ptrPrev->right = newptr; newptr->right = ptr; newptr->down = NULL; newptr->pos = NULL; newptr->shape = 0; newptr->stn = NULL; newptr->up = back_ptr; newptr->filename = file.filename; newptr->line = file.line; newptr->min_export = newptr->max_export = 0; newptr->sflags = BIT(SFLAGS_SURVEY); if (fSuspectTypo && !fImplicitPrefix) newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO); ptr = newptr; fNew = fTrue; } cached_survey = back_ptr; cached_station = ptr; } depth++; f_optional = fFalse; /* disallow after first level */ if (isSep(ch)) get_pos(&fp_firstsep); } while (isSep(ch)); if (name) osfree(name); /* don't warn about a station that is referred to twice */ if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO); if (fNew) { /* fNew means SFLAGS_SURVEY is currently set */ SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY)); if (!fSurvey) { ptr->sflags &= ~BIT(SFLAGS_SURVEY); if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX; } } else { /* check that the same name isn't being used for a survey and station */ if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) { /* TRANSLATORS: Here "station" is a survey station, not a train station. * * Here "survey" is a "cave map" rather than list of questions - it should be * translated to the terminology that cavers using the language would use. */ compile_diagnostic(DIAG_ERR, /*“%s” can’t be both a station and a survey*/27, sprint_prefix(ptr)); } if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX; } /* check the export level */ #if 0 printf("R min %d max %d depth %d pfx %s\n", ptr->min_export, ptr->max_export, depth, sprint_prefix(ptr)); #endif if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) { if (depth > ptr->max_export) ptr->max_export = depth; } else if (ptr->max_export < depth) { prefix *survey = ptr; char *s; const char *p; int level; for (level = ptr->max_export + 1; level; level--) { survey = survey->up; SVX_ASSERT(survey); } s = osstrdup(sprint_prefix(survey)); p = sprint_prefix(ptr); if (survey->filename) { compile_diagnostic_pfx(DIAG_ERR, survey, /*Station “%s” not exported from survey “%s”*/26, p, s); } else { compile_diagnostic(DIAG_ERR, /*Station “%s” not exported from survey “%s”*/26, p, s); } osfree(s); #if 0 printf(" *** pfx %s warning not exported enough depth %d " "ptr->max_export %d\n", sprint_prefix(ptr), depth, ptr->max_export); #endif } if (!fImplicitPrefix && (pfx_flags & PFX_WARN_SEPARATOR)) { filepos fp_tmp; get_pos(&fp_tmp); set_pos(&fp_firstsep); compile_diagnostic(DIAG_WARN|DIAG_COL, /*Separator in survey name*/392); set_pos(&fp_tmp); } return ptr; } /* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */ static real read_number(bool f_optional) { bool fPositive, fDigits = fFalse; real n = (real)0.0; filepos fp; int ch_old; get_pos(&fp); ch_old = ch; fPositive = !isMinus(ch); if (isSign(ch)) nextch(); while (isdigit(ch)) { n = n * (real)10.0 + (char)(ch - '0'); nextch(); fDigits = fTrue; } if (isDecimal(ch)) { real mult = (real)1.0; nextch(); while (isdigit(ch)) { mult *= (real).1; n += (char)(ch - '0') * mult; fDigits = fTrue; nextch(); } } /* !'fRead' => !fDigits so fDigits => 'fRead' */ if (fDigits) return (fPositive ? n : -n); /* didn't read a valid number. If it's optional, reset filepos & return */ set_pos(&fp); if (f_optional) { return HUGE_REAL; } if (isOmit(ch_old)) { compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/8); } else { compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9); } LONGJMP(file.jbSkipLine); return 0.0; /* for brain-fried compilers */ }
int main(int argc, char **argv) { const char *fnm_in, *fnm_out; char *desc; img_point pt; int result; point *fr = NULL, *to; double zMax = -DBL_MAX; point *p; const char *survey = NULL; const char *specfile = NULL; img *pimg; int have_xsect = 0; msg_init(argv); /* TRANSLATORS: Part of extend --help */ cmdline_set_syntax_message(/*INPUT_3D_FILE [OUTPUT_3D_FILE]*/267, 0, NULL); cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 1, 2); while (1) { int opt = cmdline_getopt(); if (opt == EOF) break; if (opt == 's') survey = optarg; if (opt == 'p') specfile = optarg; } fnm_in = argv[optind++]; if (argv[optind]) { fnm_out = argv[optind]; } else { char * base_in = base_from_fnm(fnm_in); char * base_out = osmalloc(strlen(base_in) + 8); strcpy(base_out, base_in); strcat(base_out, "_extend"); fnm_out = add_ext(base_out, EXT_SVX_3D); osfree(base_in); osfree(base_out); } /* try to open image file, and check it has correct header */ pimg = img_open_survey(fnm_in, survey); if (pimg == NULL) fatalerror(img_error2msg(img_error()), fnm_in); putnl(); puts(msg(/*Reading in data - please wait…*/105)); htab = osmalloc(ossizeof(pfx*) * HTAB_SIZE); { int i; for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL; } do { result = img_read_item(pimg, &pt); switch (result) { case img_MOVE: fr = find_point(&pt); break; case img_LINE: if (!fr) { result = img_BAD; break; } to = find_point(&pt); if (!(pimg->flags & (img_FLAG_SURFACE|img_FLAG_SPLAY))) add_leg(fr, to, pimg->label, pimg->flags); fr = to; break; case img_LABEL: to = find_point(&pt); add_label(to, pimg->label, pimg->flags); break; case img_BAD: (void)img_close(pimg); fatalerror(img_error2msg(img_error()), fnm_in); break; case img_XSECT: have_xsect = 1; break; } } while (result != img_STOP); desc = osstrdup(pimg->title); if (specfile) { FILE *fs = NULL; char *fnm_used; /* TRANSLATORS: for extend: */ printf(msg(/*Applying specfile: “%s”*/521), specfile); putnl(); fs = fopenWithPthAndExt("", specfile, NULL, "r", &fnm_used); if (fs == NULL) fatalerror(/*Couldn’t open file “%s”*/93, specfile); while (!feof(fs)) { char *lbuf = getline_alloc(fs, 32); lineno++; if (!lbuf) fatalerror_in_file(fnm_used, lineno, /*Error reading file*/18); parseconfigline(fnm_used, lbuf); osfree(lbuf); } osfree(fnm_used); } if (start == NULL) { /* i.e. start wasn't specified in specfile */ /* start at the highest entrance with some legs attached */ for (p = headpoint.next; p != NULL; p = p->next) { if (p->order > 0 && p->p.z > zMax) { const stn *s; for (s = p->stns; s; s = s->next) { if (s->flags & img_SFLAG_ENTRANCE) { start = p; zMax = p->p.z; break; } } } } if (start == NULL) { /* if no entrances with legs, start at the highest 1-node */ for (p = headpoint.next; p != NULL; p = p->next) { if (p->order == 1 && p->p.z > zMax) { start = p; zMax = p->p.z; } } /* of course we may have no 1-nodes... */ if (start == NULL) { for (p = headpoint.next; p != NULL; p = p->next) { if (p->order != 0 && p->p.z > zMax) { start = p; zMax = p->p.z; } } if (start == NULL) { /* There are no legs - just pick the highest station... */ for (p = headpoint.next; p != NULL; p = p->next) { if (p->p.z > zMax) { start = p; zMax = p->p.z; } } if (!start) fatalerror(/*No survey data*/43); } } } } /* TRANSLATORS: for extend: * Used to tell the user that a file is being written - %s is the filename */ printf(msg(/*Writing %s…*/522), fnm_out); putnl(); pimg_out = img_open_write(fnm_out, desc, img_FFLAG_EXTENDED); /* Only does single connected component currently. */ do_stn(start, 0.0, NULL, ERIGHT, 0); if (have_xsect) { img_rewind(pimg); /* Read ahead on pimg before writing pimg_out so we find out if an * img_XSECT_END comes next. */ char * label = NULL; int flags = 0; do { result = img_read_item(pimg, &pt); if (result == img_XSECT || result == img_XSECT_END) { if (label) { if (result == img_XSECT_END) flags |= img_XFLAG_END; img_write_item(pimg_out, img_XSECT, flags, label, 0, 0, 0); osfree(label); label = NULL; } } if (result == img_XSECT) { label = osstrdup(pimg->label); flags = pimg->flags; pimg_out->l = pimg->l; pimg_out->r = pimg->r; pimg_out->u = pimg->u; pimg_out->d = pimg->d; } } while (result != img_STOP); } (void)img_close(pimg); if (!img_close(pimg_out)) { (void)remove(fnm_out); fatalerror(img_error2msg(img_error()), fnm_out); } return EXIT_SUCCESS; }
static void parse_msg_file(int charset_code) { FILE *fh; unsigned char header[20]; int i; unsigned len; unsigned char *p; char *fnm, *s; int n; #ifdef DEBUG fprintf(stderr, "parse_msg_file(%d)\n", charset_code); #endif /* sort out messages we need to print if we can't open the message file */ dontextract = parse_msgs(N_DONTEXTRACTMSGS, dontextractmsgs, charset_code); fnm = osstrdup(msg_lang); /* trim off charset from stuff like "de_DE.iso8859_1" */ s = strchr(fnm, '.'); if (s) *s = '\0'; /* trim off any "@<something>" modifier. */ s = strchr(fnm, '@'); if (s) *s = '\0'; fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL); if (!fh && strlen(fnm) > 3 && fnm[2] == '_') { /* e.g. if 'en_GB' is unknown, see if we know 'en' */ fnm[2] = '\0'; fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL); if (!fh) fnm[2] = '_'; /* for error reporting */ } if (!fh && !msg_lang_explicit) { /* If msg_lang wasn't specified using environment variable SURVEXLANG, * then default to 'en' if we don't find messages for language msg_lang. */ if (fnm[0] && fnm[1]) { strcpy(fnm, "en"); } else { osfree(fnm); fnm = osstrdup("en"); } fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL); } if (!fh) { fatalerror(/*Can't open message file “%s” using path “%s”*/1000, fnm, pth_cfg_files); } if (fread(header, 1, 20, fh) < 20 || memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) { fatalerror(/*Problem with message file “%s”*/1001, fnm); } if (header[12] != 0) fatalerror(/*I don't understand this message file version*/1002); n = (header[14] << 8) | header[15]; len = 0; for (i = 16; i < 20; i++) len = (len << 8) | header[i]; p = osmalloc(len); if (fread(p, 1, len, fh) < len) fatalerror(/*Message file truncated?*/1003); fclose(fh); #ifdef DEBUG fprintf(stderr, "fnm = “%s”, n = %d, len = %d\n", fnm, n, len); #endif osfree(fnm); msg_array = parse_msgs(n, p, charset_code); num_msgs = n; }