Compare commits
25 Commits
70a3e7fc7d
...
v0.0.16
Author | SHA1 | Date | |
---|---|---|---|
13d43d8319 | |||
7bcdbd3813 | |||
60ada22674 | |||
637119d46d | |||
40f3da6a65 | |||
f4697fe7f7 | |||
3bc18b9021 | |||
c21581aefa | |||
165f25db69 | |||
9aa0617aa1 | |||
ddce88dce6 | |||
6aa2bce2be | |||
a43c1d3d1e | |||
1ed0e817e8 | |||
709ca55e65 | |||
8c13f5dbba | |||
4cb82d81b7 | |||
0c42921387 | |||
2d3e108fd9 | |||
7822b30dcb | |||
e361c3f975 | |||
1d5cdf9607 | |||
a4bf3542e0 | |||
df82cfe66b | |||
53f9547cc5 |
42
GNUmakefile
42
GNUmakefile
@ -4,8 +4,8 @@ MAKEFLAGS += --warn-undefined-variables
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
|
||||
VERSION_CODE := 16
|
||||
VERSION_NUMBER := 0.0.16-wip
|
||||
VERSION_NAME := Medium English breakfast tea.
|
||||
VERSION_NUMBER := 0.0.16
|
||||
VERSION_NAME := Now with 38% more process.
|
||||
|
||||
PROJECT = tildefriends
|
||||
BUILD_DIR ?= out
|
||||
@ -245,7 +245,6 @@ $(APP_OBJS): CFLAGS += \
|
||||
-Ideps/quickjs \
|
||||
-Ideps/sqlite \
|
||||
-Ideps/valgrind \
|
||||
-Ideps/xopt \
|
||||
-Wdouble-promotion \
|
||||
-Werror
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
@ -497,18 +496,6 @@ $(SQLITE_OBJS): CFLAGS += \
|
||||
-Wno-unused-function \
|
||||
-Wno-unused-variable
|
||||
|
||||
XOPT_SOURCES := deps/xopt/xopt.c
|
||||
XOPT_OBJS := $(call get_objs,XOPT_SOURCES)
|
||||
$(filter $(BUILD_DIR)/win%,$(XOPT_OBJS)): CFLAGS += \
|
||||
-DHAVE_SNPRINTF \
|
||||
-DHAVE_VSNPRINTF \
|
||||
-DHAVE_VASNPRINTF \
|
||||
-DHAVE_VASPRINTF \
|
||||
-Dvsnprintf=rpl_vsnprintf
|
||||
$(XOPT_OBJS): CFLAGS += \
|
||||
-Wno-implicit-const-int-float-conversion \
|
||||
-Wno-pointer-to-int-cast
|
||||
|
||||
QUICKJS_SOURCES := \
|
||||
deps/quickjs/cutils.c \
|
||||
deps/quickjs/libbf.c \
|
||||
@ -637,8 +624,7 @@ ALL_APP_OBJS := \
|
||||
$(QUICKJS_OBJS) \
|
||||
$(SODIUM_OBJS) \
|
||||
$(SQLITE_OBJS) \
|
||||
$(UV_OBJS) \
|
||||
$(XOPT_OBJS)
|
||||
$(UV_OBJS)
|
||||
|
||||
DEPS = $(ALL_APP_OBJS:.o=.d)
|
||||
-include $(DEPS)
|
||||
@ -717,7 +703,7 @@ PACKAGE_DIRS := \
|
||||
deps/prettier/ \
|
||||
deps/lit/
|
||||
|
||||
RAW_FILES := $(filter-out apps/blog% apps/gg% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f))
|
||||
RAW_FILES := $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f))
|
||||
|
||||
out/apk/TildeFriends-arm-debug.unsigned.apk: BUILD_TYPE := debug
|
||||
out/apk/TildeFriends-arm-release.unsigned.apk: BUILD_TYPE := release
|
||||
@ -736,10 +722,11 @@ out/apk/TildeFriends-arm-%.unsigned.apk:
|
||||
@cp out/android$(BUILD_TYPE)-armv7a/tildefriends out/apk-arm-$(BUILD_TYPE)/lib/armeabi-v7a/tildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-arm-$(BUILD_TYPE)/lib/arm64-v8a/tildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-arm-$(BUILD_TYPE)/lib/armeabi-v7a/tildefriends.so
|
||||
@cp out/apk/res.apk $@
|
||||
@cp out/apk/res.apk $@.zip
|
||||
@cp out/apk/classes.dex out/apk-arm-$(BUILD_TYPE)/
|
||||
@cd out/apk-arm-$(BUILD_TYPE) && zip -u ../../$@ -q -9 -r . && cd ../../
|
||||
@zip -u $@ -q -9 $(RAW_FILES)
|
||||
@cd out/apk-arm-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../
|
||||
@zip -u $@.zip -q $(RAW_FILES)
|
||||
@rm -f $@ && $(ANDROID_BUILD_TOOLS)/zipalign -z 4 $@.zip $@
|
||||
|
||||
out/apk/TildeFriends-x86-%.unsigned.apk:
|
||||
@mkdir -p $(dir $@) out/apk-x86-$(BUILD_TYPE)/lib/x86_64/ out/apk-x86-$(BUILD_TYPE)/lib/x86/
|
||||
@ -748,14 +735,15 @@ out/apk/TildeFriends-x86-%.unsigned.apk:
|
||||
@cp out/android$(BUILD_TYPE)-x86/tildefriends out/apk-x86-$(BUILD_TYPE)/lib/x86/tildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-x86-$(BUILD_TYPE)/lib/x86_64/tildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-x86-$(BUILD_TYPE)/lib/x86/tildefriends.so
|
||||
@cp out/apk/res.apk $@
|
||||
@cp out/apk/res.apk $@.zip
|
||||
@cp out/apk/classes.dex out/apk-x86-$(BUILD_TYPE)/
|
||||
@cd out/apk-x86-$(BUILD_TYPE) && zip -u ../../$@ -q -9 -r . && cd ../../
|
||||
@zip -u $@ -q -9 $(RAW_FILES)
|
||||
@cd out/apk-x86-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../
|
||||
@zip -u $@.zip -q $(RAW_FILES)
|
||||
@rm -f $@ && $(ANDROID_BUILD_TOOLS)/zipalign -z 4 $@.zip $@
|
||||
|
||||
out/%.apk: out/apk/%.unsigned.apk
|
||||
@echo "[apksigner] $(notdir $@)"
|
||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --out $@ $<
|
||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $<
|
||||
|
||||
release-apk: out/TildeFriends-arm-release.apk out/TildeFriends-x86-release.apk
|
||||
.PHONY: release-apk
|
||||
@ -839,7 +827,7 @@ $(filter $(BUILD_DIR)/win%,$(APP_OBJS)): | $(WINDOWS_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
IOS_DEPS := deps/openssl/ios/usr/local/lib/libssl.a
|
||||
IOS_DEPS := deps/openssl/ios/ios64-xcrun/usr/local/lib/libssl.a
|
||||
$(IOS_DEPS):
|
||||
+@tools/ssl-ios
|
||||
$(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(IOS_DEPS)
|
||||
@ -855,7 +843,6 @@ dist: release-apk iosrelease-ipa
|
||||
@mkdir -p dist/ out/tildefriends-$(VERSION_NUMBER)
|
||||
@git archive main | tar -x -C out/tildefriends-$(VERSION_NUMBER)
|
||||
@tar \
|
||||
--exclude=apps/gg* \
|
||||
--exclude=apps/welcome* \
|
||||
--exclude=deps/libbacktrace/Isaac.Newton-Opticks.txt \
|
||||
--exclude=deps/libsodium/builds/msvc/vs* \
|
||||
@ -871,7 +858,6 @@ dist: release-apk iosrelease-ipa
|
||||
--exclude=deps/zlib/contrib/vstudio \
|
||||
--exclude=deps/zlib/doc \
|
||||
-caf dist/tildefriends-$(VERSION_NUMBER).tar.xz out/tildefriends-$(VERSION_NUMBER)
|
||||
#@rm -rf out/tildefriends-$(VERSION_NUMBER)
|
||||
@echo "[cp] TildeFriends-x86-$(VERSION_NUMBER).apk"
|
||||
@cp out/TildeFriends-x86-release.apk dist/TildeFriends-x86-$(VERSION_NUMBER).apk
|
||||
@echo "[cp] TildeFriends-arm-$(VERSION_NUMBER).apk"
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🗺",
|
||||
"previous": "&0XSp+xdQwVtQ88bXzvWdH15Ex63hv5zUKTa4zx7HBGM=.sha256"
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
import * as tfrpc from '/tfrpc.js';
|
||||
import * as strava from './strava.js';
|
||||
|
||||
let g_database;
|
||||
let g_shared_database;
|
||||
|
||||
tfrpc.register(async function createIdentity() {
|
||||
return ssb.createIdentity();
|
||||
});
|
||||
tfrpc.register(async function appendMessage(id, message) {
|
||||
print('APPEND', JSON.stringify(message));
|
||||
return ssb.appendMessageWithIdentity(id, message);
|
||||
});
|
||||
tfrpc.register(function url() {
|
||||
return core.url;
|
||||
});
|
||||
tfrpc.register(async function getUser() {
|
||||
return core.user;
|
||||
});
|
||||
tfrpc.register(function getIdentities() {
|
||||
return ssb.getIdentities();
|
||||
});
|
||||
tfrpc.register(async function databaseGet(key) {
|
||||
return g_database ? g_database.get(key) : undefined;
|
||||
});
|
||||
tfrpc.register(async function databaseSet(key, value) {
|
||||
return g_database ? g_database.set(key, value) : undefined;
|
||||
});
|
||||
tfrpc.register(async function databaseRemove(key, value) {
|
||||
return g_database ? g_database.remove(key, value) : undefined;
|
||||
});
|
||||
tfrpc.register(async function sharedDatabaseGet(key) {
|
||||
return g_shared_database ? g_shared_database.get(key) : undefined;
|
||||
});
|
||||
tfrpc.register(async function sharedDatabaseSet(key, value) {
|
||||
return g_shared_database ? g_shared_database.set(key, value) : undefined;
|
||||
});
|
||||
tfrpc.register(async function sharedDatabaseRemove(key, value) {
|
||||
return g_shared_database ? g_shared_database.remove(key, value) : undefined;
|
||||
});
|
||||
tfrpc.register(async function query(sql, args) {
|
||||
let result = [];
|
||||
await ssb.sqlAsync(sql, args, function callback(row) {
|
||||
result.push(row);
|
||||
});
|
||||
return result;
|
||||
});
|
||||
tfrpc.register(async function store_blob(blob) {
|
||||
if (typeof blob == 'string') {
|
||||
blob = utf8Encode(blob);
|
||||
}
|
||||
if (Array.isArray(blob)) {
|
||||
blob = Uint8Array.from(blob);
|
||||
}
|
||||
return await ssb.blobStore(blob);
|
||||
});
|
||||
|
||||
tfrpc.register(async function get_blob(id) {
|
||||
return utf8Decode(await ssb.blobGet(id));
|
||||
});
|
||||
tfrpc.register(strava.refresh_token);
|
||||
|
||||
async function main() {
|
||||
g_shared_database = await shared_database('state');
|
||||
if (core.user.credentials?.session?.name) {
|
||||
g_database = await database('state');
|
||||
}
|
||||
|
||||
let attempt;
|
||||
if (core.user.credentials?.session?.name) {
|
||||
let shared_db = await shared_database('state');
|
||||
attempt = await shared_db.get(core.user.credentials.session.name);
|
||||
}
|
||||
app.setDocument(
|
||||
utf8Decode(getFile('index.html')).replace(
|
||||
'${data}',
|
||||
JSON.stringify({
|
||||
attempt: attempt,
|
||||
state: core.user?.credentials?.session?.name,
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
main();
|
File diff suppressed because one or more lines are too long
@ -1,84 +0,0 @@
|
||||
function xml_parse(xml) {
|
||||
let result;
|
||||
let path = [];
|
||||
let tag_begin;
|
||||
let text_begin;
|
||||
for (let i = 0; i < xml.length; i++) {
|
||||
let c = xml.charAt(i);
|
||||
if (!tag_begin && c == '<') {
|
||||
if (i > text_begin && path.length) {
|
||||
let value = xml.substring(text_begin, i);
|
||||
if (!/^\s*$/.test(value)) {
|
||||
path[path.length - 1].value = value;
|
||||
}
|
||||
}
|
||||
tag_begin = i + 1;
|
||||
} else if (tag_begin && c == '>') {
|
||||
let tag = xml.substring(tag_begin, i).trim();
|
||||
if (tag.startsWith('?') && tag.endsWith('?')) {
|
||||
/* Ignore directives. */
|
||||
} else if (tag.startsWith('/')) {
|
||||
path.pop();
|
||||
} else {
|
||||
let parts = tag.split(' ');
|
||||
let attributes = {};
|
||||
for (let j = 1; j < parts.length; j++) {
|
||||
let eq = parts[j].indexOf('=');
|
||||
let value = parts[j].substring(eq + 1);
|
||||
if (value.startsWith('"') && value.endsWith('"')) {
|
||||
value = value.substring(1, value.length - 1);
|
||||
}
|
||||
attributes[parts[j].substring(0, eq)] = value;
|
||||
}
|
||||
let next = {name: parts[0], children: [], attributes: attributes};
|
||||
if (path.length) {
|
||||
path[path.length - 1].children.push(next);
|
||||
} else {
|
||||
result = next;
|
||||
}
|
||||
if (!tag.endsWith('/')) {
|
||||
path.push(next);
|
||||
}
|
||||
}
|
||||
tag_begin = undefined;
|
||||
text_begin = i + 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function* xml_each(node, name) {
|
||||
for (let child of node.children) {
|
||||
if (child.name == name) {
|
||||
yield child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function gpx_parse(xml) {
|
||||
let result = {segments: []};
|
||||
let tree = xml_parse(xml);
|
||||
if (tree?.name == 'gpx') {
|
||||
for (let trk of xml_each(tree, 'trk')) {
|
||||
for (let trkseg of xml_each(trk, 'trkseg')) {
|
||||
let segment = [];
|
||||
for (let trkpt of xml_each(trkseg, 'trkpt')) {
|
||||
segment.push({
|
||||
lat: parseFloat(trkpt.attributes.lat),
|
||||
lon: parseFloat(trkpt.attributes.lon),
|
||||
});
|
||||
}
|
||||
result.segments.push(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let metadata of xml_each(tree, 'metadata')) {
|
||||
for (let link of xml_each(metadata, 'link')) {
|
||||
result.link = link.attributes.href;
|
||||
}
|
||||
for (let time of xml_each(metadata, 'time')) {
|
||||
result.time = time.value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import * as strava from './strava.js';
|
||||
|
||||
async function main() {
|
||||
print('handler running');
|
||||
let r = await strava.authorization_code(request.query.code);
|
||||
print('state =', request.query.state);
|
||||
print('body = ', r.body);
|
||||
if (request.query.state && r.body) {
|
||||
let shared_db = await shared_database('state');
|
||||
await shared_db.set(request.query.state, utf8Decode(r.body));
|
||||
}
|
||||
await respond({
|
||||
data: r.body,
|
||||
content_type: 'text/plain',
|
||||
headers: {
|
||||
Location: 'https://tildefriends.net/~cory/gg/',
|
||||
},
|
||||
status_code: 307,
|
||||
});
|
||||
}
|
||||
main();
|
@ -1,26 +0,0 @@
|
||||
<!doctype html>
|
||||
<html style="width: 100%; height: 100%; margin: 0; padding: 0">
|
||||
<head>
|
||||
<script>
|
||||
window.litDisableBundleWarning = true;
|
||||
</script>
|
||||
<script>
|
||||
let g_data = ${data};
|
||||
</script>
|
||||
<script src="script.js" type="module"></script>
|
||||
<script src="leaflet.js"></script>
|
||||
</head>
|
||||
<body
|
||||
style="
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
"
|
||||
>
|
||||
<gg-app style="width: 100%; height: 100%" id="ggapp"></gg-app>
|
||||
</body>
|
||||
</html>
|
@ -1,661 +0,0 @@
|
||||
/* required styles */
|
||||
|
||||
.leaflet-pane,
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-tile-container,
|
||||
.leaflet-pane > svg,
|
||||
.leaflet-pane > canvas,
|
||||
.leaflet-zoom-box,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
/* Prevents IE11 from highlighting tiles in blue */
|
||||
.leaflet-tile::selection {
|
||||
background: transparent;
|
||||
}
|
||||
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||
.leaflet-safari .leaflet-tile {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||
.leaflet-safari .leaflet-tile-container {
|
||||
width: 1600px;
|
||||
height: 1600px;
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
display: block;
|
||||
}
|
||||
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||
.leaflet-container .leaflet-overlay-pane svg {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
.leaflet-container .leaflet-marker-pane img,
|
||||
.leaflet-container .leaflet-shadow-pane img,
|
||||
.leaflet-container .leaflet-tile-pane img,
|
||||
.leaflet-container img.leaflet-image-layer,
|
||||
.leaflet-container .leaflet-tile {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.leaflet-container img.leaflet-tile {
|
||||
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
|
||||
mix-blend-mode: plus-lighter;
|
||||
}
|
||||
|
||||
.leaflet-container.leaflet-touch-zoom {
|
||||
-ms-touch-action: pan-x pan-y;
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag {
|
||||
-ms-touch-action: pinch-zoom;
|
||||
/* Fallback for FF which doesn't support pinch-zoom */
|
||||
touch-action: none;
|
||||
touch-action: pinch-zoom;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.leaflet-container {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.leaflet-container a {
|
||||
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||
}
|
||||
.leaflet-tile {
|
||||
filter: inherit;
|
||||
visibility: hidden;
|
||||
}
|
||||
.leaflet-tile-loaded {
|
||||
visibility: inherit;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
width: 0;
|
||||
height: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
z-index: 800;
|
||||
}
|
||||
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||
.leaflet-overlay-pane svg {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.leaflet-pane { z-index: 400; }
|
||||
|
||||
.leaflet-tile-pane { z-index: 200; }
|
||||
.leaflet-overlay-pane { z-index: 400; }
|
||||
.leaflet-shadow-pane { z-index: 500; }
|
||||
.leaflet-marker-pane { z-index: 600; }
|
||||
.leaflet-tooltip-pane { z-index: 650; }
|
||||
.leaflet-popup-pane { z-index: 700; }
|
||||
|
||||
.leaflet-map-pane canvas { z-index: 100; }
|
||||
.leaflet-map-pane svg { z-index: 200; }
|
||||
|
||||
.leaflet-vml-shape {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
.lvml {
|
||||
behavior: url(#default#VML);
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
/* control positioning */
|
||||
|
||||
.leaflet-control {
|
||||
position: relative;
|
||||
z-index: 800;
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-top {
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-right {
|
||||
right: 0;
|
||||
}
|
||||
.leaflet-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
.leaflet-left {
|
||||
left: 0;
|
||||
}
|
||||
.leaflet-control {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
float: right;
|
||||
}
|
||||
.leaflet-top .leaflet-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.leaflet-left .leaflet-control {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* zoom and fade animations */
|
||||
|
||||
.leaflet-fade-anim .leaflet-popup {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||
opacity: 1;
|
||||
}
|
||||
.leaflet-zoom-animated {
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
svg.leaflet-zoom-animated {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
}
|
||||
.leaflet-zoom-anim .leaflet-tile,
|
||||
.leaflet-pan-anim .leaflet-tile {
|
||||
-webkit-transition: none;
|
||||
-moz-transition: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* cursors */
|
||||
|
||||
.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
.leaflet-grab {
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.leaflet-crosshair,
|
||||
.leaflet-crosshair .leaflet-interactive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.leaflet-popup-pane,
|
||||
.leaflet-control {
|
||||
cursor: auto;
|
||||
}
|
||||
.leaflet-dragging .leaflet-grab,
|
||||
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||
.leaflet-dragging .leaflet-marker-draggable {
|
||||
cursor: move;
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* marker & overlays interactivity */
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-pane > svg path,
|
||||
.leaflet-tile-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon.leaflet-interactive,
|
||||
.leaflet-image-layer.leaflet-interactive,
|
||||
.leaflet-pane > svg path.leaflet-interactive,
|
||||
svg.leaflet-image-layer.leaflet-interactive path {
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* visual tweaks */
|
||||
|
||||
.leaflet-container {
|
||||
background: #ddd;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.leaflet-container a {
|
||||
color: #0078A8;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
border: 2px dotted #38f;
|
||||
background: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
|
||||
/* general typography */
|
||||
.leaflet-container {
|
||||
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
/* general toolbar styles */
|
||||
|
||||
.leaflet-bar {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.leaflet-bar a,
|
||||
.leaflet-control-layers-toggle {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
.leaflet-bar a:hover,
|
||||
.leaflet-bar a:focus {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.leaflet-bar a:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.leaflet-bar a.leaflet-disabled {
|
||||
cursor: default;
|
||||
background-color: #f4f4f4;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-bar a {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
/* zoom control */
|
||||
|
||||
.leaflet-control-zoom-in,
|
||||
.leaflet-control-zoom-out {
|
||||
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||
text-indent: 1px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
|
||||
/* layers control */
|
||||
|
||||
.leaflet-control-layers {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
.leaflet-retina .leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers-2x.png);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers-toggle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.leaflet-control-layers .leaflet-control-layers-list,
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||
display: none;
|
||||
}
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.leaflet-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
.leaflet-control-layers-scrollbar {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.leaflet-control-layers-selector {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
.leaflet-control-layers label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
}
|
||||
.leaflet-control-layers-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 5px -10px 5px -6px;
|
||||
}
|
||||
|
||||
/* Default icon URLs */
|
||||
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
|
||||
background-image: url(images/marker-icon.png);
|
||||
}
|
||||
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.leaflet-container .leaflet-control-attribution {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
margin: 0;
|
||||
}
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale-line {
|
||||
padding: 0 5px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.leaflet-control-attribution a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.leaflet-control-attribution a:hover,
|
||||
.leaflet-control-attribution a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.leaflet-attribution-flag {
|
||||
display: inline !important;
|
||||
vertical-align: baseline !important;
|
||||
width: 1em;
|
||||
height: 0.6669em;
|
||||
}
|
||||
.leaflet-left .leaflet-control-scale {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control-scale {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.leaflet-control-scale-line {
|
||||
border: 2px solid #777;
|
||||
border-top: none;
|
||||
line-height: 1.1;
|
||||
padding: 2px 5px 1px;
|
||||
white-space: nowrap;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
text-shadow: 1px 1px #fff;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child) {
|
||||
border-top: 2px solid #777;
|
||||
border-bottom: none;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||
border-bottom: 2px solid #777;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-attribution,
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
|
||||
/* popup */
|
||||
|
||||
.leaflet-popup {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.leaflet-popup-content-wrapper {
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.leaflet-popup-content {
|
||||
margin: 13px 24px 13px 20px;
|
||||
line-height: 1.3;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
min-height: 1px;
|
||||
}
|
||||
.leaflet-popup-content p {
|
||||
margin: 17px 0;
|
||||
margin: 1.3em 0;
|
||||
}
|
||||
.leaflet-popup-tip-container {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-top: -1px;
|
||||
margin-left: -20px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-popup-tip {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
padding: 1px;
|
||||
|
||||
margin: -10px auto 0;
|
||||
pointer-events: auto;
|
||||
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip {
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font: 16px/24px Tahoma, Verdana, sans-serif;
|
||||
color: #757575;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button:hover,
|
||||
.leaflet-container a.leaflet-popup-close-button:focus {
|
||||
color: #585858;
|
||||
}
|
||||
.leaflet-popup-scrolled {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||
-ms-zoom: 1;
|
||||
}
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
width: 24px;
|
||||
margin: 0 auto;
|
||||
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-control-zoom,
|
||||
.leaflet-oldie .leaflet-control-layers,
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
|
||||
/* div icon */
|
||||
|
||||
.leaflet-div-icon {
|
||||
background: #fff;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
|
||||
|
||||
/* Tooltip */
|
||||
/* Base styles for the element that has a tooltip */
|
||||
.leaflet-tooltip {
|
||||
position: absolute;
|
||||
padding: 6px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 3px;
|
||||
color: #222;
|
||||
white-space: nowrap;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-tooltip.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-tooltip-top:before,
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border: 6px solid transparent;
|
||||
background: transparent;
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Directions */
|
||||
|
||||
.leaflet-tooltip-bottom {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.leaflet-tooltip-top {
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-top:before {
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-top:before {
|
||||
bottom: 0;
|
||||
margin-bottom: -12px;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before {
|
||||
top: 0;
|
||||
margin-top: -12px;
|
||||
margin-left: -6px;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-left {
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-right {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before {
|
||||
right: 0;
|
||||
margin-right: -12px;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-right:before {
|
||||
left: 0;
|
||||
margin-left: -12px;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
/* Printing */
|
||||
|
||||
@media print {
|
||||
/* Prevent printers from removing background-images of controls. */
|
||||
.leaflet-control {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
120
apps/gg/lit-all.min.js
vendored
120
apps/gg/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,162 +0,0 @@
|
||||
/**
|
||||
* Based off of [the offical Google document](https://developers.google.com/maps/documentation/utilities/polylinealgorithm)
|
||||
*
|
||||
* Some parts from [this implementation](http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/PolylineEncoder.js)
|
||||
* by [Mark McClure](http://facstaff.unca.edu/mcmcclur/)
|
||||
*
|
||||
* @module polyline
|
||||
*/
|
||||
|
||||
var polyline = {};
|
||||
|
||||
function py2_round(value) {
|
||||
// Google's polyline algorithm uses the same rounding strategy as Python 2, which is different from JS for negative values
|
||||
return Math.floor(Math.abs(value) + 0.5) * (value >= 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
function encode(current, previous, factor) {
|
||||
current = py2_round(current * factor);
|
||||
previous = py2_round(previous * factor);
|
||||
var coordinate = (current - previous) * 2;
|
||||
if (coordinate < 0) {
|
||||
coordinate = -coordinate - 1;
|
||||
}
|
||||
var output = '';
|
||||
while (coordinate >= 0x20) {
|
||||
output += String.fromCharCode((0x20 | (coordinate & 0x1f)) + 63);
|
||||
coordinate /= 32;
|
||||
}
|
||||
output += String.fromCharCode((coordinate | 0) + 63);
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes to a [latitude, longitude] coordinates array.
|
||||
*
|
||||
* This is adapted from the implementation in Project-OSRM.
|
||||
*
|
||||
* @param {String} str
|
||||
* @param {Number} precision
|
||||
* @returns {Array}
|
||||
*
|
||||
* @see https://github.com/Project-OSRM/osrm-frontend/blob/master/WebContent/routing/OSRM.RoutingGeometry.js
|
||||
*/
|
||||
polyline.decode = function (str, precision) {
|
||||
var index = 0,
|
||||
lat = 0,
|
||||
lng = 0,
|
||||
coordinates = [],
|
||||
shift = 0,
|
||||
result = 0,
|
||||
byte = null,
|
||||
latitude_change,
|
||||
longitude_change,
|
||||
factor = Math.pow(10, Number.isInteger(precision) ? precision : 5);
|
||||
|
||||
// Coordinates have variable length when encoded, so just keep
|
||||
// track of whether we've hit the end of the string. In each
|
||||
// loop iteration, a single coordinate is decoded.
|
||||
while (index < str.length) {
|
||||
// Reset shift, result, and byte
|
||||
byte = null;
|
||||
shift = 1;
|
||||
result = 0;
|
||||
|
||||
do {
|
||||
byte = str.charCodeAt(index++) - 63;
|
||||
result += (byte & 0x1f) * shift;
|
||||
shift *= 32;
|
||||
} while (byte >= 0x20);
|
||||
|
||||
latitude_change = result & 1 ? (-result - 1) / 2 : result / 2;
|
||||
|
||||
shift = 1;
|
||||
result = 0;
|
||||
|
||||
do {
|
||||
byte = str.charCodeAt(index++) - 63;
|
||||
result += (byte & 0x1f) * shift;
|
||||
shift *= 32;
|
||||
} while (byte >= 0x20);
|
||||
|
||||
longitude_change = result & 1 ? (-result - 1) / 2 : result / 2;
|
||||
|
||||
lat += latitude_change;
|
||||
lng += longitude_change;
|
||||
|
||||
coordinates.push([lat / factor, lng / factor]);
|
||||
}
|
||||
|
||||
return coordinates;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes the given [latitude, longitude] coordinates array.
|
||||
*
|
||||
* @param {Array.<Array.<Number>>} coordinates
|
||||
* @param {Number} precision
|
||||
* @returns {String}
|
||||
*/
|
||||
polyline.encode = function (coordinates, precision) {
|
||||
if (!coordinates.length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var factor = Math.pow(10, Number.isInteger(precision) ? precision : 5),
|
||||
output =
|
||||
encode(coordinates[0][0], 0, factor) +
|
||||
encode(coordinates[0][1], 0, factor);
|
||||
|
||||
for (var i = 1; i < coordinates.length; i++) {
|
||||
var a = coordinates[i],
|
||||
b = coordinates[i - 1];
|
||||
output += encode(a[0], b[0], factor);
|
||||
output += encode(a[1], b[1], factor);
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
function flipped(coords) {
|
||||
var flipped = [];
|
||||
for (var i = 0; i < coords.length; i++) {
|
||||
var coord = coords[i].slice();
|
||||
flipped.push([coord[1], coord[0]]);
|
||||
}
|
||||
return flipped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a GeoJSON LineString feature/geometry.
|
||||
*
|
||||
* @param {Object} geojson
|
||||
* @param {Number} precision
|
||||
* @returns {String}
|
||||
*/
|
||||
polyline.fromGeoJSON = function (geojson, precision) {
|
||||
if (geojson && geojson.type === 'Feature') {
|
||||
geojson = geojson.geometry;
|
||||
}
|
||||
if (!geojson || geojson.type !== 'LineString') {
|
||||
throw new Error('Input must be a GeoJSON LineString');
|
||||
}
|
||||
return polyline.encode(flipped(geojson.coordinates), precision);
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes to a GeoJSON LineString geometry.
|
||||
*
|
||||
* @param {String} str
|
||||
* @param {Number} precision
|
||||
* @returns {Object}
|
||||
*/
|
||||
polyline.toGeoJSON = function (str, precision) {
|
||||
var coords = polyline.decode(str, precision);
|
||||
return {
|
||||
type: 'LineString',
|
||||
coordinates: flipped(coords),
|
||||
};
|
||||
};
|
||||
|
||||
let polyline_decode = polyline.decode;
|
||||
export {polyline_decode as decode};
|
@ -1,909 +0,0 @@
|
||||
import {
|
||||
LitElement,
|
||||
html,
|
||||
unsafeHTML,
|
||||
css,
|
||||
guard,
|
||||
until,
|
||||
} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import * as polyline from './polyline.js';
|
||||
import {gpx_parse} from './gpx.js';
|
||||
|
||||
const k_client_id = '28276';
|
||||
const k_redirect_url = 'https://tildefriends.net/~cory/gg/login';
|
||||
|
||||
const k_color_snow = [128, 128, 255, 255];
|
||||
const k_color_ice = [160, 160, 255, 255];
|
||||
const k_color_water = [0, 0, 255, 255];
|
||||
const k_color_dirt = [128, 129, 130, 255];
|
||||
const k_color_pavement = [32, 32, 32, 255];
|
||||
const k_color_grass = [0, 255, 0, 255];
|
||||
const k_color_default = [128, 128, 128, 255];
|
||||
|
||||
const k_store = {
|
||||
'🦞': 15,
|
||||
'🛶': 10,
|
||||
'🏠': 10,
|
||||
'⛰': 10,
|
||||
'🐠': 10,
|
||||
};
|
||||
|
||||
const k_marker_snap = {x: 5, y: 4};
|
||||
|
||||
class GgAppElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
user: {type: Object},
|
||||
strava: {type: Object},
|
||||
activities: {type: Array},
|
||||
activity: {type: Object},
|
||||
world: {type: Object},
|
||||
whoami: {type: String},
|
||||
status: {type: Object},
|
||||
tab: {type: String},
|
||||
url: {type: String},
|
||||
currency: {type: Number},
|
||||
to_build: {type: String},
|
||||
emoji_of_the_day: {type: String},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.activities = [];
|
||||
this.activity = {};
|
||||
this.loaded_activities = [];
|
||||
this.placed_emojis = [];
|
||||
this.strava = {};
|
||||
this.min_lat = Number.MAX_VALUE;
|
||||
this.min_lon = Number.MAX_VALUE;
|
||||
this.max_lat = -Number.MAX_VALUE;
|
||||
this.max_lon = -Number.MAX_VALUE;
|
||||
this.focus = undefined;
|
||||
this.status = undefined;
|
||||
this.tab = 'map';
|
||||
this.load().catch(function (e) {
|
||||
console.log('load error', e);
|
||||
});
|
||||
this.to_build = '🏠';
|
||||
}
|
||||
|
||||
async load() {
|
||||
console.log('load');
|
||||
let emojis = await (await fetch('emojis.json')).json();
|
||||
emojis = Object.values(emojis)
|
||||
.map((x) => Object.values(x))
|
||||
.flat();
|
||||
let today = new Date();
|
||||
let date_index =
|
||||
today.getYear() * 356 + today.getMonth() * 31 + today.getDate();
|
||||
this.emoji_of_the_day = emojis[(date_index * 123457) % emojis.length];
|
||||
this.user = await tfrpc.rpc.getUser();
|
||||
this.url = (await tfrpc.rpc.url()).split('?')[0];
|
||||
try {
|
||||
await this.update_credentials();
|
||||
} catch (e) {
|
||||
console.log('update_credentials failed', e);
|
||||
}
|
||||
try {
|
||||
await this.update_activities();
|
||||
} catch (e) {
|
||||
console.log('update_activities failed', e);
|
||||
}
|
||||
await this.acquire_ssb_identity();
|
||||
if (this.whoami && this.activities?.length) {
|
||||
await this.sync_activities();
|
||||
}
|
||||
await this.get_activities_from_ssb();
|
||||
}
|
||||
|
||||
/* https://gist.github.com/jcouyang/632709f30e12a7879a73e9e132c0d56b?permalink_comment_id=3591045#gistcomment-3591045 */
|
||||
async promise_all(promises, max_concurrent) {
|
||||
let index = 0;
|
||||
let results = [];
|
||||
async function exec_thread() {
|
||||
while (index < promises.length) {
|
||||
const current = index++;
|
||||
results[current] = await promises[current];
|
||||
}
|
||||
}
|
||||
const threads = [];
|
||||
for (let thread = 0; thread < max_concurrent; thread++) {
|
||||
threads.push(exec_thread());
|
||||
}
|
||||
await Promise.all(threads);
|
||||
return results;
|
||||
}
|
||||
|
||||
async get_activities_from_ssb() {
|
||||
this.status = {text: 'loading activities'};
|
||||
this.loaded_activities = [];
|
||||
let rows = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT messages.author, json_extract(mention.value, '$.link') AS blob_id
|
||||
FROM messages_fts('"gg-activity"')
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid,
|
||||
json_each(messages.content, '$.mentions') as mention
|
||||
WHERE json_extract(messages.content, '$.type') = 'gg-activity' AND
|
||||
json_extract(mention.value, '$.name') = 'activity_data'
|
||||
ORDER BY messages.timestamp DESC
|
||||
`,
|
||||
[]
|
||||
);
|
||||
this.status = {text: 'loading activity data'};
|
||||
let authors = rows.map((x) => x.author);
|
||||
let blobs = await this.promise_all(
|
||||
rows.map((x) => tfrpc.rpc.get_blob(x.blob_id)),
|
||||
8
|
||||
);
|
||||
this.status = {text: 'processing activity data'};
|
||||
for (let [index, blob] of blobs.entries()) {
|
||||
let activity;
|
||||
try {
|
||||
activity = JSON.parse(blob);
|
||||
} catch {
|
||||
activity = gpx_parse(blob);
|
||||
}
|
||||
if (activity) {
|
||||
activity.author = authors[index];
|
||||
this.loaded_activities.push(activity);
|
||||
}
|
||||
}
|
||||
this.status = {text: 'calculating balance'};
|
||||
rows = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT count(*) AS currency FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'gg-activity'
|
||||
`,
|
||||
[this.whoami]
|
||||
);
|
||||
let currency = rows[0].currency;
|
||||
rows = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT SUM(json_extract(content, '$.cost')) AS cost FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'gg-place'
|
||||
`,
|
||||
[this.whoami]
|
||||
);
|
||||
let spent = rows[0].cost;
|
||||
this.currency = currency - spent;
|
||||
this.status = {text: 'getting placed emojis'};
|
||||
rows = await tfrpc.rpc.query(`
|
||||
SELECT messages.content
|
||||
FROM messages_fts('"gg-place"')
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
WHERE json_extract(messages.content, '$.type') = 'gg-place'
|
||||
ORDER BY messages.timestamp
|
||||
`);
|
||||
for (let row of rows) {
|
||||
console.log(row.content);
|
||||
let content = JSON.parse(row.content);
|
||||
this.placed_emojis.push({
|
||||
position: content.position,
|
||||
emoji: content.emoji,
|
||||
});
|
||||
}
|
||||
console.log(this.placed_emojis);
|
||||
this.status = undefined;
|
||||
this.update_map();
|
||||
}
|
||||
|
||||
async sync_activities() {
|
||||
let ids = this.activities.map(
|
||||
(x) => `https://www.strava.com/activities/${x.id}`
|
||||
);
|
||||
let missing = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH my_activities AS (
|
||||
SELECT json_extract(mention.value, '$.link') AS url
|
||||
FROM messages, json_each(messages.content, '$.mentions') AS mention
|
||||
WHERE
|
||||
author = ? AND
|
||||
json_extract(messages.content, '$.type') = 'gg-activity' AND
|
||||
json_extract(mention.value, '$.name') = 'activity_url')
|
||||
SELECT from_strava.value FROM json_each(?) AS from_strava
|
||||
LEFT OUTER JOIN my_activities ON from_strava.value = my_activities.url
|
||||
WHERE my_activities.url IS NULL
|
||||
`,
|
||||
[this.whoami, JSON.stringify(ids)]
|
||||
);
|
||||
console.log('missing = ', missing);
|
||||
for (let [index, row] of missing.entries()) {
|
||||
this.status = {
|
||||
text: 'syncing from strava',
|
||||
value: index,
|
||||
max: missing.length,
|
||||
};
|
||||
let url = row.value;
|
||||
let id = url.match(/.*\/(\d+)/)[1];
|
||||
let response = await fetch(
|
||||
`https://www.strava.com/api/v3/activities/${id}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.strava.access_token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
let activity = await response.json();
|
||||
let blob_id = await tfrpc.rpc.store_blob(JSON.stringify(activity));
|
||||
let message = {
|
||||
type: 'gg-activity',
|
||||
mentions: [
|
||||
{
|
||||
link: url,
|
||||
name: 'activity_url',
|
||||
},
|
||||
{
|
||||
link: blob_id,
|
||||
name: 'activity_data',
|
||||
},
|
||||
],
|
||||
};
|
||||
await tfrpc.rpc.appendMessage(this.whoami, message);
|
||||
}
|
||||
this.status = undefined;
|
||||
}
|
||||
|
||||
async acquire_ssb_identity() {
|
||||
let user = await tfrpc.rpc.getUser();
|
||||
if (!user?.credentials?.session?.name) {
|
||||
return;
|
||||
}
|
||||
let ids = await tfrpc.rpc.getIdentities();
|
||||
let players = ids.length
|
||||
? (
|
||||
await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT author FROM messages JOIN json_each(?) ON messages.author = json_each.value
|
||||
WHERE
|
||||
json_extract(messages.content, '$.type') = 'gg-player' AND
|
||||
json_extract(messages.content, '$.active')
|
||||
ORDER BY timestamp DESC limit 1
|
||||
`,
|
||||
[JSON.stringify(ids)]
|
||||
)
|
||||
).map((row) => row.author)
|
||||
: [];
|
||||
if (!players.length) {
|
||||
this.whoami = await tfrpc.rpc.createIdentity();
|
||||
if (this.whoami) {
|
||||
await tfrpc.rpc.appendMessage(this.whoami, {
|
||||
type: 'gg-player',
|
||||
active: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
players.sort();
|
||||
this.whoami = players[0];
|
||||
}
|
||||
}
|
||||
|
||||
async update_credentials() {
|
||||
let name = this.user?.credentials?.session?.name;
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
let shared = await tfrpc.rpc.sharedDatabaseGet(name);
|
||||
if (shared) {
|
||||
await tfrpc.rpc.databaseSet('strava', shared);
|
||||
await tfrpc.rpc.sharedDatabaseRemove(name);
|
||||
}
|
||||
this.strava = JSON.parse((await tfrpc.rpc.databaseGet('strava')) || '{}');
|
||||
if (new Date().valueOf() / 1000 > this.strava.expires_at) {
|
||||
console.log(
|
||||
'this looks expired',
|
||||
new Date().valueOf() / 1000,
|
||||
'>',
|
||||
this.strava.expires_at
|
||||
);
|
||||
let x = await tfrpc.rpc.refresh_token(this.strava);
|
||||
if (x) {
|
||||
this.strava = x;
|
||||
await tfrpc.rpc.databaseSet('strava', JSON.stringify(x));
|
||||
} else {
|
||||
this.strava = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async update_activities() {
|
||||
if (this?.strava?.access_token) {
|
||||
let response = await fetch(
|
||||
'https://www.strava.com/api/v3/athlete/activities',
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.strava.access_token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
this.activities = await response.json();
|
||||
this.activities.sort((a, b) => a.id - b.id);
|
||||
}
|
||||
}
|
||||
|
||||
color_to_emoji(color) {
|
||||
const k_map = [
|
||||
[k_color_snow, '⬜'],
|
||||
[k_color_ice, '🟦'],
|
||||
[k_color_water, '🟦'],
|
||||
[k_color_dirt, '🟫'],
|
||||
[k_color_pavement, '⬛'],
|
||||
[k_color_grass, '🟩'],
|
||||
[k_color_default, '🟧'],
|
||||
];
|
||||
for (let m of k_map) {
|
||||
if (
|
||||
m[0][0] == color[0] &&
|
||||
m[0][1] == color[1] &&
|
||||
m[0][2] == color[2] &&
|
||||
m[0][3] == color[3]
|
||||
) {
|
||||
return m[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activity_bounds(activity) {
|
||||
let min_lat = Number.MAX_VALUE;
|
||||
let min_lon = Number.MAX_VALUE;
|
||||
let max_lat = -Number.MAX_VALUE;
|
||||
let max_lon = -Number.MAX_VALUE;
|
||||
if (activity?.map?.polyline) {
|
||||
for (let pt of polyline.decode(activity.map.polyline)) {
|
||||
min_lat = Math.min(min_lat, pt[0]);
|
||||
min_lon = Math.min(min_lon, pt[1]);
|
||||
max_lat = Math.max(max_lat, pt[0]);
|
||||
max_lon = Math.max(max_lon, pt[1]);
|
||||
}
|
||||
}
|
||||
if (activity?.segments) {
|
||||
for (let segment of activity.segments) {
|
||||
for (let pt of segment) {
|
||||
min_lat = Math.min(min_lat, pt.lat);
|
||||
min_lon = Math.min(min_lon, pt.lon);
|
||||
max_lat = Math.max(max_lat, pt.lat);
|
||||
max_lon = Math.max(max_lon, pt.lon);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
min: {
|
||||
lat: min_lat,
|
||||
lng: min_lon,
|
||||
},
|
||||
max: {
|
||||
lat: max_lat,
|
||||
lng: max_lon,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
on_click(event) {
|
||||
let popup = L.popup()
|
||||
.setLatLng(event.latlng)
|
||||
.setContent(
|
||||
`
|
||||
<div><a target="_top" href="https://www.google.com/maps/search/?api=1&query=${event.latlng.lat},${event.latlng.lng}">${event.latlng.lat}, ${event.latlng.lng}</a></div>
|
||||
`
|
||||
)
|
||||
.openOn(this.leaflet);
|
||||
}
|
||||
|
||||
async build() {
|
||||
if (this.popup) {
|
||||
this.popup.remove();
|
||||
}
|
||||
if (!this.marker) {
|
||||
return;
|
||||
}
|
||||
let latlng = this.marker.getLatLng();
|
||||
|
||||
let cost = k_store[this.to_build];
|
||||
if (cost > this.currency) {
|
||||
alert('Insufficient funds.');
|
||||
return;
|
||||
}
|
||||
let message = {
|
||||
type: 'gg-place',
|
||||
position: {lat: latlng.lat, lng: latlng.lng},
|
||||
emoji: this.to_build,
|
||||
cost: cost,
|
||||
};
|
||||
let id = await tfrpc.rpc.appendMessage(this.whoami, message);
|
||||
this.marker.remove();
|
||||
this.placed_emojis.push({
|
||||
position: {lat: latlng.lat, lng: latlng.lng},
|
||||
emoji: this.to_build,
|
||||
});
|
||||
this.currency -= cost;
|
||||
return this.update_map();
|
||||
}
|
||||
|
||||
on_marker_click(event) {
|
||||
this.popup = L.popup()
|
||||
.setLatLng(event.latlng)
|
||||
.setContent(
|
||||
`
|
||||
${this.to_build} (-${k_store[this.to_build]}) <input type="button" value="Build" onclick="document.getElementById('ggapp').build()"></input>
|
||||
`
|
||||
)
|
||||
.openOn(this.leaflet);
|
||||
}
|
||||
|
||||
snap_to_grid(latlng, fudge, zoom) {
|
||||
let position = this.leaflet.options.crs.latLngToPoint(
|
||||
latlng,
|
||||
zoom ?? this.leaflet.getZoom()
|
||||
);
|
||||
position.x = Math.round(position.x / 16) * 16 + (fudge?.x ?? 0);
|
||||
position.y = Math.round(position.y / 16) * 16 + (fudge?.y ?? 0);
|
||||
position = this.leaflet.options.crs.pointToLatLng(
|
||||
position,
|
||||
zoom ?? this.leaflet.getZoom()
|
||||
);
|
||||
return position;
|
||||
}
|
||||
|
||||
on_marker_move(event) {
|
||||
if (!this.no_snap && this.marker) {
|
||||
this.no_snap = true;
|
||||
this.marker.setLatLng(
|
||||
this.snap_to_grid(this.marker.getLatLng(), k_marker_snap)
|
||||
);
|
||||
this.no_snap = false;
|
||||
}
|
||||
}
|
||||
|
||||
on_zoom(event) {
|
||||
if (this.marker) {
|
||||
this.marker.setLatLng(
|
||||
this.snap_to_grid(this.marker.getLatLng(), k_marker_snap)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
on_mouse_down(event) {
|
||||
if (this.marker) {
|
||||
this.marker.remove();
|
||||
this.marker = undefined;
|
||||
}
|
||||
|
||||
if (this.to_build) {
|
||||
this.marker = L.marker(this.snap_to_grid(event.latlng, k_marker_snap), {
|
||||
icon: L.divIcon({className: 'build-icon'}),
|
||||
draggable: true,
|
||||
}).addTo(this.leaflet);
|
||||
this.marker.on({click: this.on_marker_click.bind(this)});
|
||||
this.marker.on({drag: this.on_marker_move.bind(this)});
|
||||
}
|
||||
}
|
||||
|
||||
async update_map() {
|
||||
let map = this.shadowRoot.getElementById('map');
|
||||
if (!map || !this.loaded_activities.length) {
|
||||
this.leaflet = undefined;
|
||||
this.grid_layer = undefined;
|
||||
return;
|
||||
}
|
||||
if (!this.leaflet) {
|
||||
this.leaflet = L.map(map, {
|
||||
attributionControl: false,
|
||||
maxZoom: 16,
|
||||
bounceAtZoomLimits: false,
|
||||
});
|
||||
this.leaflet.on({contextmenu: this.on_click.bind(this)});
|
||||
this.leaflet.on({click: this.on_mouse_down.bind(this)});
|
||||
this.leaflet.on({zoom: this.on_zoom.bind(this)});
|
||||
}
|
||||
let self = this;
|
||||
let grid_layer = L.GridLayer.extend({
|
||||
createTile: function (coords) {
|
||||
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
|
||||
var size = this.getTileSize();
|
||||
tile.width = size.x;
|
||||
tile.height = size.y;
|
||||
var context = tile.getContext('2d');
|
||||
context.font = '10pt sans';
|
||||
let bounds = this._tileCoordsToBounds(coords);
|
||||
let degrees = 360.0 / 2 ** coords.z;
|
||||
let ul = bounds.getNorthWest();
|
||||
let lr = bounds.getSouthEast();
|
||||
|
||||
let mini = document.createElement('canvas');
|
||||
mini.width = Math.floor(size.x / 16.0);
|
||||
mini.height = Math.floor(size.y / 16.0);
|
||||
let mini_context = mini.getContext('2d');
|
||||
let image_data = context.getImageData(0, 0, mini.width, mini.height);
|
||||
for (let activity of self.loaded_activities) {
|
||||
self.draw_activity_to_tile(
|
||||
image_data,
|
||||
mini.width,
|
||||
mini.height,
|
||||
ul,
|
||||
lr,
|
||||
activity
|
||||
);
|
||||
}
|
||||
context.textAlign = 'left';
|
||||
context.textBaseline = 'bottom';
|
||||
for (let x = 0; x < mini.width; x++) {
|
||||
for (let y = 0; y < mini.height; y++) {
|
||||
let start = (y * mini.width + x) * 4;
|
||||
let pixel = self.color_to_emoji(
|
||||
image_data.data.slice(start, start + 4)
|
||||
);
|
||||
if (pixel) {
|
||||
//context.fillRect(x * size.x / mini.width, y * size.y / mini.height, size.x / mini.width, size.y / mini.height);
|
||||
context.fillText(
|
||||
pixel,
|
||||
(x * size.x) / mini.width,
|
||||
(y * size.y) / mini.height + mini.height
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let placed of self.placed_emojis) {
|
||||
let position = self.leaflet.options.crs.latLngToPoint(
|
||||
self.snap_to_grid(placed.position, undefined, coords.z),
|
||||
coords.z
|
||||
);
|
||||
let tile_x = Math.floor(position.x / size.x);
|
||||
let tile_y = Math.floor(position.y / size.y);
|
||||
position.x = position.x - tile_x * size.x;
|
||||
position.y = position.y - tile_y * size.y;
|
||||
if (tile_x == coords.x && tile_y == coords.y) {
|
||||
//context.fillRect(position.x, position.y, size.x / mini.width, size.y / mini.height);
|
||||
context.fillText(
|
||||
placed.emoji,
|
||||
position.x,
|
||||
position.y + mini.height
|
||||
);
|
||||
}
|
||||
}
|
||||
return tile;
|
||||
},
|
||||
});
|
||||
if (this.grid_layer) {
|
||||
this.grid_layer.redraw();
|
||||
} else {
|
||||
this.grid_layer = new grid_layer();
|
||||
this.grid_layer.addTo(this.leaflet);
|
||||
}
|
||||
for (let activity of this.loaded_activities) {
|
||||
let bounds = this.activity_bounds(activity);
|
||||
this.min_lat = Math.min(this.min_lat, bounds.min.lat);
|
||||
this.min_lon = Math.min(this.min_lon, bounds.min.lng);
|
||||
this.max_lat = Math.max(this.max_lat, bounds.max.lat);
|
||||
this.max_lon = Math.max(this.max_lon, bounds.max.lng);
|
||||
}
|
||||
if (this.focus) {
|
||||
this.leaflet.fitBounds([this.focus.min, this.focus.max]);
|
||||
this.focus = undefined;
|
||||
} else {
|
||||
this.leaflet.fitBounds([
|
||||
[this.min_lat, this.min_lon],
|
||||
[this.max_lat, this.max_lon],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
activity_to_color(activity) {
|
||||
let color = [0, 0, 0, 255];
|
||||
switch (activity.sport_type) {
|
||||
/* Implies snow. */
|
||||
case 'AlpineSki':
|
||||
case 'BackcountrySki':
|
||||
case 'NordicSki':
|
||||
case 'Snowshoe':
|
||||
case 'Snowboard':
|
||||
color = k_color_snow;
|
||||
break;
|
||||
|
||||
/* Implies ice. */
|
||||
case 'IceSkate':
|
||||
case 'InlineSkate':
|
||||
color = k_color_ice;
|
||||
break;
|
||||
|
||||
/* Implies water. */
|
||||
case 'Canoeing':
|
||||
case 'Kayaking':
|
||||
case 'Kitesurf':
|
||||
case 'Rowing':
|
||||
case 'Sail':
|
||||
case 'StandUpPaddling':
|
||||
case 'Surfing':
|
||||
case 'Swim':
|
||||
case 'Windsurf':
|
||||
color = k_color_water;
|
||||
break;
|
||||
|
||||
/* Implies dirt. */
|
||||
case 'EMountainBikeRide':
|
||||
case 'Hike':
|
||||
case 'MountainBikeRide':
|
||||
case 'RockClimbing':
|
||||
case 'TrailRun':
|
||||
color = k_color_dirt;
|
||||
break;
|
||||
|
||||
/* Implies pavement. */
|
||||
case 'EBikeRide':
|
||||
case 'GravelRide':
|
||||
case 'Handcycle':
|
||||
case 'Ride':
|
||||
case 'RollerSki':
|
||||
case 'Run':
|
||||
case 'Skateboard':
|
||||
case 'Badminton':
|
||||
case 'Tennis':
|
||||
case 'Velomobile':
|
||||
case 'Walk':
|
||||
case 'Wheelchair':
|
||||
color = k_color_pavement;
|
||||
break;
|
||||
|
||||
/* Grass, maybe? */
|
||||
case 'Golf':
|
||||
case 'Soccer':
|
||||
case 'Squash':
|
||||
color = k_color_grass;
|
||||
break;
|
||||
|
||||
// Crossfit,
|
||||
// Elliptical
|
||||
// HighIntensityIntervalTraining
|
||||
// Pickleball
|
||||
// Pilates
|
||||
// Racquetball
|
||||
// StairStepper
|
||||
// TableTennis,
|
||||
// VirtualRide
|
||||
// VirtualRow
|
||||
// VirtualRun
|
||||
// WeightTraining
|
||||
// Workout
|
||||
// Yoga
|
||||
default:
|
||||
color = k_color_default;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
line(image_data, x0, y0, x1, y1, value) {
|
||||
/* <3 https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm */
|
||||
let dx = Math.abs(x1 - x0);
|
||||
let sx = x0 < x1 ? 1 : -1;
|
||||
let dy = -Math.abs(y1 - y0);
|
||||
let sy = y0 < y1 ? 1 : -1;
|
||||
let error = dx + dy;
|
||||
while (true) {
|
||||
if (
|
||||
x0 >= 0 &&
|
||||
y0 >= 0 &&
|
||||
x0 < image_data.width &&
|
||||
y0 < image_data.height
|
||||
) {
|
||||
let base = (y0 * image_data.width + x0) * 4;
|
||||
image_data.data[base + 0] = value[0];
|
||||
image_data.data[base + 1] = value[1];
|
||||
image_data.data[base + 2] = value[2];
|
||||
image_data.data[base + 3] = value[3];
|
||||
}
|
||||
|
||||
if (x0 == x1 && y0 == y1) {
|
||||
break;
|
||||
}
|
||||
let e2 = 2 * error;
|
||||
if (e2 >= dy) {
|
||||
if (x0 == x1) {
|
||||
break;
|
||||
}
|
||||
error += dy;
|
||||
x0 = Math.round(x0 + sx);
|
||||
}
|
||||
if (e2 <= dx) {
|
||||
if (y0 == y1) {
|
||||
break;
|
||||
}
|
||||
error += dx;
|
||||
y0 = Math.round(y0 + sy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw_activity_to_tile(image_data, width, height, ul, lr, activity) {
|
||||
let color = this.activity_to_color(activity);
|
||||
if (activity?.map?.polyline) {
|
||||
let last;
|
||||
for (let pt of polyline.decode(activity.map.polyline)) {
|
||||
let px = [
|
||||
Math.floor((width * (pt[1] - ul.lng)) / (lr.lng - ul.lng)),
|
||||
Math.floor((height * (pt[0] - ul.lat)) / (lr.lat - ul.lat)),
|
||||
];
|
||||
if (last) {
|
||||
this.line(image_data, last[0], last[1], px[0], px[1], color);
|
||||
}
|
||||
last = px;
|
||||
}
|
||||
}
|
||||
if (activity?.segments) {
|
||||
for (let segment of activity.segments) {
|
||||
let last;
|
||||
for (let pt of segment) {
|
||||
let px = [
|
||||
Math.floor((width * (pt.lon - ul.lng)) / (lr.lng - ul.lng)),
|
||||
Math.floor((height * (pt.lat - ul.lat)) / (lr.lat - ul.lat)),
|
||||
];
|
||||
if (last) {
|
||||
this.line(image_data, last[0], last[1], px[0], px[1], color);
|
||||
}
|
||||
last = px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async on_upload(event) {
|
||||
try {
|
||||
let file = event.srcElement.files[0];
|
||||
let xml = await file.text();
|
||||
let gpx = gpx_parse(xml);
|
||||
let blob_id = await tfrpc.rpc.store_blob(xml);
|
||||
console.log('blob_id = ', blob_id);
|
||||
console.log(gpx);
|
||||
let message = {
|
||||
type: 'gg-activity',
|
||||
mentions: [
|
||||
{
|
||||
link: `https://${gpx.link}/activity/${gpx.time}`,
|
||||
name: 'activity_url',
|
||||
},
|
||||
{
|
||||
link: blob_id,
|
||||
name: 'activity_data',
|
||||
},
|
||||
],
|
||||
};
|
||||
console.log('id =', this.whoami, 'message = ', message);
|
||||
let id = await tfrpc.rpc.appendMessage(this.whoami, message);
|
||||
console.log('appended message', id);
|
||||
alert('Activity uploaded.');
|
||||
await this.get_activities_from_ssb();
|
||||
} catch (e) {
|
||||
alert(`Error: ${JSON.stringify(e, null, 2)}`);
|
||||
}
|
||||
}
|
||||
|
||||
upload() {
|
||||
let input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.onchange = (event) => this.on_upload(event);
|
||||
input.click();
|
||||
}
|
||||
|
||||
updated() {
|
||||
this.update_map();
|
||||
}
|
||||
|
||||
focus_map(activity) {
|
||||
let bounds = this.activity_bounds(activity);
|
||||
if (bounds.min.lat < bounds.max.lat && bounds.min.lng < bounds.max.lng) {
|
||||
this.tab = 'map';
|
||||
this.focus = bounds;
|
||||
}
|
||||
}
|
||||
|
||||
render_news() {
|
||||
return html`
|
||||
<ul>
|
||||
${this.loaded_activities.map(
|
||||
(x) => html`
|
||||
<li style="cursor: pointer" @click=${() => this.focus_map(x)}>
|
||||
${x.author} ${x.name ?? x.time}
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
|
||||
render_store_item(item) {
|
||||
let [emoji, cost] = item;
|
||||
return html`
|
||||
<div>
|
||||
<input type="button" value="${emoji}" @click=${() => (this.to_build = emoji)}></input> ${cost} ${emoji == this.to_build ? '<-- Will be built next' : undefined}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
render_store() {
|
||||
let store = Object.assign({}, k_store);
|
||||
store[this.emoji_of_the_day] = 5;
|
||||
return html`
|
||||
<h2>Store</h2>
|
||||
<div><b>Your balance:</b> ${this.currency}</div>
|
||||
${Object.entries(store).map(this.render_store_item.bind(this))}
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
let header;
|
||||
if (!this.user?.credentials?.session?.name) {
|
||||
header = html`<div style="flex: 1 0">
|
||||
Please <a target="_top" href="/login?return=${this.url}">login</a> to
|
||||
Tilde Friends, first.
|
||||
</div>`;
|
||||
} else if (!this.strava?.access_token) {
|
||||
let strava_url = `https://www.strava.com/oauth/authorize?client_id=${k_client_id}&redirect_uri=${k_redirect_url}&response_type=code&approval_prompt=auto&scope=activity%3Aread&state=${g_data.state}`;
|
||||
header = html`
|
||||
<div style="flex: 1 0; display: flex; flex-direction: row; align-items: center; gap: 1em; width: 100%">
|
||||
<div style="flex: 1 1">Please <a target="_top" href=${strava_url}>login</a> to Strava.</div>
|
||||
<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.whoami}</span>
|
||||
<input type="button" value="📁" @click=${this.upload}></input>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
header = html`
|
||||
<div>
|
||||
<div style="flex: 1 0; display: flex; flex-direction: row; align-items: center; gap: 1em; width: 100%">
|
||||
<h1>Welcome, ${this.user.credentials.session.name}</h1>
|
||||
<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.whoami}</span>
|
||||
<input type="button" value="📁" @click=${this.upload}></input>
|
||||
</div>
|
||||
<h3 ?hidden=${!this.status?.text}>${this.status?.text} <progress ?hidden=${!this.status?.max} value=${this.status?.value} max=${this.status?.max}>${this.status?.value}</progress></h3>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
let navigation = html`
|
||||
<style>
|
||||
#navigation input[type="button"] {
|
||||
min-width: 3em;
|
||||
min-height: 3em;
|
||||
flex: 1 0;
|
||||
font-size: large;
|
||||
}
|
||||
</style>
|
||||
<div id="navigation" style="display: flex; flex-direction: row">
|
||||
<input type="button" id="button_map" @click=${() => (this.tab = 'map')} value="🗺️Map"></input>
|
||||
<input type="button" id="button_news" @click=${() => (this.tab = 'news')} value="🏃News"></input>
|
||||
<input type="button" id="button_friends" @click=${() => (this.tab = 'friends')} value="👫Friends"></input>
|
||||
<input type="button" id="button_store" @click=${() => (this.tab = 'store')} value="🏗️Store"></input>
|
||||
</div>
|
||||
`;
|
||||
|
||||
let content;
|
||||
switch (this.tab) {
|
||||
case 'map':
|
||||
content = html`<div id="map" style="width: 100%; height: 100%"></div>`;
|
||||
break;
|
||||
case 'news':
|
||||
content = this.render_news();
|
||||
break;
|
||||
case 'friends':
|
||||
content = html`<div>Friends</div>`;
|
||||
break;
|
||||
case 'store':
|
||||
content = this.render_store();
|
||||
break;
|
||||
}
|
||||
|
||||
return html`
|
||||
<style>
|
||||
.build-icon::before {
|
||||
content: '📍';
|
||||
border: 2px solid red;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="leaflet.css" />
|
||||
<div
|
||||
style="width: 100%; height: 100%; display: flex; flex-direction: column"
|
||||
>
|
||||
${header}
|
||||
<div style="flex: 1 0; overflow: scroll">${content}</div>
|
||||
${navigation}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
customElements.define('gg-app', GgAppElement);
|
@ -1,20 +0,0 @@
|
||||
const k_client_id = '28276';
|
||||
const k_client_secret = '3123f1f5afe132d9731111066d1d17bdb22ef27e';
|
||||
const k_access_token = 'f753e77764c26252bd2d80e7c5cc17ace51a8864';
|
||||
const k_refresh_token = 'f58d8e1b5a3ec3bf96e681589d5014f9a294f5a4';
|
||||
const k_redirect_url = 'https://tildefriends.net/~cory/gg/login';
|
||||
|
||||
export async function refresh_token(token) {
|
||||
let r = await fetch('https://www.strava.com/api/v3/oauth/token', {
|
||||
method: 'POST',
|
||||
body: `client_id=${k_client_id}&client_secret=${k_client_secret}&refresh_token=${token.refresh_token}&grant_type=refresh_token`,
|
||||
});
|
||||
return r?.body ? JSON.parse(utf8Decode(r.body)) : undefined;
|
||||
}
|
||||
|
||||
export async function authorization_code(code) {
|
||||
return await fetch('https://www.strava.com/api/v3/oauth/token', {
|
||||
method: 'POST',
|
||||
body: `client_id=${k_client_id}&client_secret=${k_client_secret}&code=${code}&grant_type=authorization_code`,
|
||||
});
|
||||
}
|
@ -2,8 +2,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<base target="_top" />
|
||||
<link rel="stylesheet" href="tildefriends.css" />
|
||||
</head>
|
||||
<body style="color: #fff">
|
||||
<body>
|
||||
<tf-collections-app></tf-collections-app>
|
||||
<script>
|
||||
window.litDisableBundleWarning = true;
|
||||
|
@ -5,6 +5,7 @@ class TfCollectionElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
whoami: {type: String},
|
||||
category: {type: String},
|
||||
collection: {type: Object},
|
||||
selected_id: {type: String},
|
||||
is_creating: {type: Boolean},
|
||||
@ -75,9 +76,10 @@ class TfCollectionElement extends LitElement {
|
||||
render() {
|
||||
let self = this;
|
||||
return html`
|
||||
<span style="display: inline-flex; flex-direction: row">
|
||||
<link rel="stylesheet" href="tildefriends.css"/>
|
||||
<span class="inline-flex-row">
|
||||
<select @change=${this.on_selected} id="select" value=${this.selected_id}>
|
||||
<option value="" ?selected=${this.selected_id === ''} disabled hidden>(select)</option>
|
||||
<option value="" ?selected=${this.selected_id === ''} disabled hidden>(select ${this.category})</option>
|
||||
${Object.values(this.collection ?? {})
|
||||
.sort((x, y) => x.name.localeCompare(y.name))
|
||||
.map(
|
||||
@ -91,22 +93,22 @@ class TfCollectionElement extends LitElement {
|
||||
)}
|
||||
</select>
|
||||
<span ?hidden=${!this.is_renaming || !this.whoami}>
|
||||
<span style="display: inline-flex; flex-direction: row; margin-left: 8px; margin-right: 8px">
|
||||
<span class="inline-flex-row" style="margin-left: 8px; margin-right: 8px">
|
||||
<label for="rename_name">🏷Rename to:</label>
|
||||
<input type="text" id="rename_name"></input>
|
||||
<button @click=${this.on_rename}>Rename ${this.type}</button>
|
||||
<button @click=${() => (self.is_renaming = false)}>x</button>
|
||||
</span>
|
||||
</span>
|
||||
<button @click=${() => (self.is_renaming = true)} ?disabled=${this.is_renaming || !this.selected_id} ?hidden=${!this.whoami}>🏷</button>
|
||||
<button @click=${self.on_tombstone} ?disabled=${!this.selected_id} ?hidden=${!this.whoami}>🪦</button>
|
||||
<button class="yellow" @click=${() => (self.is_renaming = true)} ?disabled=${this.is_renaming || !this.selected_id} ?hidden=${!this.whoami}>🏷</button>
|
||||
<button class="red" @click=${self.on_tombstone} ?disabled=${!this.selected_id} ?hidden=${!this.whoami}>🪦</button>
|
||||
<span ?hidden=${!this.is_creating || !this.whoami}>
|
||||
<label for="create_name">New ${this.type} name:</label>
|
||||
<input type="text" id="create_name"></input>
|
||||
<button @click=${this.on_create}>Create ${this.type}</button>
|
||||
<button @click=${() => (self.is_creating = false)}>x</button>
|
||||
</span>
|
||||
<button @click=${() => (self.is_creating = true)} ?hidden=${this.is_creating || !this.whoami}>+</button>
|
||||
<button class="green" @click=${() => (self.is_creating = true)} ?hidden=${this.is_creating || !this.whoami}>+</button>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ class TfIdentityPickerElement extends LitElement {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link rel="stylesheet" href="tildefriends.css" />
|
||||
<select @change=${this.changed} style="max-width: 100%">
|
||||
${(this.ids ?? []).map(
|
||||
(id) =>
|
||||
|
@ -255,12 +255,22 @@ class TfCollectionsAppElement extends LitElement {
|
||||
render() {
|
||||
let self = this;
|
||||
return html`
|
||||
<link rel="stylesheet" href="tildefriends.css"/>
|
||||
<style>
|
||||
.toc:hover {
|
||||
.toc-item {
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
.toc-item:hover {
|
||||
background-color: #0cc;
|
||||
}
|
||||
.toc.selected {
|
||||
.toc-item.selected {
|
||||
background-color: #088;
|
||||
font-weight: bold;
|
||||
}
|
||||
.table-of-contents {
|
||||
flex: 0 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
@ -272,6 +282,7 @@ class TfCollectionsAppElement extends LitElement {
|
||||
html`<tf-collection
|
||||
.collection=${this.wikis}
|
||||
whoami=${this.whoami}
|
||||
category="wiki"
|
||||
selected_id=${this.wiki?.id}
|
||||
@create=${this.on_wiki_create}
|
||||
@rename=${this.on_wiki_rename}
|
||||
@ -284,6 +295,7 @@ class TfCollectionsAppElement extends LitElement {
|
||||
html`<tf-collection
|
||||
.collection=${this.wiki_docs}
|
||||
whoami=${this.whoami}
|
||||
category="document"
|
||||
selected_id=${this.wiki_doc &&
|
||||
this.wiki_doc?.parent == this.wiki?.id
|
||||
? this.wiki_doc?.id
|
||||
@ -298,9 +310,9 @@ class TfCollectionsAppElement extends LitElement {
|
||||
<div ?hidden=${!this.wiki?.editors || !this.expand_editors}>
|
||||
<div>
|
||||
<ul>
|
||||
${this.wiki?.editors.map((id) => html`<li><button ?hidden=${id == this.whoami} @click=${() => self.on_remove_editor(id)}>x</button> ${id}</li>`)}
|
||||
${this.wiki?.editors.map((id) => html`<li><button class="red" ?hidden=${id == this.whoami} @click=${() => self.on_remove_editor(id)}>x</button> ${id}</li>`)}
|
||||
<li>
|
||||
<button @click=${() => (self.adding_editor = true)} ?hidden=${this.wiki?.editors?.indexOf(this.whoami) == -1 || this.adding_editor}>+</button>
|
||||
<button class="green" @click=${() => (self.adding_editor = true)} ?hidden=${this.wiki?.editors?.indexOf(this.whoami) == -1 || this.adding_editor}>+</button>
|
||||
<div ?hidden=${!this.adding_editor}>
|
||||
<label for="add_editor">Add Editor:</label>
|
||||
<input type="text" id="add_editor"></input>
|
||||
@ -312,18 +324,19 @@ class TfCollectionsAppElement extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: row">
|
||||
<div style="flex: 0 0">
|
||||
<div class="flex-row">
|
||||
<div class="box table-of-contents">
|
||||
${Object.values(this.wikis || {})
|
||||
.sort((x, y) => x.name.localeCompare(y.name))
|
||||
.map(
|
||||
(wiki) => html`
|
||||
<div
|
||||
class="toc ${self.wiki?.id === wiki.id ? 'selected' : ''}"
|
||||
style="white-space: nowrap; cursor: pointer"
|
||||
class="toc-item ${self.wiki?.id === wiki.id
|
||||
? 'selected'
|
||||
: ''}"
|
||||
@click=${() => self.on_wiki_changed({detail: {value: wiki}})}
|
||||
>
|
||||
${wiki.name}
|
||||
${self.wiki?.id === wiki.id ? '' : '>'} ${wiki.name}
|
||||
</div>
|
||||
<ul>
|
||||
${Object.values(self.wiki_docs || {})
|
||||
@ -332,10 +345,10 @@ class TfCollectionsAppElement extends LitElement {
|
||||
.map(
|
||||
(doc) => html`
|
||||
<li
|
||||
class="toc ${self.wiki_doc?.id === doc.id
|
||||
class="toc-item ${self.wiki_doc?.id === doc.id
|
||||
? 'selected'
|
||||
: ''}"
|
||||
style="white-space: nowrap; cursor: pointer; list-style: none; text-indent: -1rem"
|
||||
style="list-style: none; text-indent: -1rem"
|
||||
@click=${() =>
|
||||
self.on_wiki_doc_changed({detail: {value: doc}})}
|
||||
>
|
||||
|
@ -84,7 +84,11 @@ class TfWikiDocElement extends LitElement {
|
||||
|
||||
async load_blob() {
|
||||
let blob = await tfrpc.rpc.get_blob(this.value?.blob);
|
||||
if (blob.endsWith('.box')) {
|
||||
if (!blob) {
|
||||
console.warn(
|
||||
"no blob found, we're going to assume the document is empty (load_blob())"
|
||||
);
|
||||
} else if (blob.endsWith('.box')) {
|
||||
let d = await tfrpc.rpc.try_decrypt(this.whoami, blob);
|
||||
if (d) {
|
||||
blob = d;
|
||||
@ -253,85 +257,43 @@ class TfWikiDocElement extends LitElement {
|
||||
let self = this;
|
||||
let thumbnail_ref = this.thumbnail(this.blob);
|
||||
return html`
|
||||
<link rel="stylesheet" href="tildefriends.css"/>
|
||||
<style>
|
||||
a:link {
|
||||
color: #268bd2;
|
||||
}
|
||||
a:visited {
|
||||
color: #6c71c4;
|
||||
}
|
||||
a:hover {
|
||||
color: #859900;
|
||||
}
|
||||
a:active {
|
||||
color: #2aa198;
|
||||
}
|
||||
a:link { color: #268bd2 }
|
||||
a:visited { color: #6c71c4 }
|
||||
a:hover { color: #859900 }
|
||||
a:active { color: #2aa198 }
|
||||
|
||||
#editor-text-area {
|
||||
background-color: #00000040;
|
||||
color: white;
|
||||
style="flex: 1 1;
|
||||
min-height: 10em;
|
||||
font-size: larger;
|
||||
${this.value?.private ? 'border: 4px solid #800' : ''}
|
||||
</style>
|
||||
<div style="display: inline-flex; flex-direction: row">
|
||||
<button
|
||||
?disabled=${!this.whoami || this.is_editing}
|
||||
@click=${() => (self.is_editing = true)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
?disabled=${this.blob == this.blob_original}
|
||||
@click=${this.on_save_draft}
|
||||
>
|
||||
Save Draft
|
||||
</button>
|
||||
<button
|
||||
?disabled=${this.blob == this.blob_original && !this.value?.draft}
|
||||
@click=${this.on_publish}
|
||||
>
|
||||
Publish
|
||||
</button>
|
||||
<button ?disabled=${!this.is_editing} @click=${this.on_discard}>
|
||||
Discard
|
||||
</button>
|
||||
<button
|
||||
?disabled=${!this.is_editing}
|
||||
@click=${() =>
|
||||
(self.value = Object.assign({}, self.value, {
|
||||
private: !self.value.private,
|
||||
}))}
|
||||
>
|
||||
${this.value?.private ? 'Make Public' : 'Make Private'}
|
||||
</button>
|
||||
<button ?disabled=${!this.is_editing} @click=${this.on_blog_publish}>
|
||||
Publish Blog
|
||||
</button>
|
||||
<div class="inline-flex-row">
|
||||
<button ?disabled=${!this.whoami || this.is_editing} @click=${() => (self.is_editing = true)}>Edit</button>
|
||||
<button ?disabled=${this.blob == this.blob_original} @click=${this.on_save_draft}>Save Draft</button>
|
||||
<button ?disabled=${this.blob == this.blob_original && !this.value?.draft} @click=${this.on_publish}>Publish</button>
|
||||
<button ?disabled=${!this.is_editing} @click=${this.on_discard}>Discard</button>
|
||||
<button ?disabled=${!this.is_editing} @click=${() => (self.value = Object.assign({}, self.value, {private: !self.value.private}))}>${this.value?.private ? 'Make Public' : 'Make Private'}</button>
|
||||
<button ?disabled=${!this.is_editing} @click=${this.on_blog_publish}>Publish Blog</button>
|
||||
</div>
|
||||
<div ?hidden=${!this.value?.private} style="color: #800">
|
||||
🔒 document is private
|
||||
</div>
|
||||
<div
|
||||
style="display: flex; flex-direction: row; ${this.value?.private
|
||||
? 'border-top: 4px solid #800'
|
||||
: ''}"
|
||||
>
|
||||
<div ?hidden=${!this.value?.private} style="color: #800">🔒 document is private</div>
|
||||
<div class="flex-column" ${this.value?.private ? 'border-top: 4px solid #800' : ''}">
|
||||
<textarea
|
||||
rows="25"
|
||||
?hidden=${!this.is_editing}
|
||||
style="flex: 1 1; min-height: 10em; ${this.value?.private
|
||||
? 'border: 4px solid #800'
|
||||
: ''}"
|
||||
id="editor-text-area"
|
||||
@input=${this.on_edit}
|
||||
@paste=${this.paste}
|
||||
.value=${this.blob ?? ''}
|
||||
></textarea>
|
||||
<div style="flex: 1 1">
|
||||
<div
|
||||
?hidden=${!this.is_editing}
|
||||
style="border: 1px solid #fff; border-radius: 1em; padding: 0.5em"
|
||||
>
|
||||
<img
|
||||
?hidden=${!thumbnail_ref}
|
||||
style="max-width: 128px; max-height: 128px; float: right"
|
||||
src="/${thumbnail_ref}/view"
|
||||
/>
|
||||
<h1 ?hidden=${!this.title(this.blob)}>
|
||||
${unsafeHTML(this.markdown(this.title(this.blob)))}
|
||||
</h1>
|
||||
.value=${this.blob ?? ''}></textarea>
|
||||
<div style="flex: 1 1; margin-top: 16px">
|
||||
<div ?hidden=${!this.is_editing} class="box">
|
||||
Summary
|
||||
<img ?hidden=${!thumbnail_ref} style="max-width: 128px; max-height: 128px; float: right" src="/${thumbnail_ref}/view">
|
||||
<h1 ?hidden=${!this.title(this.blob)}>${unsafeHTML(this.markdown(this.title(this.blob)))}</h1>
|
||||
${unsafeHTML(this.markdown(this.summary(this.blob)))}
|
||||
</div>
|
||||
${unsafeHTML(this.markdown(this.blob))}
|
||||
|
115
apps/wiki/tildefriends.css
Normal file
115
apps/wiki/tildefriends.css
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Tilde Friends core stylesheet
|
||||
* This is a prototype; things may change based on feedback.
|
||||
*
|
||||
* This Software is an external library that is part of
|
||||
* Tilde Friends and is shared under the MIT license.
|
||||
*
|
||||
* Inject this file in your app at tildefriends.css
|
||||
* and use this tag to import it:
|
||||
* <link rel="stylesheet" href="tildefriends.css"/>
|
||||
*
|
||||
* Revision 0 / 2024 M02 19
|
||||
*/
|
||||
|
||||
body {
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
button,
|
||||
.button,
|
||||
input[type='button'],
|
||||
input[type='submit'],
|
||||
select {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 8px 12px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
margin: 4px;
|
||||
|
||||
&.red {
|
||||
background-color: #bd1e24;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.green {
|
||||
background-color: #18922d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.blue {
|
||||
background-color: #0067a7;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.yellow {
|
||||
background-color: #ee9600;
|
||||
color: black;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
filter: brightness(0.75);
|
||||
}
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #6c71c4;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #ffffff40;
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #ffffff20;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.inline-flex-row {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.box {
|
||||
background-color: #00000020;
|
||||
border: 1px solid grey;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin: 4px;
|
||||
}
|
@ -1662,6 +1662,29 @@ async function sourcePretty() {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleVisibleWhitespace() {
|
||||
let editor_style = document.getElementById('editor_style');
|
||||
/*
|
||||
* There is likely a better way to do this, but stomping on the CSS was
|
||||
* the easiest to wrangle at the time.
|
||||
*/
|
||||
if (editor_style.innerHTML.length) {
|
||||
editor_style.innerHTML = '';
|
||||
} else {
|
||||
editor_style.innerHTML = css`
|
||||
.cm-trailingSpace {
|
||||
background-color: unset !important;
|
||||
}
|
||||
.cm-highlightTab {
|
||||
background-image: unset !important;
|
||||
}
|
||||
.cm-highlightSpace:before {
|
||||
content: unset !important;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// TODOC
|
||||
window.addEventListener('load', function () {
|
||||
window.addEventListener('hashchange', hashChange);
|
||||
@ -1687,6 +1710,9 @@ window.addEventListener('load', function () {
|
||||
document
|
||||
.getElementById('pretty')
|
||||
.addEventListener('click', () => sourcePretty());
|
||||
document
|
||||
.getElementById('whitespace')
|
||||
.addEventListener('click', () => toggleVisibleWhitespace());
|
||||
document
|
||||
.getElementById('trace_button')
|
||||
.addEventListener('click', function (event) {
|
||||
|
150
core/index.html
150
core/index.html
@ -1,46 +1,156 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tilde Friends</title>
|
||||
<link type="text/css" rel="stylesheet" href="/static/style.css">
|
||||
<link type="text/css" rel="stylesheet" href="/static/w3.css">
|
||||
<link type="image/png" rel="shortcut icon" href="/static/favicon.png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link type="text/css" rel="stylesheet" href="/static/style.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||
<link type="image/png" rel="shortcut icon" href="/static/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script>
|
||||
function set_access_key_title(event) {
|
||||
if (!event.srcElement.title) {
|
||||
event.srcElement.title = `${event.srcElement.dataset.tip} [${(event.srcElement.accessKeyLabel || ('⌨️' + event.srcElement.accessKey)).toUpperCase()}]`;
|
||||
event.srcElement.title = `${event.srcElement.dataset.tip} [${(event.srcElement.accessKeyLabel || '⌨️' + event.srcElement.accessKey).toUpperCase()}]`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body style="display: flex; flex-flow: column; width: 100vw; height: 100vh; position: absolute; max-width: 100%; max-height: 100%">
|
||||
<body
|
||||
style="
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
"
|
||||
>
|
||||
<tf-navigation></tf-navigation>
|
||||
<div id="content" class="hbox" style="flex: 1 0; overflow: auto">
|
||||
<div id="editPane" class="vbox" style="flex: 0 1 100%; display: none; overflow: auto">
|
||||
<div
|
||||
id="editPane"
|
||||
class="vbox"
|
||||
style="flex: 0 1 100%; display: none; overflow: auto"
|
||||
>
|
||||
<div class="navigation w3-bar" style="display: flex">
|
||||
<button class="w3-bar-item w3-button w3-blue" id="closeEditor" name="closeEditor" accesskey="c" onmouseover="set_access_key_title(event)" data-tip="Close the editor">Close</button>
|
||||
<button class="w3-bar-item w3-button w3-blue" id="save" name="save" accesskey="s" onmouseover="set_access_key_title(event)" data-tip="Save the app under the given path">Save</button>
|
||||
<button class="w3-bar-item w3-button w3-blue" id="icon" name="icon" accesskey="i" onmouseover="set_access_key_title(event)" data-tip="Set an icon/emoji for the app">📦</button>
|
||||
<button class="w3-bar-item w3-button w3-blue" id="export" name="export" accesskey="e" onmouseover="set_access_key_title(event)" data-tip="Export app to .zip file">Export</button>
|
||||
<button class="w3-bar-item w3-button w3-blue" id="import" name="import" accesskey="i" onmouseover="set_access_key_title(event)" data-tip="Import app from .zip file">Import</button>
|
||||
<button class="w3-bar-item w3-button w3-blue" id="pretty" name="pretty" accesskey="p" onmouseover="set_access_key_title(event)" data-tip="Clean up source formatting">🧼</button>
|
||||
<input class="w3-bar-item w3-input w3-border w3-blue" type="text" id="name" name="name" style="flex: 1 1; min-width: 1em"></input>
|
||||
<button class="w3-bar-item w3-button w3-blue" id="delete" name="delete" accesskey="d" onmouseover="set_access_key_title(event)" data-tip="Delete the app">Delete</button>
|
||||
<button class="w3-bar-item w3-button w3-blue" id="trace_button" accesskey="t" onmouseover="set_access_key_title(event)" data-tip="Open a performance trace for the server">Trace</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-blue"
|
||||
id="closeEditor"
|
||||
name="closeEditor"
|
||||
accesskey="c"
|
||||
onmouseover="set_access_key_title(event)"
|
||||
data-tip="Close the editor"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-blue"
|
||||
id="save"
|
||||
name="save"
|
||||
accesskey="s"
|
||||
onmouseover="set_access_key_title(event)"
|
||||
data-tip="Save the app under the given path"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-blue"
|
||||
id="icon"
|
||||
name="icon"
|
||||
accesskey="j"
|
||||
onmouseover="set_access_key_title(event)"
|
||||
data-tip="Set an icon/emoji for the app"
|
||||
>
|
||||
📦
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-blue"
|
||||
id="export"
|
||||
name="export"
|
||||
accesskey="e"
|
||||
onmouseover="set_access_key_title(event)"
|
||||
data-tip="Export app to .zip file"
|
||||
>
|
||||
Export
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-blue"
|
||||
id="import"
|
||||
name="import"
|
||||
accesskey="i"
|
||||
onmouseover="set_access_key_title(event)"
|
||||
data-tip="Import app from .zip file"
|
||||
>
|
||||
Import
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-blue"
|
||||
id="pretty"
|
||||
name="pretty"
|
||||
accesskey="p"
|
||||
onmouseover="set_access_key_title(event)"
|
||||
data-tip="Clean up source formatting"
|
||||
>
|
||||
🧼
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-blue"
|
||||
id="whitespace"
|
||||
name="whitespace"
|
||||
accesskey="w"
|
||||
onmouseover="set_access_key_title(event)"
|
||||
data-tip="Toggle visible whitespace"
|
||||
>
|
||||
✨
|
||||
</button>
|
||||
<input
|
||||
class="w3-bar-item w3-input w3-border w3-blue"
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
style="flex: 1 1; min-width: 1em"
|
||||
/>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-blue"
|
||||
id="delete"
|
||||
name="delete"
|
||||
accesskey="d"
|
||||
onmouseover="set_access_key_title(event)"
|
||||
data-tip="Delete the app"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-blue"
|
||||
id="trace_button"
|
||||
accesskey="t"
|
||||
onmouseover="set_access_key_title(event)"
|
||||
data-tip="Open a performance trace for the server"
|
||||
>
|
||||
Trace
|
||||
</button>
|
||||
</div>
|
||||
<div class="hbox" style="flex: 1 1; overflow: auto">
|
||||
<div style="overflow: auto">
|
||||
<tf-files-pane style="overflow: auto"></tf-files-pane>
|
||||
</div>
|
||||
<div style="flex: 1 1; overflow: auto"><div id="editor" style="width: 100%; height: 100%"></div></div>
|
||||
<div style="flex: 1 1; overflow: auto">
|
||||
<style id="editor_style"></style>
|
||||
<div id="editor" style="width: 100%; height: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="viewPane" class="vbox" style="flex: 0 1 100%; overflow: auto">
|
||||
<iframe id="document" sandbox="allow-forms allow-scripts allow-top-navigation allow-modals allow-popups allow-downloads" style="width: 100%; height: 100%; border: 0"></iframe>
|
||||
<iframe
|
||||
id="document"
|
||||
sandbox="allow-forms allow-scripts allow-top-navigation allow-modals allow-popups allow-downloads"
|
||||
style="width: 100%; height: 100%; border: 0"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<script>window.litDisableBundleWarning = true;</script>
|
||||
<script>
|
||||
window.litDisableBundleWarning = true;
|
||||
</script>
|
||||
<script src="/static/client.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
2
deps/codemirror/cm6.js
vendored
2
deps/codemirror/cm6.js
vendored
File diff suppressed because one or more lines are too long
547
deps/codemirror_src/package-lock.json
generated
vendored
547
deps/codemirror_src/package-lock.json
generated
vendored
@ -75,9 +75,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-javascript": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz",
|
||||
"integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz",
|
||||
"integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/language": "^6.6.0",
|
||||
@ -88,24 +88,6 @@
|
||||
"@lezer/javascript": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-javascript/node_modules/@lezer/javascript": {
|
||||
"version": "1.4.13",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.13.tgz",
|
||||
"integrity": "sha512-5IBr8LIO3xJdJH1e9aj/ZNLE4LSbdsx25wFmGRAZsj2zSmwAYjx26JyU/BYOCpRQlu1jcv1z3vy4NB9+UkfRow==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
"@lezer/lr": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-javascript/node_modules/@lezer/lr": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz",
|
||||
"integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-json": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
||||
@ -128,19 +110,6 @@
|
||||
"style-mod": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language/node_modules/@lezer/lr": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz",
|
||||
"integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language/node_modules/style-mod": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz",
|
||||
"integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA=="
|
||||
},
|
||||
"node_modules/@codemirror/lint": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.5.0.tgz",
|
||||
@ -151,11 +120,6 @@
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lint/node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
||||
},
|
||||
"node_modules/@codemirror/search": {
|
||||
"version": "6.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.6.tgz",
|
||||
@ -166,11 +130,6 @@
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/search/node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
||||
},
|
||||
"node_modules/@codemirror/state": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
|
||||
@ -188,24 +147,72 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.24.0.tgz",
|
||||
"integrity": "sha512-zK6m5pNkdhdJl8idPP1gA4N8JKTiSsOz8U/Iw+C1ChMwyLG7+MLiNXnH/wFuAk6KeGEe33/adOiAh5jMqee03w==",
|
||||
"version": "6.24.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.24.1.tgz",
|
||||
"integrity": "sha512-sBfP4rniPBRQzNakwuQEqjEuiJDWJyF2kqLLqij4WXRoVwPPJfjx966Eq3F7+OPQxDtMt/Q9MWLoZLWjeveBlg==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"style-mod": "^4.1.0",
|
||||
"w3c-keyname": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/view/node_modules/style-mod": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz",
|
||||
"integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA=="
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz",
|
||||
"integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/view/node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
|
||||
"integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.23",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz",
|
||||
"integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/common": {
|
||||
"version": "1.2.1",
|
||||
@ -222,14 +229,6 @@
|
||||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/css/node_modules/@lezer/lr": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz",
|
||||
"integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/highlight": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz",
|
||||
@ -239,21 +238,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/html": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.8.tgz",
|
||||
"integrity": "sha512-EXseJ3pUzWxE6XQBQdqWHZqqlGQRSuNMBcLb6mZWS2J2v+QZhOObD+3ZIKIcm59ntTzyor4LqFTb72iJc3k23Q==",
|
||||
"version": "1.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.9.tgz",
|
||||
"integrity": "sha512-MXxeCMPyrcemSLGaTQEZx0dBUH0i+RPl8RN5GwMAzo53nTsd/Unc/t5ZxACeQoyPUM5/GkPLRUs2WliOImzkRA==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/html/node_modules/@lezer/lr": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz",
|
||||
"integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
|
||||
"node_modules/@lezer/javascript": {
|
||||
"version": "1.4.13",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.13.tgz",
|
||||
"integrity": "sha512-5IBr8LIO3xJdJH1e9aj/ZNLE4LSbdsx25wFmGRAZsj2zSmwAYjx26JyU/BYOCpRQlu1jcv1z3vy4NB9+UkfRow==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
"@lezer/lr": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/json": {
|
||||
@ -266,7 +267,7 @@
|
||||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/json/node_modules/@lezer/lr": {
|
||||
"node_modules/@lezer/lr": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz",
|
||||
"integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
|
||||
@ -341,27 +342,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz",
|
||||
@ -458,6 +438,30 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz",
|
||||
"integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz",
|
||||
"integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz",
|
||||
@ -494,11 +498,45 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/builtin-modules": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
|
||||
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/codemirror": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
||||
@ -513,6 +551,17 @@
|
||||
"@codemirror/view": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
||||
},
|
||||
"node_modules/deepmerge": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
@ -521,6 +570,11 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@ -534,6 +588,25 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
|
||||
"integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/is-builtin-module": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
|
||||
@ -548,15 +621,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-builtin-module/node_modules/builtin-modules": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
|
||||
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
||||
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-module": {
|
||||
@ -564,6 +637,31 @@
|
||||
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
@ -580,52 +678,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve/node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve/node_modules/hasown": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
|
||||
"integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve/node_modules/is-core-module": {
|
||||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
||||
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve/node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"node_modules/resolve/node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz",
|
||||
@ -657,54 +709,7 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz",
|
||||
"integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/rollup/node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz",
|
||||
"integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/rollup/node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serialize-javascript/node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serialize-javascript/node_modules/safe-buffer": {
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
@ -724,16 +729,60 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/smob": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/smob/-/smob-1.4.1.tgz",
|
||||
"integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/style-mod": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz",
|
||||
"integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA=="
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.27.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.27.2.tgz",
|
||||
"integrity": "sha512-sHXmLSkImesJ4p5apTeT63DsV4Obe1s37qT8qvwHRmVxKTBH7Rv9Wr26VcAMmLbmk9UliiwK8z+657NyJHHy/w==",
|
||||
"version": "5.28.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz",
|
||||
"integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
@ -748,106 +797,10 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
|
||||
"integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/terser/node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.22",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
|
||||
"integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/terser/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/terser/node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
deps/xopt/.circleci/Dockerfile.test
vendored
17
deps/xopt/.circleci/Dockerfile.test
vendored
@ -1,17 +0,0 @@
|
||||
FROM alpine:3.7 AS base
|
||||
WORKDIR /src
|
||||
RUN apk --update add alpine-sdk cmake bash
|
||||
COPY . ./
|
||||
WORKDIR /src/build
|
||||
|
||||
# Debug
|
||||
FROM base
|
||||
RUN cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=On
|
||||
RUN cmake --build .
|
||||
RUN ctest -VV
|
||||
|
||||
# Release
|
||||
FROM base
|
||||
RUN cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=On
|
||||
RUN cmake --build .
|
||||
RUN ctest -VV
|
7
deps/xopt/.circleci/config.yml
vendored
7
deps/xopt/.circleci/config.yml
vendored
@ -1,7 +0,0 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
machine: true
|
||||
steps:
|
||||
- checkout
|
||||
- run: docker build -f .circleci/Dockerfile.test .
|
1
deps/xopt/.dockerignore
vendored
1
deps/xopt/.dockerignore
vendored
@ -1 +0,0 @@
|
||||
.gitignore
|
18
deps/xopt/.editorconfig
vendored
18
deps/xopt/.editorconfig
vendored
@ -1,18 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.coffee]
|
||||
indent_style = space
|
||||
|
||||
[{package.json,*.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
6
deps/xopt/.gitignore
vendored
6
deps/xopt/.gitignore
vendored
@ -1,6 +0,0 @@
|
||||
*.sw[a-p]
|
||||
/bin/
|
||||
/build/
|
||||
/.tup/
|
||||
/*.o
|
||||
/*.a
|
43
deps/xopt/CMakeLists.txt
vendored
43
deps/xopt/CMakeLists.txt
vendored
@ -1,43 +0,0 @@
|
||||
cmake_minimum_required (VERSION 3.8)
|
||||
project (xopt)
|
||||
|
||||
add_library (xopt STATIC "${CMAKE_CURRENT_SOURCE_DIR}/xopt.c")
|
||||
|
||||
if (APPLE OR UNIX)
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -pedantic -std=c11 -D_XOPEN_SOURCE=600 -fdiagnostics-color=always -fvisibility=hidden")
|
||||
set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g3 -O0")
|
||||
set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast")
|
||||
endif ()
|
||||
|
||||
function (_xopt_test name)
|
||||
if (NOT TARGET "xopt-test-${name}")
|
||||
add_executable ("xopt-test-${name}" "${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.c")
|
||||
target_link_libraries ("xopt-test-${name}" xopt)
|
||||
endif ()
|
||||
|
||||
set (testname "xopt-${name}-test")
|
||||
set (testnum 1)
|
||||
|
||||
while (TEST "${testname}-${testnum}")
|
||||
math (EXPR testnum "${testnum}+1")
|
||||
endwhile ()
|
||||
|
||||
add_test (
|
||||
NAME "${testname}-${testnum}"
|
||||
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/test/test-case.sh" $<TARGET_FILE:xopt-test-${name}> "${CMAKE_CURRENT_SOURCE_DIR}/test/${name}-${testnum}.out" ${ARGN}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/test")
|
||||
endfunction ()
|
||||
|
||||
if (BUILD_TESTING)
|
||||
enable_testing ()
|
||||
_xopt_test (simple --some-int=10 --some-double=14.5 foo bar -- --some-other=20)
|
||||
_xopt_test (macro --some-int=10 --some-double=14.5 foo bar -- --some-other=20)
|
||||
_xopt_test (required --some-int=10 --some-double=14.5 --some-required=1337 foo bar -- --some-other=20)
|
||||
_xopt_test (optional-longarg -i 10 -d 14.5 foo bar -- --some-other=20)
|
||||
_xopt_test (autohelp --help -- --is-not-passed ignoreme)
|
||||
_xopt_test (sloppyshorts -i10 -d 14.5 "-ssome string" -m -mm -mmm foo bar -- --is-not-passed ignoreme)
|
||||
_xopt_test (nocondense-sloppy -i 10 -d 14.5 -s "some string" -m -mm -mmm foo bar -- --is-not-passed ignoreme)
|
||||
_xopt_test (nocondense-sloppy -i 10 -d 14.5 "-ssome string" -m -mm -mmm foo bar -- --is-not-passed ignoreme)
|
||||
_xopt_test (nocondense-sloppy -i 10 -d 14.5 "-ssome string" -m -m -m -m -m -m foo bar -- --is-not-passed ignoreme)
|
||||
_xopt_test (nocondense -i 10 -d 14.5 "-ssome string" -m -mm -mmm foo bar -- --is-not-passed ignoreme)
|
||||
endif ()
|
9
deps/xopt/README.md
vendored
9
deps/xopt/README.md
vendored
@ -1,9 +0,0 @@
|
||||
# XOpt [](https://circleci.com/gh/Qix-/xopt)
|
||||
The sane answer to POpt.
|
||||
|
||||
XOpt is a command line argument parsing library written in ANSI C. XOpt
|
||||
accepts arguments in GNU format and focuses on clean definition, taking stress
|
||||
off the implementation.
|
||||
|
||||
# License
|
||||
Originally by Josh Junon, released under [CC0](https://creativecommons.org/publicdomain/zero/1.0/). Go nuts.
|
2099
deps/xopt/snprintf.c
vendored
2099
deps/xopt/snprintf.c
vendored
File diff suppressed because it is too large
Load Diff
2
deps/xopt/test/.gitignore
vendored
2
deps/xopt/test/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
/*-test
|
||||
/*.o
|
12
deps/xopt/test/autohelp-1.out
vendored
12
deps/xopt/test/autohelp-1.out
vendored
@ -1,12 +0,0 @@
|
||||
args: «--help» «--» «--is-not-passed» «ignoreme»
|
||||
|
||||
usage: autohelp-test-case [opts...] [--] [extras...]
|
||||
|
||||
Tests the simple parser macro
|
||||
|
||||
-i, --some-int=n Some integer value. Can set to whatever number you like.
|
||||
-f Some float value.
|
||||
--some-double=n Some double value.
|
||||
-?, --help Shows this help message
|
||||
|
||||
[end of arguments]
|
100
deps/xopt/test/autohelp.c
vendored
100
deps/xopt/test/autohelp.c
vendored
@ -1,100 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../xopt.h"
|
||||
|
||||
typedef struct {
|
||||
int someInt;
|
||||
float someFloat;
|
||||
double someDouble;
|
||||
bool help;
|
||||
} SimpleConfig;
|
||||
|
||||
xoptOption options[] = {
|
||||
{
|
||||
"some-int",
|
||||
'i',
|
||||
offsetof(SimpleConfig, someInt),
|
||||
0,
|
||||
XOPT_TYPE_INT,
|
||||
"n",
|
||||
"Some integer value. Can set to whatever number you like."
|
||||
},
|
||||
{
|
||||
0,
|
||||
'f',
|
||||
offsetof(SimpleConfig, someFloat),
|
||||
0,
|
||||
XOPT_TYPE_FLOAT,
|
||||
"n",
|
||||
"Some float value."
|
||||
},
|
||||
{
|
||||
"some-double",
|
||||
0,
|
||||
offsetof(SimpleConfig, someDouble),
|
||||
0,
|
||||
XOPT_TYPE_DOUBLE,
|
||||
"n",
|
||||
"Some double value."
|
||||
},
|
||||
{
|
||||
"help",
|
||||
'?',
|
||||
offsetof(SimpleConfig, help),
|
||||
0,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Shows this help message"
|
||||
},
|
||||
XOPT_NULLOPTION
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int exit_code = 1;
|
||||
const char *err = NULL;
|
||||
SimpleConfig config;
|
||||
const char **extras = NULL;
|
||||
int extraCount = 0;
|
||||
|
||||
/* show arguments */
|
||||
fputs("args:", stderr);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
fprintf(stderr, " «%s»", argv[i]);
|
||||
}
|
||||
fputs("\n\n", stderr);
|
||||
|
||||
/* setup defaults */
|
||||
config.someInt = 0;
|
||||
config.someDouble = 0.0;
|
||||
config.help = 0;
|
||||
|
||||
XOPT_SIMPLE_PARSE(
|
||||
"autohelp-test-case",
|
||||
0,
|
||||
&options[0], &config,
|
||||
argc, argv,
|
||||
&extraCount, &extras,
|
||||
&err,
|
||||
stderr,
|
||||
"[opts...] [--] [extras...]",
|
||||
"Tests the simple parser macro",
|
||||
"[end of arguments]",
|
||||
15);
|
||||
|
||||
if (!err) {
|
||||
err = config.help
|
||||
? "--help was passed but autohelp didn't fire"
|
||||
: "--help was not passed - it is required for this test case";
|
||||
}
|
||||
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
|
||||
exit:
|
||||
free(extras); /* DO NOT free individual strings */
|
||||
return exit_code;
|
||||
xopt_help:
|
||||
exit_code = 0;
|
||||
goto exit;
|
||||
}
|
11
deps/xopt/test/macro-1.out
vendored
11
deps/xopt/test/macro-1.out
vendored
@ -1,11 +0,0 @@
|
||||
args: «--some-int=10» «--some-double=14.5» «foo» «bar» «--» «--some-other=20»
|
||||
|
||||
someInt: 10
|
||||
someFloat: 0.000000
|
||||
someDouble: 14.500000
|
||||
help: 0
|
||||
|
||||
extra count: 3
|
||||
- foo
|
||||
- bar
|
||||
- --some-other=20
|
116
deps/xopt/test/macro.c
vendored
116
deps/xopt/test/macro.c
vendored
@ -1,116 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../xopt.h"
|
||||
|
||||
typedef struct {
|
||||
int someInt;
|
||||
float someFloat;
|
||||
double someDouble;
|
||||
bool help;
|
||||
} SimpleConfig;
|
||||
|
||||
xoptOption options[] = {
|
||||
{
|
||||
"some-int",
|
||||
'i',
|
||||
offsetof(SimpleConfig, someInt),
|
||||
0,
|
||||
XOPT_TYPE_INT,
|
||||
"n",
|
||||
"Some integer value. Can set to whatever number you like."
|
||||
},
|
||||
{
|
||||
"some-float",
|
||||
'f',
|
||||
offsetof(SimpleConfig, someFloat),
|
||||
0,
|
||||
XOPT_TYPE_FLOAT,
|
||||
"n",
|
||||
"Some float value."
|
||||
},
|
||||
{
|
||||
"some-double",
|
||||
'd',
|
||||
offsetof(SimpleConfig, someDouble),
|
||||
0,
|
||||
XOPT_TYPE_DOUBLE,
|
||||
"n",
|
||||
"Some double value."
|
||||
},
|
||||
{
|
||||
"help",
|
||||
'?',
|
||||
offsetof(SimpleConfig, help),
|
||||
0,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Shows this help message"
|
||||
},
|
||||
XOPT_NULLOPTION
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int exit_code = 1;
|
||||
const char *err = NULL;
|
||||
SimpleConfig config;
|
||||
const char **extras = NULL;
|
||||
const char **extrasPtr = NULL;
|
||||
int extraCount = 0;
|
||||
|
||||
/* show arguments */
|
||||
fputs("args:", stderr);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
fprintf(stderr, " «%s»", argv[i]);
|
||||
}
|
||||
fputs("\n\n", stderr);
|
||||
|
||||
/* setup defaults */
|
||||
config.someInt = 0;
|
||||
config.someDouble = 0.0;
|
||||
config.help = 0;
|
||||
|
||||
XOPT_SIMPLE_PARSE(
|
||||
argv[0],
|
||||
0,
|
||||
&options[0], &config,
|
||||
argc, argv,
|
||||
&extraCount, &extras,
|
||||
&err,
|
||||
stderr,
|
||||
"[opts...] [--] [extras...]",
|
||||
"Tests the simple parser macro",
|
||||
"[end of arguments]",
|
||||
15);
|
||||
|
||||
if (err) {
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* print */
|
||||
#define P(field, delim) fprintf(stderr, #field ":\t%" #delim "\n", \
|
||||
config.field)
|
||||
|
||||
P(someInt, d);
|
||||
P(someFloat, f);
|
||||
P(someDouble, f);
|
||||
P(help, d);
|
||||
|
||||
fprintf(stderr, "\nextra count: %d\n", extraCount);
|
||||
extrasPtr = extras;
|
||||
while (extraCount--) {
|
||||
fprintf(stderr, "- %s\n", *extrasPtr++);
|
||||
}
|
||||
|
||||
#undef P
|
||||
|
||||
exit_code = 0;
|
||||
exit:
|
||||
free(extras); /* DO NOT free individual strings */
|
||||
return exit_code;
|
||||
xopt_help:
|
||||
exit_code = 2;
|
||||
goto exit;
|
||||
}
|
3
deps/xopt/test/nocondense-1.out
vendored
3
deps/xopt/test/nocondense-1.out
vendored
@ -1,3 +0,0 @@
|
||||
args: «-i» «10» «-d» «14.5» «-ssome string» «-m» «-mm» «-mmm» «foo» «bar» «--» «--is-not-passed» «ignoreme»
|
||||
|
||||
Error: short option parameters must be separated, not condensed: -ssome string
|
3
deps/xopt/test/nocondense-sloppy-1.out
vendored
3
deps/xopt/test/nocondense-sloppy-1.out
vendored
@ -1,3 +0,0 @@
|
||||
args: «-i» «10» «-d» «14.5» «-s» «some string» «-m» «-mm» «-mmm» «foo» «bar» «--» «--is-not-passed» «ignoreme»
|
||||
|
||||
Error: short options cannot be combined: -mm
|
3
deps/xopt/test/nocondense-sloppy-2.out
vendored
3
deps/xopt/test/nocondense-sloppy-2.out
vendored
@ -1,3 +0,0 @@
|
||||
args: «-i» «10» «-d» «14.5» «-ssome string» «-m» «-mm» «-mmm» «foo» «bar» «--» «--is-not-passed» «ignoreme»
|
||||
|
||||
Error: short options cannot be combined: -mm
|
14
deps/xopt/test/nocondense-sloppy-3.out
vendored
14
deps/xopt/test/nocondense-sloppy-3.out
vendored
@ -1,14 +0,0 @@
|
||||
args: «-i» «10» «-d» «14.5» «-ssome string» «-m» «-m» «-m» «-m» «-m» «-m» «foo» «bar» «--» «--is-not-passed» «ignoreme»
|
||||
|
||||
someInt: 10
|
||||
someFloat: 0.000000
|
||||
someDouble: 14.500000
|
||||
someString: some string
|
||||
multiCount: 6
|
||||
help: 0
|
||||
|
||||
extra count: 4
|
||||
- foo
|
||||
- bar
|
||||
- --is-not-passed
|
||||
- ignoreme
|
149
deps/xopt/test/nocondense-sloppy.c
vendored
149
deps/xopt/test/nocondense-sloppy.c
vendored
@ -1,149 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../xopt.h"
|
||||
|
||||
typedef struct {
|
||||
int someInt;
|
||||
float someFloat;
|
||||
double someDouble;
|
||||
const char *someString;
|
||||
int multiCount;
|
||||
bool help;
|
||||
} SimpleConfig;
|
||||
|
||||
static void on_verbose(const char *v, void *data, const struct xoptOption *option, bool longArg, const char **err) {
|
||||
(void) v;
|
||||
(void) longArg;
|
||||
(void) err;
|
||||
int *count = (int *)(((char *) data) + option->offset);
|
||||
++*count;
|
||||
}
|
||||
|
||||
xoptOption options[] = {
|
||||
{
|
||||
"some-int",
|
||||
'i',
|
||||
offsetof(SimpleConfig, someInt),
|
||||
0,
|
||||
XOPT_TYPE_INT,
|
||||
"n",
|
||||
"Some integer value. Can set to whatever number you like."
|
||||
},
|
||||
{
|
||||
"some-float",
|
||||
'f',
|
||||
offsetof(SimpleConfig, someFloat),
|
||||
0,
|
||||
XOPT_TYPE_FLOAT,
|
||||
"n",
|
||||
"Some float value."
|
||||
},
|
||||
{
|
||||
"some-double",
|
||||
'd',
|
||||
offsetof(SimpleConfig, someDouble),
|
||||
0,
|
||||
XOPT_TYPE_DOUBLE,
|
||||
"n",
|
||||
"Some double value."
|
||||
},
|
||||
{
|
||||
"some-string",
|
||||
's',
|
||||
offsetof(SimpleConfig, someString),
|
||||
0,
|
||||
XOPT_TYPE_STRING,
|
||||
"s",
|
||||
"Some string value."
|
||||
},
|
||||
{
|
||||
0,
|
||||
'm',
|
||||
offsetof(SimpleConfig, multiCount),
|
||||
&on_verbose,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Specify multiple times to increase count"
|
||||
},
|
||||
{
|
||||
"help",
|
||||
'?',
|
||||
offsetof(SimpleConfig, help),
|
||||
0,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Shows this help message"
|
||||
},
|
||||
XOPT_NULLOPTION
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int exit_code = 1;
|
||||
const char *err = NULL;
|
||||
SimpleConfig config;
|
||||
const char **extras = NULL;
|
||||
const char **extrasPtr = NULL;
|
||||
int extraCount = 0;
|
||||
|
||||
/* show arguments */
|
||||
fputs("args:", stderr);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
fprintf(stderr, " «%s»", argv[i]);
|
||||
}
|
||||
fputs("\n\n", stderr);
|
||||
|
||||
/* setup defaults */
|
||||
config.someInt = 0;
|
||||
config.someDouble = 0.0;
|
||||
config.someFloat = 0.0f;
|
||||
config.someString = 0;
|
||||
config.multiCount = 0;
|
||||
config.help = 0;
|
||||
|
||||
XOPT_SIMPLE_PARSE(
|
||||
argv[0],
|
||||
XOPT_CTX_SLOPPYSHORTS | XOPT_CTX_NOCONDENSE,
|
||||
&options[0], &config,
|
||||
argc, argv,
|
||||
&extraCount, &extras,
|
||||
&err,
|
||||
stderr,
|
||||
"[opts...] [--] [extras...]",
|
||||
"Tests the simple parser macro",
|
||||
"[end of arguments]",
|
||||
15);
|
||||
|
||||
if (err) {
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* print */
|
||||
#define P(field, delim) fprintf(stderr, #field ":\t%" #delim "\n", \
|
||||
config.field)
|
||||
|
||||
P(someInt, d);
|
||||
P(someFloat, f);
|
||||
P(someDouble, f);
|
||||
P(someString, s);
|
||||
P(multiCount, d);
|
||||
P(help, d);
|
||||
|
||||
fprintf(stderr, "\nextra count: %d\n", extraCount);
|
||||
extrasPtr = extras;
|
||||
while (extraCount--) {
|
||||
fprintf(stderr, "- %s\n", *extrasPtr++);
|
||||
}
|
||||
|
||||
#undef P
|
||||
|
||||
exit_code = 0;
|
||||
exit:
|
||||
free(extras); /* DO NOT free individual strings */
|
||||
return exit_code;
|
||||
xopt_help:
|
||||
exit_code = 2;
|
||||
goto exit;
|
||||
}
|
149
deps/xopt/test/nocondense.c
vendored
149
deps/xopt/test/nocondense.c
vendored
@ -1,149 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../xopt.h"
|
||||
|
||||
typedef struct {
|
||||
int someInt;
|
||||
float someFloat;
|
||||
double someDouble;
|
||||
const char *someString;
|
||||
int multiCount;
|
||||
bool help;
|
||||
} SimpleConfig;
|
||||
|
||||
static void on_verbose(const char *v, void *data, const struct xoptOption *option, bool longArg, const char **err) {
|
||||
(void) v;
|
||||
(void) longArg;
|
||||
(void) err;
|
||||
int *count = (int *)(((char *) data) + option->offset);
|
||||
++*count;
|
||||
}
|
||||
|
||||
xoptOption options[] = {
|
||||
{
|
||||
"some-int",
|
||||
'i',
|
||||
offsetof(SimpleConfig, someInt),
|
||||
0,
|
||||
XOPT_TYPE_INT,
|
||||
"n",
|
||||
"Some integer value. Can set to whatever number you like."
|
||||
},
|
||||
{
|
||||
"some-float",
|
||||
'f',
|
||||
offsetof(SimpleConfig, someFloat),
|
||||
0,
|
||||
XOPT_TYPE_FLOAT,
|
||||
"n",
|
||||
"Some float value."
|
||||
},
|
||||
{
|
||||
"some-double",
|
||||
'd',
|
||||
offsetof(SimpleConfig, someDouble),
|
||||
0,
|
||||
XOPT_TYPE_DOUBLE,
|
||||
"n",
|
||||
"Some double value."
|
||||
},
|
||||
{
|
||||
"some-string",
|
||||
's',
|
||||
offsetof(SimpleConfig, someString),
|
||||
0,
|
||||
XOPT_TYPE_STRING,
|
||||
"s",
|
||||
"Some string value."
|
||||
},
|
||||
{
|
||||
0,
|
||||
'm',
|
||||
offsetof(SimpleConfig, multiCount),
|
||||
&on_verbose,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Specify multiple times to increase count"
|
||||
},
|
||||
{
|
||||
"help",
|
||||
'?',
|
||||
offsetof(SimpleConfig, help),
|
||||
0,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Shows this help message"
|
||||
},
|
||||
XOPT_NULLOPTION
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int exit_code = 1;
|
||||
const char *err = NULL;
|
||||
SimpleConfig config;
|
||||
const char **extras = NULL;
|
||||
const char **extrasPtr = NULL;
|
||||
int extraCount = 0;
|
||||
|
||||
/* show arguments */
|
||||
fputs("args:", stderr);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
fprintf(stderr, " «%s»", argv[i]);
|
||||
}
|
||||
fputs("\n\n", stderr);
|
||||
|
||||
/* setup defaults */
|
||||
config.someInt = 0;
|
||||
config.someDouble = 0.0;
|
||||
config.someFloat = 0.0f;
|
||||
config.someString = 0;
|
||||
config.multiCount = 0;
|
||||
config.help = 0;
|
||||
|
||||
XOPT_SIMPLE_PARSE(
|
||||
argv[0],
|
||||
XOPT_CTX_NOCONDENSE,
|
||||
&options[0], &config,
|
||||
argc, argv,
|
||||
&extraCount, &extras,
|
||||
&err,
|
||||
stderr,
|
||||
"[opts...] [--] [extras...]",
|
||||
"Tests the simple parser macro",
|
||||
"[end of arguments]",
|
||||
15);
|
||||
|
||||
if (err) {
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* print */
|
||||
#define P(field, delim) fprintf(stderr, #field ":\t%" #delim "\n", \
|
||||
config.field)
|
||||
|
||||
P(someInt, d);
|
||||
P(someFloat, f);
|
||||
P(someDouble, f);
|
||||
P(someString, s);
|
||||
P(multiCount, d);
|
||||
P(help, d);
|
||||
|
||||
fprintf(stderr, "\nextra count: %d\n", extraCount);
|
||||
extrasPtr = extras;
|
||||
while (extraCount--) {
|
||||
fprintf(stderr, "- %s\n", *extrasPtr++);
|
||||
}
|
||||
|
||||
#undef P
|
||||
|
||||
exit_code = 0;
|
||||
exit:
|
||||
free(extras); /* DO NOT free individual strings */
|
||||
return exit_code;
|
||||
xopt_help:
|
||||
exit_code = 2;
|
||||
goto exit;
|
||||
}
|
11
deps/xopt/test/optional-longarg-1.out
vendored
11
deps/xopt/test/optional-longarg-1.out
vendored
@ -1,11 +0,0 @@
|
||||
args: «-i» «10» «-d» «14.5» «foo» «bar» «--» «--some-other=20»
|
||||
|
||||
someInt: 10
|
||||
someFloat: 0.000000
|
||||
someDouble: 14.500000
|
||||
help: 0
|
||||
|
||||
extra count: 3
|
||||
- foo
|
||||
- bar
|
||||
- --some-other=20
|
116
deps/xopt/test/optional-longarg.c
vendored
116
deps/xopt/test/optional-longarg.c
vendored
@ -1,116 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../xopt.h"
|
||||
|
||||
typedef struct {
|
||||
int someInt;
|
||||
float someFloat;
|
||||
double someDouble;
|
||||
bool help;
|
||||
} SimpleConfig;
|
||||
|
||||
xoptOption options[] = {
|
||||
{
|
||||
0,
|
||||
'i',
|
||||
offsetof(SimpleConfig, someInt),
|
||||
0,
|
||||
XOPT_TYPE_INT,
|
||||
"n",
|
||||
"Some integer value. Can set to whatever number you like."
|
||||
},
|
||||
{
|
||||
0,
|
||||
'f',
|
||||
offsetof(SimpleConfig, someFloat),
|
||||
0,
|
||||
XOPT_TYPE_FLOAT,
|
||||
"n",
|
||||
"Some float value."
|
||||
},
|
||||
{
|
||||
0,
|
||||
'd',
|
||||
offsetof(SimpleConfig, someDouble),
|
||||
0,
|
||||
XOPT_TYPE_DOUBLE,
|
||||
"n",
|
||||
"Some double value."
|
||||
},
|
||||
{
|
||||
"help",
|
||||
'?',
|
||||
offsetof(SimpleConfig, help),
|
||||
0,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Shows this help message"
|
||||
},
|
||||
XOPT_NULLOPTION
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int exit_code = 1;
|
||||
const char *err = NULL;
|
||||
SimpleConfig config;
|
||||
const char **extras = NULL;
|
||||
const char **extrasPtr = NULL;
|
||||
int extraCount = 0;
|
||||
|
||||
/* show arguments */
|
||||
fputs("args:", stderr);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
fprintf(stderr, " «%s»", argv[i]);
|
||||
}
|
||||
fputs("\n\n", stderr);
|
||||
|
||||
/* setup defaults */
|
||||
config.someInt = 0;
|
||||
config.someDouble = 0.0;
|
||||
config.help = 0;
|
||||
|
||||
XOPT_SIMPLE_PARSE(
|
||||
argv[0],
|
||||
0,
|
||||
&options[0], &config,
|
||||
argc, argv,
|
||||
&extraCount, &extras,
|
||||
&err,
|
||||
stderr,
|
||||
"macro-test [opts...] [--] [extras...]",
|
||||
"Tests the simple parser macro",
|
||||
"[end of arguments]",
|
||||
15);
|
||||
|
||||
if (err) {
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* print */
|
||||
#define P(field, delim) fprintf(stderr, #field ":\t%" #delim "\n", \
|
||||
config.field)
|
||||
|
||||
P(someInt, d);
|
||||
P(someFloat, f);
|
||||
P(someDouble, f);
|
||||
P(help, d);
|
||||
|
||||
fprintf(stderr, "\nextra count: %d\n", extraCount);
|
||||
extrasPtr = extras;
|
||||
while (extraCount--) {
|
||||
fprintf(stderr, "- %s\n", *extrasPtr++);
|
||||
}
|
||||
|
||||
#undef P
|
||||
|
||||
exit_code = 0;
|
||||
exit:
|
||||
free(extras); /* DO NOT free individual strings */
|
||||
return exit_code;
|
||||
xopt_help:
|
||||
exit_code = 2;
|
||||
goto exit;
|
||||
}
|
12
deps/xopt/test/required-1.out
vendored
12
deps/xopt/test/required-1.out
vendored
@ -1,12 +0,0 @@
|
||||
args: «--some-int=10» «--some-double=14.5» «--some-required=1337» «foo» «bar» «--» «--some-other=20»
|
||||
|
||||
someInt: 10
|
||||
someFloat: 0.000000
|
||||
someDouble: 14.500000
|
||||
someRequired: 1337
|
||||
help: 0
|
||||
|
||||
extra count: 3
|
||||
- foo
|
||||
- bar
|
||||
- --some-other=20
|
128
deps/xopt/test/required.c
vendored
128
deps/xopt/test/required.c
vendored
@ -1,128 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../xopt.h"
|
||||
|
||||
typedef struct {
|
||||
int someInt;
|
||||
float someFloat;
|
||||
double someDouble;
|
||||
int someRequired;
|
||||
bool help;
|
||||
} SimpleConfig;
|
||||
|
||||
xoptOption options[] = {
|
||||
{
|
||||
"some-int",
|
||||
'i',
|
||||
offsetof(SimpleConfig, someInt),
|
||||
0,
|
||||
XOPT_TYPE_INT,
|
||||
"n",
|
||||
"Some integer value. Can set to whatever number you like."
|
||||
},
|
||||
{
|
||||
"some-float",
|
||||
'f',
|
||||
offsetof(SimpleConfig, someFloat),
|
||||
0,
|
||||
XOPT_TYPE_FLOAT,
|
||||
"n",
|
||||
"Some float value."
|
||||
},
|
||||
{
|
||||
"some-double",
|
||||
'd',
|
||||
offsetof(SimpleConfig, someDouble),
|
||||
0,
|
||||
XOPT_TYPE_DOUBLE,
|
||||
"n",
|
||||
"Some double value."
|
||||
},
|
||||
{
|
||||
"some-required",
|
||||
'r',
|
||||
offsetof(SimpleConfig, someRequired),
|
||||
0,
|
||||
XOPT_TYPE_INT | XOPT_REQUIRED,
|
||||
"n",
|
||||
"Some value"
|
||||
},
|
||||
{
|
||||
"help",
|
||||
'?',
|
||||
offsetof(SimpleConfig, help),
|
||||
0,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Shows this help message"
|
||||
},
|
||||
XOPT_NULLOPTION
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int exit_code = 1;
|
||||
const char *err = NULL;
|
||||
SimpleConfig config;
|
||||
const char **extras = NULL;
|
||||
const char **extrasPtr = NULL;
|
||||
int extraCount = 0;
|
||||
|
||||
/* show arguments */
|
||||
fputs("args:", stderr);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
fprintf(stderr, " «%s»", argv[i]);
|
||||
}
|
||||
fputs("\n\n", stderr);
|
||||
|
||||
/* setup defaults */
|
||||
config.someInt = 0;
|
||||
config.someDouble = 0.0;
|
||||
config.someRequired = 0;
|
||||
config.help = 0;
|
||||
|
||||
XOPT_SIMPLE_PARSE(
|
||||
argv[0],
|
||||
0,
|
||||
&options[0], &config,
|
||||
argc, argv,
|
||||
&extraCount, &extras,
|
||||
&err,
|
||||
stderr,
|
||||
"[opts...] [--] [extras...]",
|
||||
"Tests the simple parser macro",
|
||||
"[end of arguments]",
|
||||
15);
|
||||
|
||||
if (err) {
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* print */
|
||||
#define P(field, delim) fprintf(stderr, #field ":\t%" #delim "\n", \
|
||||
config.field)
|
||||
|
||||
P(someInt, d);
|
||||
P(someFloat, f);
|
||||
P(someDouble, f);
|
||||
P(someRequired, d);
|
||||
P(help, d);
|
||||
|
||||
fprintf(stderr, "\nextra count: %d\n", extraCount);
|
||||
extrasPtr = extras;
|
||||
while (extraCount--) {
|
||||
fprintf(stderr, "- %s\n", *extrasPtr++);
|
||||
}
|
||||
|
||||
#undef P
|
||||
|
||||
exit_code = 0;
|
||||
exit:
|
||||
free(extras); /* DO NOT free individual strings */
|
||||
return exit_code;
|
||||
xopt_help:
|
||||
exit_code = 2;
|
||||
goto exit;
|
||||
}
|
11
deps/xopt/test/simple-1.out
vendored
11
deps/xopt/test/simple-1.out
vendored
@ -1,11 +0,0 @@
|
||||
args: «--some-int=10» «--some-double=14.5» «foo» «bar» «--» «--some-other=20»
|
||||
|
||||
someInt: 10
|
||||
someFloat: 0.000000
|
||||
someDouble: 14.500000
|
||||
help: 0
|
||||
|
||||
extra count: 3
|
||||
- foo
|
||||
- bar
|
||||
- --some-other=20
|
128
deps/xopt/test/simple.c
vendored
128
deps/xopt/test/simple.c
vendored
@ -1,128 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../xopt.h"
|
||||
|
||||
typedef struct {
|
||||
int someInt;
|
||||
float someFloat;
|
||||
double someDouble;
|
||||
bool help;
|
||||
} SimpleConfig;
|
||||
|
||||
xoptOption options[] = {
|
||||
{
|
||||
"some-int",
|
||||
'i',
|
||||
offsetof(SimpleConfig, someInt),
|
||||
0,
|
||||
XOPT_TYPE_INT,
|
||||
"n",
|
||||
"Some integer value. Can set to whatever number you like."
|
||||
},
|
||||
{
|
||||
"some-float",
|
||||
'f',
|
||||
offsetof(SimpleConfig, someFloat),
|
||||
0,
|
||||
XOPT_TYPE_FLOAT,
|
||||
"n",
|
||||
"Some float value."
|
||||
},
|
||||
{
|
||||
"some-double",
|
||||
'd',
|
||||
offsetof(SimpleConfig, someDouble),
|
||||
0,
|
||||
XOPT_TYPE_DOUBLE,
|
||||
"n",
|
||||
"Some double value."
|
||||
},
|
||||
{
|
||||
"help",
|
||||
'?',
|
||||
offsetof(SimpleConfig, help),
|
||||
0,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Shows this help message"
|
||||
},
|
||||
XOPT_NULLOPTION
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int result;
|
||||
const char *err;
|
||||
xoptContext *ctx;
|
||||
SimpleConfig config;
|
||||
const char **extras = 0;
|
||||
const char **extrasPtr = 0;
|
||||
int extraCount;
|
||||
|
||||
result = 0;
|
||||
err = 0;
|
||||
|
||||
/* show arguments */
|
||||
fputs("args:", stderr);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
fprintf(stderr, " «%s»", argv[i]);
|
||||
}
|
||||
fputs("\n\n", stderr);
|
||||
|
||||
/* setup defaults */
|
||||
config.someInt = 0;
|
||||
config.someDouble = 0.0;
|
||||
config.help = 0;
|
||||
|
||||
/* create context */
|
||||
ctx = xopt_context("xopt-test", options,
|
||||
XOPT_CTX_POSIXMEHARDER | XOPT_CTX_STRICT, &err);
|
||||
if (err) {
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
result = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* parse */
|
||||
extraCount = xopt_parse(ctx, argc, argv, &config, &extras, &err);
|
||||
if (err) {
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
result = 2;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* help? */
|
||||
if (config.help) {
|
||||
xoptAutohelpOptions opts;
|
||||
opts.usage = "[options] [extras...]";
|
||||
opts.prefix = "A simple demonstration of the XOpt options parser library.";
|
||||
opts.suffix = "End argument list.";
|
||||
opts.spacer = 10;
|
||||
|
||||
xopt_autohelp(ctx, stderr, &opts, &err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* print */
|
||||
#define P(field, delim) fprintf(stderr, #field ":\t%" #delim "\n", \
|
||||
config.field)
|
||||
|
||||
P(someInt, d);
|
||||
P(someFloat, f);
|
||||
P(someDouble, f);
|
||||
P(help, d);
|
||||
|
||||
fprintf(stderr, "\nextra count: %d\n", extraCount);
|
||||
extrasPtr = extras;
|
||||
while (extraCount--) {
|
||||
fprintf(stderr, "- %s\n", *extrasPtr++);
|
||||
}
|
||||
|
||||
#undef P
|
||||
|
||||
exit:
|
||||
if (extras) free(extras); /* DO NOT free individual strings */
|
||||
if (ctx) free(ctx); /* they point to argv strings */
|
||||
return result;
|
||||
}
|
14
deps/xopt/test/sloppyshorts-1.out
vendored
14
deps/xopt/test/sloppyshorts-1.out
vendored
@ -1,14 +0,0 @@
|
||||
args: «-i10» «-d» «14.5» «-ssome string» «-m» «-mm» «-mmm» «foo» «bar» «--» «--is-not-passed» «ignoreme»
|
||||
|
||||
someInt: 10
|
||||
someFloat: 0.000000
|
||||
someDouble: 14.500000
|
||||
someString: some string
|
||||
multiCount: 6
|
||||
help: 0
|
||||
|
||||
extra count: 4
|
||||
- foo
|
||||
- bar
|
||||
- --is-not-passed
|
||||
- ignoreme
|
149
deps/xopt/test/sloppyshorts.c
vendored
149
deps/xopt/test/sloppyshorts.c
vendored
@ -1,149 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../xopt.h"
|
||||
|
||||
typedef struct {
|
||||
int someInt;
|
||||
float someFloat;
|
||||
double someDouble;
|
||||
const char *someString;
|
||||
int multiCount;
|
||||
bool help;
|
||||
} SimpleConfig;
|
||||
|
||||
static void on_verbose(const char *v, void *data, const struct xoptOption *option, bool longArg, const char **err) {
|
||||
(void) v;
|
||||
(void) longArg;
|
||||
(void) err;
|
||||
int *count = (int *)(((char *) data) + option->offset);
|
||||
++*count;
|
||||
}
|
||||
|
||||
xoptOption options[] = {
|
||||
{
|
||||
"some-int",
|
||||
'i',
|
||||
offsetof(SimpleConfig, someInt),
|
||||
0,
|
||||
XOPT_TYPE_INT,
|
||||
"n",
|
||||
"Some integer value. Can set to whatever number you like."
|
||||
},
|
||||
{
|
||||
"some-float",
|
||||
'f',
|
||||
offsetof(SimpleConfig, someFloat),
|
||||
0,
|
||||
XOPT_TYPE_FLOAT,
|
||||
"n",
|
||||
"Some float value."
|
||||
},
|
||||
{
|
||||
"some-double",
|
||||
'd',
|
||||
offsetof(SimpleConfig, someDouble),
|
||||
0,
|
||||
XOPT_TYPE_DOUBLE,
|
||||
"n",
|
||||
"Some double value."
|
||||
},
|
||||
{
|
||||
"some-string",
|
||||
's',
|
||||
offsetof(SimpleConfig, someString),
|
||||
0,
|
||||
XOPT_TYPE_STRING,
|
||||
"s",
|
||||
"Some string value."
|
||||
},
|
||||
{
|
||||
0,
|
||||
'm',
|
||||
offsetof(SimpleConfig, multiCount),
|
||||
&on_verbose,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Specify multiple times to increase count"
|
||||
},
|
||||
{
|
||||
"help",
|
||||
'?',
|
||||
offsetof(SimpleConfig, help),
|
||||
0,
|
||||
XOPT_TYPE_BOOL,
|
||||
0,
|
||||
"Shows this help message"
|
||||
},
|
||||
XOPT_NULLOPTION
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int exit_code = 1;
|
||||
const char *err = NULL;
|
||||
SimpleConfig config;
|
||||
const char **extras = NULL;
|
||||
const char **extrasPtr = NULL;
|
||||
int extraCount = 0;
|
||||
|
||||
/* show arguments */
|
||||
fputs("args:", stderr);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
fprintf(stderr, " «%s»", argv[i]);
|
||||
}
|
||||
fputs("\n\n", stderr);
|
||||
|
||||
/* setup defaults */
|
||||
config.someInt = 0;
|
||||
config.someDouble = 0.0;
|
||||
config.someFloat = 0.0f;
|
||||
config.someString = 0;
|
||||
config.multiCount = 0;
|
||||
config.help = 0;
|
||||
|
||||
XOPT_SIMPLE_PARSE(
|
||||
argv[0],
|
||||
XOPT_CTX_SLOPPYSHORTS,
|
||||
&options[0], &config,
|
||||
argc, argv,
|
||||
&extraCount, &extras,
|
||||
&err,
|
||||
stderr,
|
||||
"[opts...] [--] [extras...]",
|
||||
"Tests the simple parser macro",
|
||||
"[end of arguments]",
|
||||
15);
|
||||
|
||||
if (err) {
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* print */
|
||||
#define P(field, delim) fprintf(stderr, #field ":\t%" #delim "\n", \
|
||||
config.field)
|
||||
|
||||
P(someInt, d);
|
||||
P(someFloat, f);
|
||||
P(someDouble, f);
|
||||
P(someString, s);
|
||||
P(multiCount, d);
|
||||
P(help, d);
|
||||
|
||||
fprintf(stderr, "\nextra count: %d\n", extraCount);
|
||||
extrasPtr = extras;
|
||||
while (extraCount--) {
|
||||
fprintf(stderr, "- %s\n", *extrasPtr++);
|
||||
}
|
||||
|
||||
#undef P
|
||||
|
||||
exit_code = 0;
|
||||
exit:
|
||||
free(extras); /* DO NOT free individual strings */
|
||||
return exit_code;
|
||||
xopt_help:
|
||||
exit_code = 2;
|
||||
goto exit;
|
||||
}
|
46
deps/xopt/test/test-case.sh
vendored
46
deps/xopt/test/test-case.sh
vendored
@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# To be used with CMake builds located in <xopt>/build/
|
||||
#
|
||||
|
||||
set -uo pipefail
|
||||
exec >&2
|
||||
|
||||
casebin="$1"
|
||||
caseout="$2"
|
||||
shift 2
|
||||
|
||||
function die {
|
||||
echo -e "error: $*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
if [ -z "$casebin" ]; then
|
||||
die 'no test case executable specified'
|
||||
fi
|
||||
|
||||
if [ -z "$caseout" ]; then
|
||||
die 'no test case output (some-case.out) specified'
|
||||
fi
|
||||
|
||||
if [ ! -x "$casebin" ]; then
|
||||
die "test case does not exist or is not executable: $casebin"
|
||||
fi
|
||||
|
||||
if [ ! -f "$caseout" ]; then
|
||||
die "test case expected output file does not exist: $caseout"
|
||||
fi
|
||||
|
||||
output="$("$casebin" "$@" 2>&1)"
|
||||
r=$?
|
||||
if [ $r -eq 139 ]; then
|
||||
die "xopt test case failed with SEGFAULT ($r)"
|
||||
fi
|
||||
|
||||
diff="$(diff -U0 -d -t "$caseout" - <<< "$output" 2>&1)"
|
||||
if [ ! -z "$diff" ]; then
|
||||
die "xopt test case didn't match expected output: '$caseout'\n$diff"
|
||||
fi
|
584
deps/xopt/xopt.c
vendored
584
deps/xopt/xopt.c
vendored
@ -1,584 +0,0 @@
|
||||
/**
|
||||
* XOpt - command line parsing library
|
||||
*
|
||||
* Copyright (c) 2015-2019 Josh Junon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef XOPT_NOSTANDARD
|
||||
# define HAVE_STDARG_H 1
|
||||
# define HAVE_STDLIB_H 1
|
||||
# define HAVE_ASPRINTF_H 1
|
||||
# define vasprintf rpl_vasprintf
|
||||
# ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./xopt.h"
|
||||
#include "./snprintf.c"
|
||||
|
||||
#define EXTRAS_INIT 10
|
||||
#define ERRBUF_SIZE 1024 * 4
|
||||
|
||||
static char errbuf[ERRBUF_SIZE];
|
||||
|
||||
struct xoptContext {
|
||||
const xoptOption *options;
|
||||
long flags;
|
||||
const char *name;
|
||||
bool doubledash;
|
||||
size_t options_count;
|
||||
bool *required;
|
||||
jmp_buf *jmp;
|
||||
};
|
||||
|
||||
static void _xopt_set_err(xoptContext *ctx, const char **err, const char *const fmt, ...);
|
||||
static int _xopt_parse_arg(xoptContext *ctx, int argc, const char **argv,
|
||||
int *argi, void *data, const char **err);
|
||||
static void _xopt_assert_increment(xoptContext *ctx, const char ***extras, int extrasCount,
|
||||
size_t *extrasCapac, const char **err);
|
||||
static int _xopt_get_size(const char *arg);
|
||||
static int _xopt_get_arg(const xoptContext *ctx, const char *arg, size_t len,
|
||||
int size, const xoptOption **option, size_t *option_index);
|
||||
static void _xopt_set(xoptContext *ctx, void *data, const xoptOption *option, const char *val,
|
||||
bool longArg, const char **err);
|
||||
static void _xopt_default_callback(const char *value, void *data,
|
||||
const xoptOption *option, bool longArg, const char **err);
|
||||
|
||||
xoptContext* xopt_context(const char *name, const xoptOption *options, long flags,
|
||||
const char **err) {
|
||||
xoptContext* ctx;
|
||||
*err = 0;
|
||||
|
||||
/* malloc context and check */
|
||||
ctx = malloc(sizeof(xoptContext));
|
||||
if (!ctx) {
|
||||
ctx = 0;
|
||||
_xopt_set_err(NULL, err, "could not allocate context");
|
||||
} else {
|
||||
const xoptOption *cur;
|
||||
|
||||
ctx->options = options;
|
||||
ctx->flags = flags;
|
||||
ctx->name = name;
|
||||
ctx->doubledash = false;
|
||||
ctx->required = NULL;
|
||||
ctx->jmp = NULL;
|
||||
|
||||
ctx->options_count = 0;
|
||||
cur = options;
|
||||
for (; cur->longArg || cur->shortArg; cur++) ++ctx->options_count;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static int _xopt_parse_impl(xoptContext *ctx, int argc, const char **argv, void *data,
|
||||
const char ***inextras, const char **err, int *extrasCount, size_t *extrasCapac,
|
||||
const char ***extras, int *argi) {
|
||||
int parseResult;
|
||||
size_t i;
|
||||
|
||||
*err = 0;
|
||||
*argi = 0;
|
||||
*extrasCount = 0;
|
||||
*extrasCapac = EXTRAS_INIT;
|
||||
*extras = malloc(sizeof(**extras) * EXTRAS_INIT);
|
||||
|
||||
jmp_buf jmp;
|
||||
ctx->jmp = &jmp;
|
||||
if (setjmp(jmp)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* check if extras malloc'd okay */
|
||||
if (!*extras) {
|
||||
_xopt_set_err(ctx, err, "could not allocate extras array");
|
||||
}
|
||||
|
||||
/* increment argument counter if we aren't
|
||||
instructed to check argv[0] */
|
||||
if (!(ctx->flags & XOPT_CTX_KEEPFIRST)) {
|
||||
++(*argi);
|
||||
}
|
||||
|
||||
/* set up required parameters list */
|
||||
ctx->required = malloc(sizeof(*ctx->required) * ctx->options_count);
|
||||
for (i = 0; i < ctx->options_count; i++) {
|
||||
ctx->required[i] = (ctx->options[i].options & XOPT_REQUIRED) > 0;
|
||||
}
|
||||
|
||||
/* iterate over passed command line arguments */
|
||||
for (; *argi < argc; (*argi)++) {
|
||||
/* parse, breaking if there was a failure
|
||||
parseResult is 0 if option, 1 if extra, or 2 if double-dash was encountered */
|
||||
parseResult = _xopt_parse_arg(ctx, argc, argv, argi, data, err);
|
||||
|
||||
/* is the argument an extra? */
|
||||
switch (parseResult) {
|
||||
case 0: /* option */
|
||||
/* make sure we're super-posix'd if specified to be
|
||||
(check that no extras have been specified when an option is parsed,
|
||||
enforcing options to be specific before [extra] arguments */
|
||||
if ((ctx->flags & XOPT_CTX_POSIXMEHARDER) && *extrasCount) {
|
||||
_xopt_set_err(ctx, err, "options cannot be specified after arguments: %s", argv[*argi]);
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
case 1: /* extra */
|
||||
/* make sure we have enough room, or realloc if we don't -
|
||||
check that it succeeded */
|
||||
_xopt_assert_increment(ctx, extras, *extrasCount, extrasCapac, err);
|
||||
|
||||
/* add extra to list */
|
||||
(*extras)[(*extrasCount)++] = argv[*argi];
|
||||
break;
|
||||
case 2: /* "--" was encountered */
|
||||
/* nothing to do here - "--" was already handled for us */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (!*err) {
|
||||
for (i = 0; i < ctx->options_count; i++) {
|
||||
if (ctx->required[i]) {
|
||||
const xoptOption *opt = &ctx->options[i];
|
||||
if (opt->longArg) {
|
||||
_xopt_set_err(ctx, err, "missing required option: --%s", opt->longArg);
|
||||
} else {
|
||||
_xopt_set_err(ctx, err, "missing required option: -%c", opt->shortArg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(ctx->required);
|
||||
|
||||
if (!*err) {
|
||||
/* append null terminator to extras */
|
||||
_xopt_assert_increment(ctx, extras, *extrasCount, extrasCapac, err);
|
||||
if (!*err) {
|
||||
(*extras)[*extrasCount] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (*err) {
|
||||
free(*extras);
|
||||
*inextras = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*inextras = *extras;
|
||||
return *extrasCount;
|
||||
}
|
||||
|
||||
int xopt_parse(xoptContext *ctx, int argc, const char **argv, void *data,
|
||||
const char ***inextras, const char **err) {
|
||||
/* avoid longjmp clobbering */
|
||||
int extrasCount;
|
||||
size_t extrasCapac;
|
||||
const char **extras;
|
||||
int argi;
|
||||
return _xopt_parse_impl(ctx, argc, argv, data, inextras, err, &extrasCount, &extrasCapac, &extras, &argi);
|
||||
}
|
||||
|
||||
void xopt_autohelp(xoptContext *ctx, FILE *stream, const xoptAutohelpOptions *options,
|
||||
const char **err) {
|
||||
const xoptOption *o;
|
||||
size_t i, width = 0, twidth;
|
||||
const char *nl = "";
|
||||
size_t spacer = options ? options->spacer : 2;
|
||||
|
||||
*err = 0;
|
||||
|
||||
/* make sure that if we ever write a call to _set_err() in the future here,
|
||||
that we won't accidentally cause segfaults - we have an assertion in place
|
||||
for ctx->jmp != NULL, so we make sure we'd trigger that assertion */
|
||||
ctx->jmp = NULL;
|
||||
|
||||
if (options && options->usage) {
|
||||
fprintf(stream, "%susage: %s %s\n", nl, ctx->name, options->usage);
|
||||
nl = "\n";
|
||||
}
|
||||
|
||||
if (options && options->prefix) {
|
||||
fprintf(stream, "%s%s\n\n", nl, options->prefix);
|
||||
nl = "\n";
|
||||
}
|
||||
|
||||
/* find max width */
|
||||
for (i = 0; ctx->options[i].longArg || ctx->options[i].shortArg; i++) {
|
||||
o = &ctx->options[i];
|
||||
twidth = 0;
|
||||
if (o->longArg) {
|
||||
twidth += 2 + strlen(o->longArg);
|
||||
if (o->argDescrip) {
|
||||
twidth += 1 + strlen(o->argDescrip);
|
||||
}
|
||||
}
|
||||
if (ctx->options[i].shortArg) {
|
||||
twidth += 2;
|
||||
}
|
||||
if (ctx->options[i].shortArg && ctx->options[i].longArg) {
|
||||
twidth += 2; /* `, ` */
|
||||
}
|
||||
|
||||
width = width > twidth ? width : twidth;
|
||||
}
|
||||
|
||||
/* print */
|
||||
for (i = 0; ctx->options[i].longArg || ctx->options[i].shortArg; i++) {
|
||||
o = &ctx->options[i];
|
||||
twidth = 0;
|
||||
if (o->shortArg) {
|
||||
fprintf(stream, "-%c", o->shortArg);
|
||||
twidth += 2;
|
||||
}
|
||||
|
||||
if (o->shortArg && o->longArg) {
|
||||
fprintf(stream, ", ");
|
||||
twidth += 2;
|
||||
}
|
||||
|
||||
if (o->longArg) {
|
||||
fprintf(stream, "--%s", o->longArg);
|
||||
twidth += 2 + strlen(o->longArg);
|
||||
if (o->argDescrip) {
|
||||
fprintf(stream, "=%s", o->argDescrip);
|
||||
twidth += 1 + strlen(o->argDescrip);
|
||||
}
|
||||
}
|
||||
|
||||
if (o->descrip) {
|
||||
for (; twidth < (width + spacer); twidth++) {
|
||||
fprintf(stream, " ");
|
||||
}
|
||||
|
||||
if (o->options & XOPT_REQUIRED) {
|
||||
fprintf(stream, "(Required) %s\n", o->descrip);
|
||||
} else {
|
||||
fprintf(stream, "%s\n", o->descrip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options && options->suffix) {
|
||||
fprintf(stream, "%s%s\n", nl, options->suffix);
|
||||
}
|
||||
}
|
||||
|
||||
static void _xopt_set_err(xoptContext *ctx, const char **err, const char *const fmt, ...) {
|
||||
va_list list;
|
||||
va_start(list, fmt);
|
||||
rpl_vsnprintf(&errbuf[0], ERRBUF_SIZE, fmt, list);
|
||||
va_end(list);
|
||||
*err = &errbuf[0];
|
||||
|
||||
if (ctx != NULL) {
|
||||
assert(ctx->jmp != NULL);
|
||||
longjmp(*ctx->jmp, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int _xopt_parse_arg(xoptContext *ctx, int argc, const char **argv,
|
||||
int *argi, void *data, const char **err) {
|
||||
int size;
|
||||
size_t length;
|
||||
bool isExtra = false;
|
||||
const xoptOption *option = NULL;
|
||||
size_t option_index = 0;
|
||||
const char* arg = argv[*argi];
|
||||
|
||||
/* are we in doubledash mode? */
|
||||
if (ctx->doubledash) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* get argument 'size' (long/short/extra) */
|
||||
size = _xopt_get_size(arg);
|
||||
|
||||
/* adjust to parse from beginning of actual content */
|
||||
arg += size;
|
||||
length = strlen(arg);
|
||||
|
||||
if (size == 1 && length == 0) {
|
||||
/* it's just a singular dash - treat it as an extra arg */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (size == 2 && length == 0) {
|
||||
/* double-dash - everything after this is an extra */
|
||||
ctx->doubledash = 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
int argRequirement;
|
||||
char *valStart;
|
||||
case 1: /* short */
|
||||
/* parse all */
|
||||
while (length--) {
|
||||
/* get argument or error if not found and strict mode enabled. */
|
||||
argRequirement = _xopt_get_arg(ctx, arg++, 1, size, &option, &option_index);
|
||||
if (!option) {
|
||||
if (ctx->flags & XOPT_CTX_STRICT) {
|
||||
_xopt_set_err(ctx, err, "invalid option: -%c", arg[-1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (argRequirement > 0 && length > 0 && !(ctx->flags & XOPT_CTX_SLOPPYSHORTS)) {
|
||||
_xopt_set_err(ctx, err, "short option parameters must be separated, not condensed: %s", argv[*argi]);
|
||||
}
|
||||
|
||||
switch (argRequirement) {
|
||||
case 0: /* flag; doesn't take an argument */
|
||||
if (length > 0 && (ctx->flags & XOPT_CTX_NOCONDENSE)) {
|
||||
_xopt_set_err(ctx, err, "short options cannot be combined: %s", argv[*argi]);
|
||||
}
|
||||
|
||||
_xopt_set(ctx, data, option, 0, false, err);
|
||||
break;
|
||||
case 1: /* argument is optional */
|
||||
/* is there another argument, and is it a non-option? */
|
||||
if (*argi + 1 < argc && _xopt_get_size(argv[*argi + 1]) == 0) {
|
||||
_xopt_set(ctx, data, option, argv[++*argi], false, err);
|
||||
} else {
|
||||
_xopt_set(ctx, data, option, 0, false, err);
|
||||
}
|
||||
break;
|
||||
case 2: /* requires an argument */
|
||||
/* is it the last in a set of condensed options? */
|
||||
if (length == 0) {
|
||||
/* is there another argument? */
|
||||
if (*argi + 1 < argc) {
|
||||
/* is the next argument actually an option?
|
||||
this indicates no value was passed */
|
||||
if (_xopt_get_size(argv[*argi + 1])) {
|
||||
_xopt_set_err(ctx, err, "missing option value: -%c",
|
||||
option->shortArg);
|
||||
} else {
|
||||
_xopt_set(ctx, data, option, argv[++*argi], false, err);
|
||||
}
|
||||
} else {
|
||||
_xopt_set_err(ctx, err, "missing option value: -%c",
|
||||
option->shortArg);
|
||||
}
|
||||
} else {
|
||||
_xopt_set(ctx, data, option, arg, false, err);
|
||||
length = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2: /* long */
|
||||
/* find first equals sign */
|
||||
valStart = strchr(arg, '=');
|
||||
|
||||
/* is there a value? */
|
||||
if (valStart) {
|
||||
/* we also increase valStart here in order to lop off
|
||||
the equals sign */
|
||||
length = valStart++ - arg;
|
||||
|
||||
/* but not really, if it's null */
|
||||
if (!*valStart) {
|
||||
valStart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* get the option */
|
||||
argRequirement = _xopt_get_arg(ctx, arg, length, size, &option, &option_index);
|
||||
if (!option) {
|
||||
_xopt_set_err(ctx, err, "invalid option: --%.*s", length, arg);
|
||||
} else {
|
||||
switch (argRequirement) {
|
||||
case 0: /* flag; doesn't take an argument */
|
||||
if (valStart) {
|
||||
_xopt_set_err(ctx, err, "option doesn't take a value: --%s", arg);
|
||||
}
|
||||
|
||||
_xopt_set(ctx, data, option, valStart, true, err);
|
||||
break;
|
||||
case 2: /* requires an argument */
|
||||
if (!valStart) {
|
||||
_xopt_set_err(ctx, err, "missing option value: --%s", arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
_xopt_set(ctx, data, option, valStart, true, err);
|
||||
}
|
||||
|
||||
break;
|
||||
case 0: /* extra */
|
||||
isExtra = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (option) {
|
||||
/* indicate that we've seen this option and thus is no longer required */
|
||||
ctx->required[option_index] = false;
|
||||
}
|
||||
|
||||
return isExtra ? 1 : 0;
|
||||
}
|
||||
|
||||
static void _xopt_assert_increment(xoptContext *ctx, const char ***extras, int extrasCount,
|
||||
size_t *extrasCapac, const char **err) {
|
||||
/* have we hit the list size limit? */
|
||||
if ((size_t) extrasCount == *extrasCapac) {
|
||||
/* increase capcity, realloc, and check for success */
|
||||
*extrasCapac += EXTRAS_INIT;
|
||||
*extras = realloc(*extras, sizeof(**extras) * *extrasCapac);
|
||||
if (!*extras) {
|
||||
_xopt_set_err(ctx, err, "could not realloc arguments array");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int _xopt_get_size(const char *arg) {
|
||||
int size;
|
||||
for (size = 0; size < 2; size++) {
|
||||
if (arg[size] != '-') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static int _xopt_get_arg(const xoptContext *ctx, const char *arg, size_t len,
|
||||
int size, const xoptOption **option, size_t *option_index) {
|
||||
size_t i;
|
||||
|
||||
*option = 0;
|
||||
|
||||
/* find the argument */
|
||||
for (i = 0; i < ctx->options_count; i++) {
|
||||
const xoptOption *opt = &ctx->options[i];
|
||||
|
||||
if ((size == 1 && opt->shortArg == arg[0])
|
||||
|| (opt->longArg && strlen(opt->longArg) == len && !strncmp(opt->longArg, arg, len))) {
|
||||
*option_index = i;
|
||||
*option = opt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* determine the optionality of a value */
|
||||
if (!*option || (*option)->options & XOPT_TYPE_BOOL) {
|
||||
return 0;
|
||||
} else if ((*option)->options & XOPT_PARAM_OPTIONAL) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void _xopt_set(xoptContext *ctx, void *data, const xoptOption *option, const char *val,
|
||||
bool longArg, const char **err) {
|
||||
/* determine callback */
|
||||
xoptCallback callback = option->callback ? option->callback : &_xopt_default_callback;
|
||||
|
||||
/* dispatch callback */
|
||||
callback(val, data, option, longArg, err);
|
||||
|
||||
/* we check err here instead of relying upon longjmp()
|
||||
since we can't call _set_err() with a context */
|
||||
if (*err) {
|
||||
assert(ctx->jmp != NULL);
|
||||
longjmp(*ctx->jmp, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void _xopt_default_callback(const char *value, void *data,
|
||||
const xoptOption *option, bool longArg, const char **err) {
|
||||
void *target;
|
||||
char *parsePtr = 0;
|
||||
|
||||
/* is a value specified? */
|
||||
if ((!value || !strlen(value)) && !(option->options & XOPT_TYPE_BOOL)) {
|
||||
/* we reach this point when they specified an optional, non-boolean
|
||||
option but didn't specify a custom handler (therefore, it's not
|
||||
optional).
|
||||
|
||||
to fix, just remove the optional flag or specify a callback to handle
|
||||
it yourself.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* get location */
|
||||
target = ((char*) data) + option->offset;
|
||||
|
||||
/* switch on the type */
|
||||
switch (option->options & 0x3F) {
|
||||
case XOPT_TYPE_BOOL:
|
||||
/* booleans are special in that they won't have an argument passed
|
||||
into this callback */
|
||||
*((_Bool*) target) = true;
|
||||
break;
|
||||
case XOPT_TYPE_STRING:
|
||||
/* lifetime here works out fine; argv can usually be assumed static-like
|
||||
in nature */
|
||||
*((const char**) target) = value;
|
||||
break;
|
||||
case XOPT_TYPE_INT:
|
||||
*((int*) target) = (int) strtol(value, &parsePtr, 0);
|
||||
break;
|
||||
case XOPT_TYPE_LONG:
|
||||
*((long*) target) = strtol(value, &parsePtr, 0);
|
||||
break;
|
||||
case XOPT_TYPE_FLOAT:
|
||||
*((float*) target) = (float) strtod(value, &parsePtr);
|
||||
break;
|
||||
case XOPT_TYPE_DOUBLE:
|
||||
*((double*) target) = strtod(value, &parsePtr);
|
||||
break;
|
||||
default: /* something wonky, or the implementation specifies two types */
|
||||
fprintf(stderr, "warning: XOpt argument type invalid: %ld\n",
|
||||
option->options & 0x2F);
|
||||
break;
|
||||
}
|
||||
|
||||
/* check that our parsing functions worked */
|
||||
if (parsePtr && *parsePtr) {
|
||||
if (longArg) {
|
||||
_xopt_set_err(NULL, err, "value isn't a valid number: --%s=%s",
|
||||
(void*) option->longArg, value);
|
||||
} else {
|
||||
_xopt_set_err(NULL, err, "value isn't a valid number: -%c %s",
|
||||
option->shortArg, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
225
deps/xopt/xopt.h
vendored
225
deps/xopt/xopt.h
vendored
@ -1,225 +0,0 @@
|
||||
/**
|
||||
* XOpt - command line parsing library
|
||||
*
|
||||
* Copyright (c) 2015 Josh Junon.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef XOPT_H__
|
||||
#define XOPT_H__
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct xoptOption;
|
||||
|
||||
#ifndef offsetof
|
||||
# define offsetof(T, member) (size_t)(&(((T*)0)->member))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Callback type for handling values.
|
||||
* Called when a command line argument has been
|
||||
* processed.
|
||||
*/
|
||||
typedef void (*xoptCallback)(
|
||||
const char *value, /* string cmd line option */
|
||||
void *data, /* custom data structure */
|
||||
const struct xoptOption *option, /* detected option */
|
||||
bool longArg, /* true if the long-arg version
|
||||
was used */
|
||||
const char **err); /* err output */
|
||||
|
||||
enum xoptOptionFlag {
|
||||
XOPT_TYPE_STRING = 0x1, /* const char* type */
|
||||
XOPT_TYPE_INT = 0x2, /* int type */
|
||||
XOPT_TYPE_LONG = 0x4, /* long type */
|
||||
XOPT_TYPE_FLOAT = 0x8, /* float type */
|
||||
XOPT_TYPE_DOUBLE = 0x10, /* double type */
|
||||
XOPT_TYPE_BOOL = 0x20, /* boolean (int) type */
|
||||
|
||||
XOPT_PARAM_OPTIONAL = 0x40, /* whether the argument value is
|
||||
optional */
|
||||
XOPT_REQUIRED = 0x80 /* indicates the flag must be
|
||||
present on the command line */
|
||||
};
|
||||
|
||||
enum xoptContextFlag {
|
||||
XOPT_CTX_KEEPFIRST = 0x1, /* don't ignore argv[0] */
|
||||
XOPT_CTX_POSIXMEHARDER = 0x2, /* options cannot come after
|
||||
extra arguments */
|
||||
XOPT_CTX_NOCONDENSE = 0x4, /* don't allow short args to be
|
||||
condensed (i.e. `ls -laF') */
|
||||
XOPT_CTX_SLOPPYSHORTS = 0x8, /* allow short arg values to be
|
||||
directly after the character */
|
||||
XOPT_CTX_STRICT = 0x10 /* fails on invalid arguments */
|
||||
};
|
||||
|
||||
typedef struct xoptOption {
|
||||
const char *longArg; /* --long-arg-name, or 0 for short
|
||||
arg only */
|
||||
const char shortArg; /* -s hort arg character, or '\0'
|
||||
for long arg only */
|
||||
size_t offset; /* offsetof(type, property) for
|
||||
automatic configuration handler */
|
||||
xoptCallback callback; /* callback for resolved option
|
||||
handling */
|
||||
long options; /* xoptOptionFlag options */
|
||||
const char *argDescrip; /* --argument=argDescrip (autohelp) */
|
||||
const char *descrip; /* argument explanation (autohelp) */
|
||||
} xoptOption;
|
||||
|
||||
/* option list terminator */
|
||||
#define XOPT_NULLOPTION {0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
typedef struct xoptContext xoptContext;
|
||||
|
||||
typedef struct xoptAutohelpOptions {
|
||||
const char *usage; /* usage string, or null */
|
||||
const char *prefix; /* printed before options, or null */
|
||||
const char *suffix; /* printed after options, or null */
|
||||
size_t spacer; /* number of spaces between option and
|
||||
description */
|
||||
} xoptAutohelpOptions;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Creates an XOpt context to be used with
|
||||
* subsequent calls to XOpt functions
|
||||
*/
|
||||
xoptContext*
|
||||
xopt_context(
|
||||
const char *name, /* name of the argument set (usually
|
||||
name of the cli binary file/cmd */
|
||||
const xoptOption *options, /* list of xoptOption objects,
|
||||
terminated with XOPT_NULLOPTION */
|
||||
long flags, /* xoptContextFlag flags */
|
||||
const char **err); /* pointer to a const char* that
|
||||
receives an err should one occur -
|
||||
set to 0 if command completed
|
||||
successfully */
|
||||
|
||||
/**
|
||||
* Parses the command line of a program
|
||||
* and returns the number of non-options
|
||||
* returned to the `extras' pointer (see
|
||||
* below)
|
||||
*/
|
||||
int
|
||||
xopt_parse(
|
||||
xoptContext *ctx, /* previously created XOpt context */
|
||||
int argc, /* argc, from int main() */
|
||||
const char **argv, /* argv, from int main() */
|
||||
void *data, /* a custom data object whos type
|
||||
corresponds to `.offset' values
|
||||
specified in the options list;
|
||||
populated with values interpreted
|
||||
from the command line */
|
||||
const char ***extras, /* receives a list of extra non-option
|
||||
arguments (i.e. files, subcommands,
|
||||
etc.) - length of which is returned
|
||||
by the function call */
|
||||
const char **err); /* pointer to a const char* that
|
||||
receives an err should one occur -
|
||||
set to 0 if command completed
|
||||
successfully */
|
||||
|
||||
/**
|
||||
* Generates and prints a help message
|
||||
* and prints it to a FILE stream.
|
||||
* If `defaults' is supplied, uses
|
||||
* offsets (values) defined by the options
|
||||
* list to show default options
|
||||
*/
|
||||
void
|
||||
xopt_autohelp(
|
||||
xoptContext *ctx, /* previously created XOpt context */
|
||||
FILE *stream, /* a stream to print to - if 0,
|
||||
defaults to `stderr'. */
|
||||
const xoptAutohelpOptions *options, /* configuration options to tailor
|
||||
autohelp output */
|
||||
const char **err); /* pointer to a const char* that
|
||||
receives an err should one occur -
|
||||
set to 0 if command completed
|
||||
successfully */
|
||||
|
||||
/**
|
||||
* Generates a default option parser that's sane for most cases.
|
||||
*
|
||||
* Assumes there's a `help` property that is boolean-checkable that exists on the
|
||||
* config pointer passed to `config_ptr` (i.e. does a lookup of `config_ptr->help`).
|
||||
*
|
||||
* In the event help is invoked, xopt will `goto xopt_help`. It is up to you to define such
|
||||
* a label in order to recover. In this case, extrav will still be allocated and will still need to be
|
||||
* freed.
|
||||
*
|
||||
* To be extra clear, you need to free `extrav_ptr` is if `*err_ptr` is not `NULL`.
|
||||
*
|
||||
* `name` is the name of the binary you'd like to pass to the context (welcome to use `argv[0]` here),
|
||||
* `options` is a reference to the xoptOptions array you've specified,
|
||||
* `config_ptr` is a *pointer* to your configuration instance,
|
||||
* `argc` and `argv` are the int/const char ** passed into main,
|
||||
* `extrac_ptr` and `extrav_ptr` are pointers to an `int`/`const char **`
|
||||
* (so `int*` and `const char ***`, respectively) that receive the parsed extra args
|
||||
* (note that, unless there is an error, `extrav_ptr` is owned by your program and must
|
||||
* be `free()`'d when you're done using it, even if there are zero extra arguments),
|
||||
* and `err_ptr` is a pointer to a `const char *` (so a `const char **`) that receives any error
|
||||
* strings in the event of a problem. These errors are statically allocated so no need to
|
||||
* free them. This variable should be initialized to NULL and checked after calling
|
||||
* `XOPT_SIMPLE_PARSE()`.
|
||||
*
|
||||
* `autohelp_file`, `autohelp_usage`, `autohelp_prefix`, `autohelp_suffix` and `autohelp_spacer` are all
|
||||
* parameters to the `xoptAutohelpOptions` struct (with the exception of `autohelp_file`, which must be a
|
||||
* `FILE*` reference (e.g. `stdout` or `stderr`) which receives the rendered autohelp text). Consult the
|
||||
* `xoptAutohelpOptions` struct above for documentation as to valid values for each of these properties.
|
||||
*/
|
||||
#define XOPT_SIMPLE_PARSE(name, flags, options, config_ptr, argc, argv, extrac_ptr, extrav_ptr, err_ptr, autohelp_file, autohelp_usage, autohelp_prefix, autohelp_suffix, autohelp_spacer) do { \
|
||||
xoptContext *_xopt_ctx; \
|
||||
*(err_ptr) = NULL; \
|
||||
_xopt_ctx = xopt_context((name), (options), ((flags) ^ XOPT_CTX_POSIXMEHARDER ^ XOPT_CTX_STRICT), (err_ptr)); \
|
||||
if (*(err_ptr)) break; \
|
||||
*extrac_ptr = xopt_parse(_xopt_ctx, (argc), (argv), (config_ptr), (extrav_ptr), (err_ptr)); \
|
||||
if ((config_ptr)->help) { \
|
||||
xoptAutohelpOptions __xopt_autohelp_opts; \
|
||||
__xopt_autohelp_opts.usage = (autohelp_usage); \
|
||||
__xopt_autohelp_opts.prefix = (autohelp_prefix); \
|
||||
__xopt_autohelp_opts.suffix = (autohelp_suffix); \
|
||||
__xopt_autohelp_opts.spacer = (autohelp_spacer); \
|
||||
xopt_autohelp(_xopt_ctx, (autohelp_file), &__xopt_autohelp_opts, (err_ptr)); \
|
||||
if (*(err_ptr)) goto __xopt_end_free_extrav; \
|
||||
goto xopt_help; \
|
||||
} \
|
||||
if (*(err_ptr)) goto __xopt_end_free_ctx; \
|
||||
__xopt_end_free_ctx: \
|
||||
free(_xopt_ctx); \
|
||||
break; \
|
||||
__xopt_end_free_extrav: \
|
||||
free(*(extrav_ptr)); \
|
||||
free(_xopt_ctx); \
|
||||
break; \
|
||||
} while (false)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
@ -2,7 +2,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.unprompted.tildefriends"
|
||||
android:versionCode="16"
|
||||
android:versionName="0.0.16-wip">
|
||||
android:versionName="0.0.16">
|
||||
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="34"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<application
|
||||
|
@ -80,11 +80,13 @@ static JSValue _httpd_response_end(JSContext* context, JSValueConst this_val, in
|
||||
{
|
||||
tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
|
||||
size_t length = 0;
|
||||
const char* cstring = NULL;
|
||||
const void* data = NULL;
|
||||
JSValue buffer = JS_UNDEFINED;
|
||||
if (JS_IsString(argv[0]))
|
||||
{
|
||||
data = JS_ToCStringLen(context, &length, argv[0]);
|
||||
cstring = JS_ToCStringLen(context, &length, argv[0]);
|
||||
data = cstring;
|
||||
}
|
||||
else if ((data = tf_util_try_get_array_buffer(context, &length, argv[0])) != 0)
|
||||
{
|
||||
@ -117,6 +119,10 @@ static JSValue _httpd_response_end(JSContext* context, JSValueConst this_val, in
|
||||
JS_FreeCString(context, headers[i]);
|
||||
}
|
||||
JS_FreeValue(context, buffer);
|
||||
if (cstring)
|
||||
{
|
||||
JS_FreeCString(context, cstring);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
|
485
src/main.c
485
src/main.c
@ -11,8 +11,8 @@
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "sqlite3.h"
|
||||
#include "xopt.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -41,47 +41,13 @@ struct backtrace_state* g_backtrace_state;
|
||||
|
||||
const char* k_db_path_default = "db.sqlite";
|
||||
|
||||
#define XOPT_PARSE( \
|
||||
name, flags, options, config_ptr, argc, argv, extrac_ptr, extrav_ptr, err_ptr, autohelp_file, autohelp_usage, autohelp_prefix, autohelp_suffix, autohelp_spacer) \
|
||||
do \
|
||||
{ \
|
||||
xoptContext* _xopt_ctx; \
|
||||
*(err_ptr) = NULL; \
|
||||
_xopt_ctx = xopt_context((name), (options), ((flags) ^ XOPT_CTX_POSIXMEHARDER), (err_ptr)); \
|
||||
if (*(err_ptr)) \
|
||||
break; \
|
||||
*extrac_ptr = xopt_parse(_xopt_ctx, (argc), (argv), (config_ptr), (extrav_ptr), (err_ptr)); \
|
||||
if ((config_ptr)->help) \
|
||||
{ \
|
||||
xoptAutohelpOptions __xopt_autohelp_opts; \
|
||||
__xopt_autohelp_opts.usage = (autohelp_usage); \
|
||||
__xopt_autohelp_opts.prefix = (autohelp_prefix); \
|
||||
__xopt_autohelp_opts.suffix = (autohelp_suffix); \
|
||||
__xopt_autohelp_opts.spacer = (autohelp_spacer); \
|
||||
xopt_autohelp(_xopt_ctx, (autohelp_file), &__xopt_autohelp_opts, (err_ptr)); \
|
||||
if (*(err_ptr)) \
|
||||
goto __xopt_end_free_extrav; \
|
||||
free(_xopt_ctx); \
|
||||
goto xopt_help; \
|
||||
} \
|
||||
if (*(err_ptr)) \
|
||||
goto __xopt_end_free_ctx; \
|
||||
__xopt_end_free_ctx: \
|
||||
free(_xopt_ctx); \
|
||||
break; \
|
||||
__xopt_end_free_extrav: \
|
||||
free(*(extrav_ptr)); \
|
||||
free(_xopt_ctx); \
|
||||
break; \
|
||||
} while (false)
|
||||
|
||||
#if !TARGET_OS_IPHONE && !defined(__ANDROID__)
|
||||
static int _tf_command_test(const char* file, int argc, char* argv[]);
|
||||
static int _tf_command_import(const char* file, int argc, char* argv[]);
|
||||
static int _tf_command_export(const char* file, int argc, char* argv[]);
|
||||
static int _tf_command_run(const char* file, int argc, char* argv[]);
|
||||
static int _tf_command_sandbox(const char* file, int argc, char* argv[]);
|
||||
static int _tf_command_usage(const char* file, int argc, char* argv[]);
|
||||
static int _tf_command_usage(const char* file);
|
||||
|
||||
typedef struct _command_t
|
||||
{
|
||||
@ -101,140 +67,178 @@ const command_t k_commands[] = {
|
||||
static int _tf_command_test(const char* file, int argc, char* argv[])
|
||||
{
|
||||
#if !defined(__ANDROID__)
|
||||
typedef struct args_t
|
||||
{
|
||||
const char* tests;
|
||||
bool help;
|
||||
} args_t;
|
||||
|
||||
xoptOption options[] = {
|
||||
{ "tests", 't', offsetof(args_t, tests), NULL, XOPT_TYPE_STRING, NULL, "Comma-separated list of test names to run." },
|
||||
{ "help", 'h', offsetof(args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
|
||||
XOPT_NULLOPTION,
|
||||
};
|
||||
|
||||
args_t args = { 0 };
|
||||
const char** extras = NULL;
|
||||
int extra_count = 0;
|
||||
const char* err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_STRICT, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr, "test [options]", "options:", NULL, 15);
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
return 2;
|
||||
}
|
||||
|
||||
tf_test_options_t test_options = {
|
||||
.exe_path = file,
|
||||
.tests = args.tests,
|
||||
};
|
||||
bool show_usage = false;
|
||||
|
||||
while (!show_usage)
|
||||
{
|
||||
static const struct option k_options[] = {
|
||||
{ "tests", required_argument, NULL, 't' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ 0 },
|
||||
};
|
||||
int c = getopt_long(argc, argv, "t:h", k_options, NULL);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
show_usage = true;
|
||||
break;
|
||||
case 't':
|
||||
test_options.tests = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = optind; i < argc; i++)
|
||||
{
|
||||
tf_printf("Unexpected argument: %s\n", argv[i]);
|
||||
show_usage = true;
|
||||
}
|
||||
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\n%s test [options]\n\n", file);
|
||||
tf_printf("options\n");
|
||||
tf_printf(" -t, --tests tests Comma-separated list of tests to run. (default: all)\n");
|
||||
tf_printf(" -h, --help Show this usage information.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
tf_tests(&test_options);
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 0;
|
||||
xopt_help:
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
#else
|
||||
return EXIT_FAILURE;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _tf_command_import(const char* file, int argc, char* argv[])
|
||||
{
|
||||
typedef struct args_t
|
||||
{
|
||||
const char* user;
|
||||
const char* db_path;
|
||||
bool help;
|
||||
} args_t;
|
||||
const char* user = "import";
|
||||
const char* db_path = k_db_path_default;
|
||||
bool show_usage = false;
|
||||
|
||||
xoptOption options[] = {
|
||||
{ "user", 'u', offsetof(args_t, user), NULL, XOPT_TYPE_STRING, NULL, "User into whose account apps will be imported (default: \"import\")." },
|
||||
{ "db-path", 'd', offsetof(args_t, db_path), NULL, XOPT_TYPE_STRING, NULL, "Sqlite database path (default: db.sqlite)." },
|
||||
{ "help", 'h', offsetof(args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
|
||||
XOPT_NULLOPTION,
|
||||
};
|
||||
|
||||
args_t args = { .user = "import", .db_path = k_db_path_default };
|
||||
const char** extras = NULL;
|
||||
int extra_count = 0;
|
||||
const char* err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER | XOPT_CTX_STRICT, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr,
|
||||
"import [options] [paths] ...", "options:", NULL, 15);
|
||||
if (err)
|
||||
while (!show_usage)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
return 2;
|
||||
static const struct option k_options[] = {
|
||||
{ "user", required_argument, NULL, 'u' },
|
||||
{ "db-path", required_argument, NULL, 'd' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ 0 },
|
||||
};
|
||||
int c = getopt_long(argc, argv, "u:d:h", k_options, NULL);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
show_usage = true;
|
||||
break;
|
||||
case 'u':
|
||||
user = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
db_path = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, args.db_path);
|
||||
if (extra_count)
|
||||
if (show_usage)
|
||||
{
|
||||
for (int i = 0; i < extra_count; i++)
|
||||
tf_printf("\n%s import [options] [paths...]\n\n", file);
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -u, --user user User into whose account apps will be imported (default: \"import\").\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default);
|
||||
tf_printf(" -h, --help Show this usage information.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path);
|
||||
if (optind < argc)
|
||||
{
|
||||
for (int i = optind; i < argc; i++)
|
||||
{
|
||||
tf_printf("Importing %s...\n", extras[i]);
|
||||
tf_ssb_import(ssb, args.user, extras[i]);
|
||||
tf_printf("Importing %s...\n", argv[i]);
|
||||
tf_ssb_import(ssb, user, argv[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("Importing %s...\n", "apps");
|
||||
tf_ssb_import(ssb, args.user, "apps");
|
||||
tf_ssb_import(ssb, user, "apps");
|
||||
}
|
||||
tf_ssb_destroy(ssb);
|
||||
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 0;
|
||||
|
||||
xopt_help:
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 1;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int _tf_command_export(const char* file, int argc, char* argv[])
|
||||
{
|
||||
typedef struct args_t
|
||||
{
|
||||
const char* user;
|
||||
const char* db_path;
|
||||
bool help;
|
||||
} args_t;
|
||||
const char* user = "core";
|
||||
const char* db_path = k_db_path_default;
|
||||
bool show_usage = false;
|
||||
|
||||
xoptOption options[] = {
|
||||
{ "db-path", 'd', offsetof(args_t, db_path), NULL, XOPT_TYPE_STRING, NULL, "Sqlite database path (default: db.sqlite)." },
|
||||
{ "user", 'u', offsetof(args_t, user), NULL, XOPT_TYPE_STRING, NULL, "User into whose apps will be exported (default: \"core\")." },
|
||||
{ "help", 'h', offsetof(args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
|
||||
XOPT_NULLOPTION,
|
||||
};
|
||||
|
||||
args_t args = { .user = "core", .db_path = k_db_path_default };
|
||||
const char** extras = NULL;
|
||||
int extra_count = 0;
|
||||
const char* err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER | XOPT_CTX_STRICT, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr,
|
||||
"export [options] [paths] ...", "options:", NULL, 15);
|
||||
if (err)
|
||||
while (!show_usage)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
return 2;
|
||||
}
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, args.db_path);
|
||||
if (extra_count)
|
||||
{
|
||||
for (int i = 0; i < extra_count; i++)
|
||||
static const struct option k_options[] = {
|
||||
{ "user", required_argument, NULL, 'u' },
|
||||
{ "db-path", required_argument, NULL, 'd' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ 0 },
|
||||
};
|
||||
int c = getopt_long(argc, argv, "u:d:h", k_options, NULL);
|
||||
if (c == -1)
|
||||
{
|
||||
tf_printf("Exporting %s...\n", extras[i]);
|
||||
tf_ssb_export(ssb, extras[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
show_usage = true;
|
||||
break;
|
||||
case 'u':
|
||||
user = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
db_path = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\n%s export [options] [paths...]\n\n", file);
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -u, --user user User from whose account apps will be exported (default: \"core\").\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default);
|
||||
tf_printf(" -h, --help Show this usage information.\n");
|
||||
tf_printf("\n");
|
||||
tf_printf("paths Paths of apps to export (example: /~core/ssb /~user/app).\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path);
|
||||
if (optind < argc)
|
||||
{
|
||||
for (int i = optind; i < argc; i++)
|
||||
{
|
||||
tf_printf("Exporting %s...\n", argv[i]);
|
||||
tf_ssb_export(ssb, argv[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -253,25 +257,13 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
|
||||
for (int i = 0; i < (int)_countof(k_export); i++)
|
||||
{
|
||||
char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer), "/~%s/%s", args.user, k_export[i]);
|
||||
snprintf(buffer, sizeof(buffer), "/~%s/%s", user, k_export[i]);
|
||||
tf_printf("Exporting %s...\n", buffer);
|
||||
tf_ssb_export(ssb, buffer);
|
||||
}
|
||||
}
|
||||
tf_ssb_destroy(ssb);
|
||||
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 0;
|
||||
|
||||
xopt_help:
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 1;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -412,21 +404,6 @@ static void _shed_privileges()
|
||||
|
||||
static int _tf_command_run(const char* file, int argc, char* argv[])
|
||||
{
|
||||
xoptOption options[] = {
|
||||
{ "script", 's', offsetof(tf_run_args_t, script), NULL, XOPT_TYPE_STRING, NULL, "Script to run (default: core/core.js)." },
|
||||
{ "ssb-port", 'b', offsetof(tf_run_args_t, ssb_port), NULL, XOPT_TYPE_INT, NULL, "Port on which to run SSB (default: 8008)." },
|
||||
{ "http-port", 'p', offsetof(tf_run_args_t, http_port), NULL, XOPT_TYPE_INT, NULL, "Port on which to run Tilde Friends web server (default: 12345)." },
|
||||
{ "https-port", 'q', offsetof(tf_run_args_t, https_port), NULL, XOPT_TYPE_INT, NULL, "Port on which to run secure Tilde Friends web server (default: 12346)." },
|
||||
{ "db-path", 'd', offsetof(tf_run_args_t, db_path), NULL, XOPT_TYPE_STRING, NULL, "Sqlite database path (default: db.sqlite)." },
|
||||
{ "count", 'n', offsetof(tf_run_args_t, count), NULL, XOPT_TYPE_INT, NULL, "Number of instances to run." },
|
||||
{ "args", 'a', offsetof(tf_run_args_t, args), NULL, XOPT_TYPE_STRING, NULL, "Arguments of the form key=value,foo=bar,verbose=true." },
|
||||
{ "one-proc", 'o', offsetof(tf_run_args_t, one_proc), NULL, XOPT_TYPE_BOOL, NULL, "Run everything in one process (unsafely!)." },
|
||||
{ "zip", 'z', offsetof(tf_run_args_t, zip), NULL, XOPT_TYPE_STRING, NULL, "Zip archive from which to load files." },
|
||||
{ "verbose", 'v', offsetof(tf_run_args_t, verbose), NULL, XOPT_TYPE_BOOL, NULL, "Log raw messages." },
|
||||
{ "help", 'h', offsetof(tf_run_args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
|
||||
XOPT_NULLOPTION,
|
||||
};
|
||||
|
||||
tf_run_args_t args = {
|
||||
.count = 1,
|
||||
.script = "core/core.js",
|
||||
@ -435,16 +412,85 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
||||
.ssb_port = 8008,
|
||||
.db_path = k_db_path_default,
|
||||
};
|
||||
const char** extras = NULL;
|
||||
int extra_count = 0;
|
||||
const char* err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER | XOPT_CTX_STRICT, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr,
|
||||
"run [options] [paths] ...", "options:", NULL, 15);
|
||||
bool show_usage = false;
|
||||
|
||||
if (err)
|
||||
while (!show_usage)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
return 2;
|
||||
static const struct option k_options[] = {
|
||||
{ "script", required_argument, NULL, 's' },
|
||||
{ "ssb-port", required_argument, NULL, 'b' },
|
||||
{ "http-port", required_argument, NULL, 'p' },
|
||||
{ "https-port", required_argument, NULL, 'q' },
|
||||
{ "db-path", required_argument, NULL, 'd' },
|
||||
{ "count", required_argument, NULL, 'n' },
|
||||
{ "args", required_argument, NULL, 'a' },
|
||||
{ "one-proc", no_argument, NULL, 'o' },
|
||||
{ "zip", required_argument, NULL, 'z' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
};
|
||||
int c = getopt_long(argc, argv, "s:b:p:q:d:n:a:oz:vh", k_options, NULL);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
show_usage = true;
|
||||
break;
|
||||
case 's':
|
||||
args.script = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
args.ssb_port = atoi(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
args.http_port = atoi(optarg);
|
||||
break;
|
||||
case 'q':
|
||||
args.https_port = atoi(optarg);
|
||||
break;
|
||||
case 'd':
|
||||
args.db_path = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
args.count = atoi(optarg);
|
||||
break;
|
||||
case 'a':
|
||||
args.args = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
args.one_proc = true;
|
||||
break;
|
||||
case 'z':
|
||||
args.zip = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
args.verbose = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\n%s run [options]\n\n", file);
|
||||
tf_printf("options\n");
|
||||
tf_printf(" -s, --script script Script to run (default: core/core.js).\n");
|
||||
tf_printf(" -b, --ssb-port port Port on which to run SSB (default: 8008, 0 disables).\n");
|
||||
tf_printf(" -p, --http-port port Port on which to run Tilde Friends web server (default: 12345).\n");
|
||||
tf_printf(" -q, --https-port port Port on which to run secure Tilde Friends web server (default: 12346).\n");
|
||||
tf_printf(" -d, --db-path path SQLite database path (default: %s).\n", k_db_path_default);
|
||||
tf_printf(" -n, --count count Number of instances to run.\n");
|
||||
tf_printf(" -a, --args args Arguments of the format key=value,foo=bar,verbose=true.\n");
|
||||
tf_printf(" -o, --one-proc Run everything in one process (unsafely!).\n");
|
||||
tf_printf(" -z, --zip path Zip archive from which to load files.\n");
|
||||
tf_printf(" -v, --verbose Log raw messages.\n");
|
||||
tf_printf(" -h, --help Show this usage information.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
@ -479,44 +525,40 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
||||
tf_free(data);
|
||||
tf_free(threads);
|
||||
}
|
||||
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return result;
|
||||
|
||||
xopt_help:
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _tf_command_sandbox(const char* file, int argc, char* argv[])
|
||||
{
|
||||
typedef struct args_t
|
||||
{
|
||||
const char* script;
|
||||
bool help;
|
||||
} args_t;
|
||||
bool show_usage = false;
|
||||
|
||||
xoptOption options[] = {
|
||||
{ "help", 'h', offsetof(args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
|
||||
XOPT_NULLOPTION,
|
||||
};
|
||||
|
||||
args_t args = { 0 };
|
||||
const char** extras = NULL;
|
||||
int extra_count = 0;
|
||||
const char* err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER | XOPT_CTX_STRICT, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr,
|
||||
"sandbox [options]", "options:", NULL, 15);
|
||||
if (err)
|
||||
while (!show_usage)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
return 2;
|
||||
static const struct option k_options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ 0 },
|
||||
};
|
||||
int c = getopt_long(argc, argv, "h", k_options, NULL);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
show_usage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\nUsage: %s sandbox [options]\n\n", file);
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -h, --help Show this usage information.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
@ -528,22 +570,11 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
|
||||
/* The caller will trigger tf_task_activate with a message. */
|
||||
tf_task_run(task);
|
||||
tf_task_destroy(task);
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 0;
|
||||
|
||||
xopt_help:
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 1;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
static int _tf_command_usage(const char* file, int argc, char* argv[])
|
||||
static int _tf_command_usage(const char* file)
|
||||
{
|
||||
tf_printf("Usage: %s command [command-options]\n", file);
|
||||
tf_printf("commands:\n");
|
||||
@ -631,11 +662,11 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
if (strcmp(argv[1], "run") == 0)
|
||||
{
|
||||
result = _tf_command_run(argv[0], argc - 2, argv + 2);
|
||||
result = _tf_command_run(argv[0], argc - 1, argv + 1);
|
||||
}
|
||||
else if (strcmp(argv[1], "sandbox") == 0)
|
||||
{
|
||||
result = _tf_command_sandbox(argv[0], argc - 2, argv + 2);
|
||||
result = _tf_command_sandbox(argv[0], argc - 1, argv + 1);
|
||||
}
|
||||
}
|
||||
tf_mem_shutdown();
|
||||
@ -675,15 +706,15 @@ int main(int argc, char* argv[])
|
||||
const command_t* command = &k_commands[i];
|
||||
if (strcmp(argv[1], command->name) == 0)
|
||||
{
|
||||
result = command->callback(argv[0], argc - 2, argv + 2);
|
||||
result = command->callback(argv[0], argc - 1, argv + 1);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
result = _tf_command_usage(argv[0], argc, argv);
|
||||
result = _tf_command_usage(argv[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = _tf_command_run(argv[0], argc - 1, argv + 1);
|
||||
result = _tf_command_run(argv[0], argc, argv);
|
||||
}
|
||||
done:
|
||||
tf_mem_shutdown();
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
110
src/util.js.h
110
src/util.js.h
@ -10,24 +10,134 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
** Register utility script functions.
|
||||
** @param context The JS context.
|
||||
*/
|
||||
void tf_util_register(JSContext* context);
|
||||
|
||||
/**
|
||||
** Convert UTF-8 bytes in a buffer or Uint8Array or similar to a String.
|
||||
** @param context The JS context.
|
||||
** @param value The UTF-8 bytes.
|
||||
** @return A string representation of the data interpreted as UTF-8 bytes.
|
||||
*/
|
||||
JSValue tf_util_utf8_decode(JSContext* context, JSValue value);
|
||||
|
||||
/**
|
||||
** Get the data from what might be an ArrayBuffer.
|
||||
** @param context The JS context.
|
||||
** @param[out] psize The size of the data in bytes.
|
||||
** @param obj The object which might be an ArrayBuffer.
|
||||
** @return The ArrayBuffer's data.
|
||||
*/
|
||||
uint8_t* tf_util_try_get_array_buffer(JSContext* context, size_t* psize, JSValueConst obj);
|
||||
|
||||
/**
|
||||
** Get the ArrayBuffer from what might be a typed ArrayBuffer.
|
||||
** @param context The JS context.
|
||||
** @param obj The object which might be a typed ArrayBuffer.
|
||||
** @param[out] pbyte_offset The offset into the buffer at which the typed ArrayBuffer starts.
|
||||
** @param[out] pbyte_length The length of the buffer.
|
||||
** @param[out] pbytes_per_element Element size in bytes.
|
||||
** @return An ArrayBuffer if obj was a typed ArrayBuffer.
|
||||
*/
|
||||
JSValue tf_util_try_get_typed_array_buffer(JSContext* context, JSValueConst obj, size_t* pbyte_offset, size_t* pbyte_length, size_t* pbytes_per_element);
|
||||
|
||||
/**
|
||||
** Print an error and message the owning task if possible.
|
||||
** @param context The JS context.
|
||||
** @param value The value which might be an exception.
|
||||
** @return true If the value was an exception and an error was reported.
|
||||
*/
|
||||
bool tf_util_report_error(JSContext* context, JSValue value);
|
||||
|
||||
/**
|
||||
** Get the length of an array.
|
||||
** @param context The JS context.
|
||||
** @param value An array with a "length" field.
|
||||
** @return The array's length.
|
||||
*/
|
||||
int tf_util_get_length(JSContext* context, JSValue value);
|
||||
|
||||
/**
|
||||
** Get the index at which to insert into an array in order to preserve sorted order.
|
||||
** @param key The key being inserted.
|
||||
** @param base The beginning of the array.
|
||||
** @param count The number of elements in the array.
|
||||
** @param size The size of a single element of the array.
|
||||
** @param compare A comparison function comparing key and an element of the array.
|
||||
** @return The index at which to insert key in the array, between 0 and count inclusive.
|
||||
*/
|
||||
int tf_util_insert_index(const void* key, const void* base, size_t count, size_t size, int (*compare)(const void*, const void*));
|
||||
|
||||
/**
|
||||
** Create a Uint8Array from bytes.
|
||||
** @param context The JS context.
|
||||
** @param data The bytes.
|
||||
** @param size The number of bytes in data.
|
||||
** @return The created array.
|
||||
*/
|
||||
JSValue tf_util_new_uint8_array(JSContext* context, const uint8_t* data, size_t size);
|
||||
|
||||
/**
|
||||
** Base64-encode data.
|
||||
** @param source The source data.
|
||||
** @param source_length The length of the source data.
|
||||
** @param[out] out A buffer to receive the encoded data.
|
||||
** @param out_length The size of the buffer to receive encoded data.
|
||||
** @return The size of the encoded data.
|
||||
*/
|
||||
size_t tf_base64_encode(const uint8_t* source, size_t source_length, char* out, size_t out_length);
|
||||
|
||||
/**
|
||||
** Base64-decode data.
|
||||
** @param source The source data.
|
||||
** @param source_length The length of the source data.
|
||||
** @param[out] out A buffer to receipve the decoded data.
|
||||
** @param out_length The size of the buffer to receive decoded data.
|
||||
** @return The size of the decoded data.
|
||||
*/
|
||||
size_t tf_base64_decode(const char* source, size_t source_length, uint8_t* out, size_t out_length);
|
||||
|
||||
/**
|
||||
** Capture a stack backtrace of the calling thread.
|
||||
** @param[out] buffer A buffer with at least count element to receive the backtrace.
|
||||
** @param count The size of buffer.
|
||||
** @return The numbef of captured frames.
|
||||
*/
|
||||
int tf_util_backtrace(void** buffer, int count);
|
||||
|
||||
/**
|
||||
** Convert a stack backtrace to string.
|
||||
** @param buffer A stack backtrace.
|
||||
** @param count The number of elements in the backtrace.
|
||||
** @return A string representation of the stack backtrace with function names,
|
||||
** files, and line numbers, as possible. Must be freed with tf_free() by the
|
||||
** caller.
|
||||
*/
|
||||
const char* tf_util_backtrace_to_string(void* const* buffer, int count);
|
||||
|
||||
/**
|
||||
** Capture a stack backtrace of the calling thread and convert it immediately to string.
|
||||
** @return A string representation of the stack backtrace with function names,
|
||||
** files, and line numbers, as possible. Must be freed with tf_free() by the
|
||||
** caller.
|
||||
*/
|
||||
const char* tf_util_backtrace_string();
|
||||
|
||||
/**
|
||||
** Convert a function pointer to its name, if possible.
|
||||
** @return The function name or null.
|
||||
*/
|
||||
const char* tf_util_function_to_string(void* function);
|
||||
|
||||
/**
|
||||
** Get the minimum of two values.
|
||||
** @param a The first value.
|
||||
** @param b The second value.
|
||||
** @return The minimum of a and b.
|
||||
*/
|
||||
#define tf_min(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
|
@ -1,2 +1,2 @@
|
||||
#define VERSION_NUMBER "0.0.16-wip"
|
||||
#define VERSION_NAME "Medium English breakfast tea."
|
||||
#define VERSION_NUMBER "0.0.16"
|
||||
#define VERSION_NAME "Now with 38% more process."
|
||||
|
1621
tools/apple.py
1621
tools/apple.py
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user