rm index and indexof

This commit is contained in:
2026-01-17 16:21:02 -06:00
parent 97ece8e5cb
commit a7a323a74e
19 changed files with 86 additions and 303 deletions

View File

@@ -595,7 +595,7 @@ Total benchmarks: ${total_benches}
}
}
var json_path = `${report_dir}/${pkg_res.package.replace(/\//g, '_')}.json`
var json_path = `${report_dir}/${replace(pkg_res.package, /\//, '_')}.json`
fd.slurpwrite(json_path, stone(blob(json.encode(pkg_benches))))
}
}

View File

@@ -17,30 +17,7 @@ var writepath = "."
function normalize_path(path) {
if (!path) return ""
// Remove leading/trailing slashes and normalize
return path.replace(/^\/+|\/+$/g, "")
}
// Helper to get directory from path
function dirname(path) {
var idx = path.lastIndexOf("/")
if (idx == -1) return ""
return text(path, 0, idx)
}
// Helper to get basename from path
function basename(path) {
var idx = path.lastIndexOf("/")
if (idx == -1) return path
return text(path, idx + 1)
}
// Helper to join paths
function join_paths(base, rel) {
base = base.replace(/\/+$/, "")
rel = rel.replace(/^\/+/, "")
if (!base) return rel
if (!rel) return base
return base + "/" + rel
return replace(path, /^\/+|\/+$/, "")
}
// Check if a file exists in a specific mount
@@ -59,7 +36,7 @@ function mount_exists(mount, path) {
return false
}
} else { // fs
var full_path = join_paths(mount.source, path)
var full_path = fd.join_paths(mount.source, path)
try {
var st = fd.stat(full_path)
return st.isFile || st.isDirectory
@@ -86,7 +63,7 @@ function is_directory(path) {
return false;
}
} else { // fs
var full_path = join_paths(mount.source, path)
var full_path = fd.join_paths(mount.source, path)
try {
var st = fd.stat(full_path)
return st.isDirectory
@@ -103,11 +80,11 @@ function resolve(path, must_exist) {
// Check for named mount
if (starts_with(path, "@")) {
var idx = path.indexOf("/")
var idx = search(path, "/")
var mount_name = ""
var rel_path = ""
if (idx == -1) {
if (idx == null) {
mount_name = text(path, 1)
rel_path = ""
} else {
@@ -212,7 +189,7 @@ function slurp(path) {
if (!data) throw Error("File not found in qop: " + path)
return data
} else {
var full_path = join_paths(res.mount.source, res.path)
var full_path = fd.join_paths(res.mount.source, res.path)
return fd.slurp(full_path)
}
}
@@ -256,7 +233,7 @@ function stat(path) {
isDirectory: s.isDirectory
}
} else {
var full_path = join_paths(res.mount.source, res.path)
var full_path = fd.join_paths(res.mount.source, res.path)
var s = fd.stat(full_path)
return {
filesize: s.size,
@@ -298,14 +275,14 @@ function rm(path) {
var res = resolve(path, true)
if (res.mount.type != 'fs') throw Error("Cannot delete from non-fs mount")
var full_path = join_paths(res.mount.source, res.path)
var full_path = fd.join_paths(res.mount.source, res.path)
var st = fd.stat(full_path)
if (st.isDirectory) fd.rmdir(full_path)
else fd.unlink(full_path)
}
function mkdir(path) {
var full = join_paths(writepath, path)
var full = fd.join_paths(writepath, path)
fd.mkdir(full)
}
@@ -324,7 +301,7 @@ function prefdir(org, app) {
function realdir(path) {
var res = resolve(path, false)
if (!res) return null
return join_paths(res.mount.source, res.path)
return fd.join_paths(res.mount.source, res.path)
}
function enumerate(path, recurse) {
@@ -342,16 +319,16 @@ function enumerate(path, recurse) {
results.push(item_rel)
if (recurse) {
var st = fd.stat(join_paths(curr_full, item))
var st = fd.stat(fd.join_paths(curr_full, item))
if (st.isDirectory) {
visit(join_paths(curr_full, item), item_rel)
visit(fd.join_paths(curr_full, item), item_rel)
}
}
}
}
if (res.mount.type == 'fs') {
var full = join_paths(res.mount.source, res.path)
var full = fd.join_paths(res.mount.source, res.path)
var st = fd.stat(full)
if (st && st.isDirectory) {
visit(full, "")
@@ -370,8 +347,8 @@ function enumerate(path, recurse) {
if (rel.length == 0) continue
if (!recurse) {
var slash = rel.indexOf('/')
if (slash != -1) {
var slash = search(rel, '/')
if (slash != null) {
rel = text(rel, 0, slash)
}
}
@@ -415,7 +392,7 @@ function globfs(globs, dir) {
for (var item of list) {
var item_rel = rel_prefix ? rel_prefix + "/" + item : item
var child_full = join_paths(curr_full, item)
var child_full = fd.join_paths(curr_full, item)
var st = fd.stat(child_full)
if (st.isDirectory) {
@@ -431,7 +408,7 @@ function globfs(globs, dir) {
}
if (res.mount.type == 'fs') {
var full = join_paths(res.mount.source, res.path)
var full = fd.join_paths(res.mount.source, res.path)
var st = fd.stat(full)
if (st && st.isDirectory) {
visit(full, "")

View File

@@ -111,7 +111,7 @@ if (is_shop_scope) {
// Gather files to clean
var lib_dir = shop.get_lib_dir()
var build_dir = shop.get_build_dir()
var packages_dir = shop.get_package_dir('').replace(/\/$/, '') // Get base packages dir
var packages_dir = replace(shop.get_package_dir(''), /\/$/, '') // Get base packages dir
if (clean_build) {
if (is_shop_scope) {

View File

@@ -31,7 +31,7 @@ if (target_path == '.' || starts_with(target_path, './') || starts_with(target_p
target_path = cwd + text(target_path, 1)
} else if (starts_with(target_path, '../')) {
// Go up one directory from cwd
var parent = text(cwd, 0, cwd.lastIndexOf('/'))
var parent = fd.dirname(cwd)
target_path = parent + text(target_path, 2)
}
}
@@ -99,7 +99,7 @@ try {
parts.shift()
var rel_path = text(parts, '/')
var full_path = target_path + '/' + rel_path
var dir_path = text(full_path, 0, full_path.lastIndexOf('/'))
var dir_path = fd.dirname(full_path)
// Ensure directory exists
if (!fd.is_dir(dir_path)) {

View File

@@ -64,7 +64,7 @@ function parse_value(str) {
if (str == 'false') return false
// Number (including underscores)
var num_str = str.replace(/_/g, '')
var num_str = replace(str, /_/g, '')
if (/^-?\d+$/.test(num_str)) return parseInt(num_str)
if (/^-?\d*\.\d+$/.test(num_str)) return parseFloat(num_str)
@@ -77,7 +77,7 @@ function format_value(val) {
if (is_text(val)) return '"' + val + '"'
if (is_number(val) && val >= 1000) {
// Add underscores to large numbers
return val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '_')
return replace(val.toString(), /\B(?=(\d{3})+(?!\d))/g, '_')
}
return text(val)
}

View File

@@ -270,7 +270,7 @@ Cell supports regex patterns in string functions, but not standalone regex objec
```javascript
text.search("hello world", /world/)
text.replace("hello", /l/g, "L")
replace("hello", /l/g, "L")
```
## Error Handling

View File

@@ -70,18 +70,18 @@ text.search("hello world", "xyz") // null
text.search("hello hello", "hello", 1) // 6
```
### text.replace(text, target, replacement, limit)
### text.replace(text, target, replacement, cap)
Replace occurrences of `target` with `replacement`.
Replace occurrences of `target` with `replacement`. If `cap` is not specified, replaces all occurrences.
```javascript
text.replace("hello", "l", "L") // "heLLo"
text.replace("hello", "l", "L", 1) // "heLlo"
text.replace("hello", "l", "L") // "heLLo" (replaces all)
text.replace("hello", "l", "L", 1) // "heLlo" (replaces first only)
// With function
text.replace("hello", "l", function(match, pos) {
return pos == 2 ? "L" : match
}) // "heLlo"
}) // "heLLo" (replaces all by default)
```
### text.format(text, collection, transformer)

32
fd.cm
View File

@@ -1,15 +1,43 @@
var fd = this
var wildstar = use('wildstar')
function last_pos(str, sep) {
var last = null
replace(str, sep, function(m, pos) {
last = pos
return m
})
return last
}
// Helper to join paths
function join_paths(base, rel) {
base = base.replace(/\/+$/, "")
rel = rel.replace(/^\/+/, "")
base = replace(base, /\/+$/, "")
rel = replace(rel, /^\/+/, "")
if (!base) return rel
if (!rel) return base
return base + "/" + rel
}
fd.join_paths = join_paths
fd.basename = function basename(path) {
var last = last_pos(path, '/')
if (last == null) return path
return text(path, last+1)
}
fd.dirname = function dirname(path) {
var last = last_pos(path, '/')
if (last == null) return ""
return text(path,0,last)
}
fd.stem = function stem(path) {
var last = last_pos(path, '.')
if (last == null) return path
return text(path,0,last)
}
fd.globfs = function(globs, dir) {
if (dir == null) dir = "."
var results = []

View File

@@ -202,7 +202,7 @@ if (format == 'tree') {
if (node.local) attrs += ', color=blue'
// Safe node ID for dot
var safe_id = id.replaceAll(/[^a-zA-Z0-9]/g, '_')
var safe_id = replace(id, /[^a-zA-Z0-9]/g, '_')
log.console(' ' + safe_id + ' [' + attrs + '];')
}
@@ -210,8 +210,8 @@ if (format == 'tree') {
// Edges
for (var e of edges) {
var from_id = e.from.replaceAll(/[^a-zA-Z0-9]/g, '_')
var to_id = e.to.replaceAll(/[^a-zA-Z0-9]/g, '_')
var from_id = replace(e.from, /[^a-zA-Z0-9]/g, '_')
var to_id = replace(e.to, /[^a-zA-Z0-9]/g, '_')
var label = e.alias != e.to ? 'label="' + e.alias + '"' : ''
log.console(' ' + from_id + ' -> ' + to_id + (label ? ' [' + label + ']' : '') + ';')
}

View File

@@ -1106,7 +1106,7 @@ function install_zip(zip_blob, target_dir) {
parts.shift()
var rel_path = text(parts, '/')
var full_path = target_dir + '/' + rel_path
var dir_path = text(full_path, 0, full_path.lastIndexOf('/'))
var dir_path = fd.dirname(full_path)
if (!created_dirs[dir_path]) {
ensure_dir(dir_path)
@@ -1251,7 +1251,7 @@ Shop.get_package_dir = function(pkg) {
// -> 'js_gitea_pockle_world_john_prosperon_sprite_use'
Shop.c_symbol_for_file = function(pkg, file) {
var pkg_safe = replace(replace(replace(pkg, '/', '_'), '.', '_'), '-', '_')
var file_safe = replace(replace(text(file, 0, file.lastIndexOf('.')), '/', '_'), '.', '_')
var file_safe = replace(replace(fd.stem(file), '/', '_'), '.', '_')
return 'js_' + pkg_safe + '_' + file_safe + '_use'
}

View File

@@ -184,7 +184,7 @@ Link.sync_one = function(canonical, target, shop) {
var link_target = resolve_link_target(target)
// Ensure parent directories exist
var parent = text(target_dir, 0, target_dir.lastIndexOf('/'))
var parent = fd.dirname(target_dir)
ensure_dir(parent)
// Check current state

View File

@@ -120,19 +120,15 @@ package.find_package_dir = function(file)
var absolute = fd.realpath(file)
var dir = absolute
if (fd.is_file(dir)) {
var last_slash = dir.lastIndexOf('/')
if (last_slash > 0) dir = text(dir, 0, last_slash)
}
if (fd.is_file(dir))
dir = fd.dirname(dir)
while (dir && dir.length > 0) {
var toml_path = dir + '/cell.toml'
if (fd.is_file(toml_path)) {
return dir
}
var last_slash = dir.lastIndexOf('/')
if (last_slash <= 0) break
dir = text(dir, 0, last_slash)
dir = fd.dirname(dir)
}
return null
@@ -291,12 +287,7 @@ package.get_c_files = function(name, target, exclude_main) {
var ext = ends_with(file, '.cpp') ? '.cpp' : '.c'
var base = text(file, 0, -ext.length)
var dir = ''
var name_part = base
var slash = base.lastIndexOf('/')
if (slash >= 0) {
dir = text(base, 0, slash + 1)
name_part = text(base, slash + 1)
}
var name_part = fd.basename(base)
// Check for target suffix
var is_variant = false
@@ -341,9 +332,7 @@ package.get_c_files = function(name, target, exclude_main) {
if (selected) {
// Skip main.c if requested
if (exclude_main) {
var basename = selected
var s = selected.lastIndexOf('/')
if (s >= 0) basename = text(selected, s + 1)
var basename = fd.basename(selected)
if (basename == 'main.c' || starts_with(basename, 'main_')) continue
}
result.push(selected)

View File

@@ -52,7 +52,7 @@ function unpack(archive_path) {
var data = archive.read(f)
if (data) {
// Ensure directory exists
var dir = text(f, 0, f.lastIndexOf('/'))
var dir = fd.dirname(f)
if (dir) {
// recursive mkdir
var parts = array(dir, '/')

View File

@@ -30302,103 +30302,9 @@ static int js_string_find_invalid_codepoint(JSString *p)
return -1;
}
static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int lastIndexOf)
{
JSValue str, v;
int i, len, v_len, pos, start, stop, ret, inc;
JSString *p;
JSString *p1;
str = JS_ToStringCheckObject(ctx, this_val);
if (JS_IsException(str))
return str;
v = JS_ToString(ctx, argv[0]);
if (JS_IsException(v))
goto fail;
p = JS_VALUE_GET_STRING(str);
p1 = JS_VALUE_GET_STRING(v);
len = p->len;
v_len = p1->len;
if (lastIndexOf) {
pos = len - v_len;
if (argc > 1) {
double d;
if (JS_ToFloat64(ctx, &d, argv[1]))
goto fail;
if (!isnan(d)) {
if (d <= 0)
pos = 0;
else if (d < pos)
pos = d;
}
}
start = pos;
stop = 0;
inc = -1;
} else {
pos = 0;
if (argc > 1) {
if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
goto fail;
}
start = pos;
stop = len - v_len;
inc = 1;
}
ret = -1;
if (len >= v_len && inc * (stop - start) >= 0) {
for (i = start;; i += inc) {
if (!string_cmp(p, p1, i, 0, v_len)) {
ret = i;
break;
}
if (i == stop)
break;
}
}
JS_FreeValue(ctx, str);
JS_FreeValue(ctx, v);
return JS_NewInt32(ctx, ret);
fail:
JS_FreeValue(ctx, str);
JS_FreeValue(ctx, v);
return JS_EXCEPTION;
}
/* return < 0 if exception or TRUE/FALSE */
static int js_is_regexp(JSContext *ctx, JSValueConst obj);
static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)
{
int ret;
JSValue flags;
ret = js_is_regexp(ctx, regexp);
if (ret < 0)
return -1;
if (ret) {
flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);
if (JS_IsException(flags))
return -1;
if (JS_IsNull(flags) || JS_IsNull(flags)) {
JS_ThrowTypeError(ctx, "cannot convert to object");
return -1;
}
flags = JS_ToStringFree(ctx, flags);
if (JS_IsException(flags))
return -1;
ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);
JS_FreeValue(ctx, flags);
if (ret < 0) {
JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");
return -1;
}
}
return 0;
}
static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
@@ -30507,119 +30413,6 @@ exception:
return JS_EXCEPTION;
}
static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv,
int is_replaceAll)
{
// replace(rx, rep)
JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];
JSValueConst args[6];
JSValue str, search_str, replaceValue_str, repl_str;
JSString *sp, *searchp;
StringBuffer b_s, *b = &b_s;
int pos, functionalReplace, endOfLastMatch;
BOOL is_first;
if (JS_IsNull(O) || JS_IsNull(O))
return JS_ThrowTypeError(ctx, "cannot convert to object");
search_str = JS_NULL;
replaceValue_str = JS_NULL;
repl_str = JS_NULL;
if (!JS_IsNull(searchValue) && !JS_IsNull(searchValue)) {
JSValue replacer;
if (is_replaceAll) {
if (check_regexp_g_flag(ctx, searchValue) < 0)
return JS_EXCEPTION;
}
replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);
if (JS_IsException(replacer))
return JS_EXCEPTION;
if (!JS_IsNull(replacer) && !JS_IsNull(replacer)) {
args[0] = O;
args[1] = replaceValue;
return JS_CallFree(ctx, replacer, searchValue, 2, args);
}
}
string_buffer_init(ctx, b, 0);
str = JS_ToString(ctx, O);
if (JS_IsException(str))
goto exception;
search_str = JS_ToString(ctx, searchValue);
if (JS_IsException(search_str))
goto exception;
functionalReplace = JS_IsFunction(ctx, replaceValue);
if (!functionalReplace) {
replaceValue_str = JS_ToString(ctx, replaceValue);
if (JS_IsException(replaceValue_str))
goto exception;
}
sp = JS_VALUE_GET_STRING(str);
searchp = JS_VALUE_GET_STRING(search_str);
endOfLastMatch = 0;
is_first = TRUE;
for(;;) {
if (unlikely(searchp->len == 0)) {
if (is_first)
pos = 0;
else if (endOfLastMatch >= sp->len)
pos = -1;
else
pos = endOfLastMatch + 1;
} else {
pos = string_indexof(sp, searchp, endOfLastMatch);
}
if (pos < 0) {
if (is_first) {
string_buffer_free(b);
JS_FreeValue(ctx, search_str);
JS_FreeValue(ctx, replaceValue_str);
return str;
} else {
break;
}
}
if (functionalReplace) {
args[0] = search_str;
args[1] = JS_NewInt32(ctx, pos);
args[2] = str;
repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_NULL, 3, args));
} else {
args[0] = search_str;
args[1] = str;
args[2] = JS_NewInt32(ctx, pos);
args[3] = JS_NULL;
args[4] = JS_NULL;
args[5] = replaceValue_str;
repl_str = js_string___GetSubstitution(ctx, JS_NULL, 6, args);
}
if (JS_IsException(repl_str))
goto exception;
string_buffer_concat(b, sp, endOfLastMatch, pos);
string_buffer_concat_value_free(b, repl_str);
endOfLastMatch = pos + searchp->len;
is_first = FALSE;
if (!is_replaceAll)
break;
}
string_buffer_concat(b, sp, endOfLastMatch, sp->len);
JS_FreeValue(ctx, search_str);
JS_FreeValue(ctx, replaceValue_str);
JS_FreeValue(ctx, str);
return string_buffer_end(b);
exception:
string_buffer_free(b);
JS_FreeValue(ctx, search_str);
JS_FreeValue(ctx, replaceValue_str);
JS_FreeValue(ctx, str);
return JS_EXCEPTION;
}
/* also used for String.prototype.valueOf */
static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
@@ -30686,10 +30479,6 @@ static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,
static const JSCFunctionListEntry js_string_proto_funcs[] = {
JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),
JS_CFUNC_DEF("concat", 1, js_string_concat),
JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
JS_CFUNC_DEF("toString", 0, js_string_toString ),
JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),

View File

@@ -644,7 +644,7 @@ Total: ${totals.total}, Passed: ${totals.passed}, Failed: ${totals.failed}
}
}
var json_path = `${report_dir}/${pkg_res.package.replace(/\//g, '_')}.json`
var json_path = `${report_dir}/${replace(pkg_res.package, /\//, '_')}.json`
fd.slurpwrite(json_path, stone(blob(json.encode(pkg_tests))))
}
}

View File

@@ -1566,13 +1566,13 @@ return {
test_string_indexOf: function() {
var str = "hello world"
if (str.indexOf("world") != 6) throw "string indexOf failed"
if (str.indexOf("xyz") != -1) throw "string indexOf not found failed"
if (search(str, "world") != 6) throw "string search failed"
if (search(str, "xyz") != null) throw "string search not found failed"
},
test_string_lastIndexOf: function() {
var str = "hello hello"
if (str.lastIndexOf("hello") != 6) throw "string lastIndexOf failed"
if (search(str, "hello", 0, true) != 6) throw "string lastSearch failed"
},
test_string_toLowerCase: function() {
@@ -1949,8 +1949,8 @@ return {
test_text_returns_function_source: function() {
var fn = function(x) { return x * 2 }
var src = text(fn)
if (src.indexOf("function") == -1) throw "text(fn) should contain 'function'"
if (src.indexOf("return") == -1) throw "text(fn) should contain function body"
if (search(src, "function") == null) throw "text(fn) should contain 'function'"
if (search(src, "return") == null) throw "text(fn) should contain function body"
},
test_call_invokes_function: function() {
@@ -2057,7 +2057,7 @@ return {
proxy.nonexistent()
} catch (e) {
caught = true
if (e.indexOf("no such method") == -1) throw "wrong error message"
if (search(e, "no such method") == null) throw "wrong error message"
}
if (!caught) throw "proxy should throw for unknown method"
},
@@ -2892,7 +2892,7 @@ return {
test_text_number_float: function() {
var result = text(3.14)
if (result.indexOf("3.14") != 0) throw "text number float failed"
if (search(result, "3.14") != 0) throw "text number float failed"
},
test_text_array_join: function() {

View File

@@ -236,12 +236,12 @@ return {
test_tiny_number: function() {
var tiny = 0.0000001
var result = text(tiny, "n")
if (result.indexOf('e') == -1) throw "Tiny number format failed"
if (search(result, 'e', 0) == null) throw "Tiny number format failed"
},
test_huge_number: function() {
var huge = 1e22
var result = text(huge, "n")
if (result.indexOf('e') == -1) throw "Huge number format failed"
if (search(result, 'e', 0) == null) throw "Huge number format failed"
}
}

View File

@@ -190,9 +190,9 @@ function time_text(num = now(),
fmt = replace(fmt, "s", rec.second);
fmt = replace(fmt, "x", dst ? "DST" : ""); /* new */
fmt = replace(fmt, "z", (full_offset >= 0 ? "+" : "") + text(full_offset));
fmt = fmt.replaceAll(/mm[^bB]/g, rec.month + 1);
fmt = fmt.replaceAll(/m[^bB]/g, rec.month + 1);
fmt = fmt.replaceAll(/v[^bB]/g, rec.weekday);
fmt = replace(fmt, /mm[^bB]/g, rec.month + 1);
fmt = replace(fmt, /m[^bB]/g, rec.month + 1);
fmt = replace(fmt, /v[^bB]/g, rec.weekday);
fmt = replace(fmt, "mb", text(time.monthstr[rec.month], 0, 3));
fmt = replace(fmt, "mB", time.monthstr[rec.month]);
fmt = replace(fmt, "vB", time.weekdays[rec.weekday]);

View File

@@ -63,8 +63,8 @@ function parse_toml(toml_text) {
}
// Key-value pair
var eq_index = line.indexOf('=')
if (eq_index > 0) {
var eq_index = search(line, '=')
if (eq_index != null && eq_index > 0) {
var key_part = trim(text(line, 0, eq_index))
var value = trim(text(line, eq_index + 1))
if (key_part == null) key_part = trim(text(line, 0, eq_index))