Throw instead of exiting from the tile clipping and overzooming code

The code reachable from overzooming a tile no longer calls exit(). It
reports errors by throwing tippecanoe_error, and owns its FILE * and
json_pull * handles through RAII wrappers so that nothing leaks when an
error unwinds the stack.

New files:

* errors.hpp/errors.cpp: the tippecanoe_error exception class, plus
  throw_tippecanoe_error() and throw_perror() helpers.
* raii.hpp: unique_file and unique_json_pull, both std::unique_ptr
  aliases, for FILE * and json_pull *.

exit() becomes throw in clip.cpp, mvt.cpp, mvt.hpp, read_json.cpp,
evaluator.cpp, text.cpp, geometry.cpp, attribute.cpp, and
write_json.hpp. Each tool's main() is now a try/catch around an
inner_main(), so the command-line tools exit with the status they
always did.

Along the way this fixes a z_stream leaked when compress() failed, an
exit() that could run from json_writer's destructor during unwinding,
and an exit() in mvt_value's hash function.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuCSVGssWNUCpVEE8Yp5HT
This commit is contained in:
Claude
2026-08-07 18:46:09 +00:00
parent 734bba7c78
commit 85547150c2
19 changed files with 272 additions and 170 deletions
+6 -9
View File
@@ -8,6 +8,7 @@
#include "milo/dtoa_milo.h"
#include "milo/milo.h"
#include "errors.hpp"
#include "raii.hpp"
/**
* Returns an empty string if `s` is valid utf8;
@@ -135,7 +136,7 @@ int integer_zoom(std::string where, std::string text) {
double d = atof(text.c_str());
if (!std::isfinite(d) || d != floor(d) || d < 0 || d > 32) {
fprintf(stderr, "%s: Expected integer zoom level in \"tippecanoe\" GeoJSON extension, not %s\n", where.c_str(), text.c_str());
exit(EXIT_JSON);
throw_tippecanoe_error(EXIT_JSON, "%s: Expected integer zoom level in \"tippecanoe\" GeoJSON extension, not %s", where.c_str(), text.c_str());
}
return d;
}
@@ -181,8 +182,7 @@ char *dtoa_milo(double val) {
std::string s = milo::dtoa_milo(val);
char *dup = strdup(s.c_str());
if (dup == NULL) {
perror("strdup");
exit(EXIT_MEMORY);
throw_perror(EXIT_MEMORY, "strdup");
}
return dup;
}
@@ -191,25 +191,22 @@ char *dtoa_milo(double val) {
std::vector<std::string> read_unidecode(const char *fname) {
std::string data;
FILE *f = fopen(fname, "rb");
unique_file f(fopen(fname, "rb"));
if (f == NULL) {
perror(fname);
exit(EXIT_OPEN);
throw_perror(EXIT_OPEN, fname);
}
std::string buf;
buf.resize(2000);
while (true) {
size_t nread = fread((void *) buf.c_str(), sizeof(char), buf.size(), f);
size_t nread = fread((void *) buf.c_str(), sizeof(char), buf.size(), f.get());
if (nread == 0) {
break;
}
data.append(buf.c_str(), nread);
}
fclose(f);
std::vector<std::string> out;
out.emplace_back(); // because the data file is 1-indexed
out.emplace_back(); // ascii 001