diff options
Diffstat (limited to 'extension/record.awk')
-rw-r--r-- | extension/record.awk | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/extension/record.awk b/extension/record.awk new file mode 100644 index 00000000..18a3ce48 --- /dev/null +++ b/extension/record.awk @@ -0,0 +1,252 @@ +# record.awk -- represent fixed-length records in a file as an array. +# Each element in the array corresponds to a record in the file. +# The records are numbered starting from 1, and each record read in +# from the file is cached. If opened using mode "r+", +# changes to the array are reflected in the file immediately i.e. +# writing to an element writes the data into the file. +# +# Usage: +# record(r, path [, reclen [, mode]]) +# r -- array to bind +# path -- filename +# reclen -- length of each record +# mode -- "r" for reading (default), "r+" for reading and writing +# +# With reclen <= 0, entire file is treated as one record #1. +# +# record(r, "data.in", 80, "r+") +# r[10] = r[1] +# for (i = 1; i in r; i++) +# print r[i] +# delete r[1] +# +# See Also: testrecord.sh +# +# +# TODO: +# * implement deferred writing +# * limit memory usage for read cache +# * use fixed size buffer when deleting a record +# + +BEGIN { + extension("fileop.so") + extension("bindarr.so") +} + +# _record_count --- return the number of records in file + +function _record_count(symbol, rd) +{ + if (! ("rectot" in rd)) + rd["rectot"] = ("reclen" in rd) ? + int(filesize(rd["path"]) / rd["reclen"]) : 1 + return rd["rectot"] +} + +# _record_exists --- check if record exists + +function _record_exists(symbol, rd, recnum, + path, mode, reclen, rectot) +{ + path = rd["path"] + reclen = ("reclen" in rd) ? rd["reclen"] : filesize(path) + mode = rd["mode"] + rectot = _record_count(symbol, rd) + + recnum = int(recnum) + if (recnum <= 0 || recnum > rectot) + return 0 + + if (! (recnum in symbol)) { + fseek(path, mode, (recnum - 1) * reclen, "SEEK_SET") + symbol[recnum] = fread(path, mode, reclen) + } + return 0 +} + +# _record_lookup --- lookup a record + +function _record_lookup(symbol, rd, recnum, + path, mode, reclen, rectot) +{ + path = rd["path"] + reclen = ("reclen" in rd) ? rd["reclen"] : filesize(path) + mode = rd["mode"] + rectot = _record_count(symbol, rd) + + recnum = int(recnum) + if (recnum <= 0 || recnum > rectot) { + ERRNO = sprintf("record: %s: reference to non-existent record #%d", path, recnum) + return -1 + } + + if (! (recnum in symbol)) { + fseek(path, mode, (recnum - 1) * reclen, "SEEK_SET") + symbol[recnum] = fread(path, mode, reclen) + } + return 0 +} + +# _record_clear --- remove all records + +function _record_clear(symbol, rd, + path, mode) +{ + path = rd["path"] + mode = rd["mode"] + if (mode == "r") { + ERRNO = sprintf("record: cannot delete record from file `%s' opened only for reading", path) + return -1 + } + ftruncate(path, mode, 0) + delete rd["reclen"] + return 0 +} + +# _record_delete --- delete a record from the file + +function _record_delete(symbol, rd, recnum, + path, mode, reclen, rectot) +{ + path = rd["path"] + reclen = ("reclen" in rd) ? rd["reclen"] : filesize(path) + mode = rd["mode"] + + if (mode == "r") { + ERRNO = sprintf("record: cannot delete record from file `%s' opened only for reading", path) + return -1 + } + + recnum = int(recnum) + if (! ("reclen" in rd)) { + # entire file is record #1 + ftruncate(path, mode, 0) + delete rd["reclen"] + return 0 + } + + sz = filesize(path) + rectot = int(sz / reclen) + + recstart = (recnum - 1) * reclen + off = sz - (recstart + reclen) + + fseek(path, mode, -off, "SEEK_END") + tmp = fread(path, mode, off) + fseek(path, mode, recstart, "SEEK_SET") + if (fwrite(path, mode, tmp) != length(tmp)) + return -1 + flush(path, mode) + ftruncate(path, mode, sz - reclen) + + rd["rectot"] = rectot - 1 + for (i = recnum + 1; i <= rectot; i++) { + if (i in symbol) { + symbol[i - 1] = symbol[i] + delete symbol[i] + } + } + return 0 +} + +# _record_store --- write a record to file + +function _record_store(symbol, rd, recnum, + path, mode, reclen, val) +{ + path = rd["path"] + reclen = ("reclen" in rd) ? rd["reclen"] : filesize(path) + mode = rd["mode"] + + if (mode == "r") { + ERRNO = sprintf("record: cannot write to file `%s' opened only for reading", path) + return -1 + } + + recnum = int(recnum) + val = symbol[recnum] + if (! ("reclen" in rd)) { + # the entire file is record #1 + if (reclen != 0) + ftruncate(path, mode, 0) + } else if (length(val) != reclen) { + ERRNO = sprintf("record: %s: invalid length for record #%d", path, recnum) + return -1 + } + + fseek(path, mode, (recnum - 1) * reclen, "SEEK_SET") + if (fwrite(path, mode, val) != length(val)) + return -1 + flush(path, mode) + return 0 +} + +# _record_fetchall --- retrieve all the records + +function _record_fetchall(symbol, rd, + path, mode, reclen, rectot, recnum) +{ + path = rd["path"] + reclen = ("reclen" in rd) ? rd["reclen"] : filesize(path) + mode = rd["mode"] + rectot = _record_count(symbol, rd) + + if (rd["loaded"]) + return 0 + for (recnum = 1; recnum <= rectot; recnum++) { + if (! (recnum in symbol)) { + fseek(path, mode, (recnum - 1) * reclen, "SEEK_SET") + symbol[recnum] = fread(path, mode, reclen) + } + } + rd["loaded"] = 1 + return 0 +} + +# _record_init --- initialization routine + +function _record_init(symbol, rd) +{ + if (! file_exists(rd["path"])) { + ERRNO = sprintf("record: cannot open file `%s' for reading", rd["path"]) + return -1 + } + return 0 +} + +# _record_fini --- cleanup routine + +function _record_fini(symbol, rd) +{ + fclose(rd["path"], rd["mode"]) +} + +# record --- bind an array to a file with fixed-length records + +function record(array, path, reclen, mode, rd) +{ + if (path == "") { + print "fatal: record: empty string value for filename" > "/dev/stderr" + exit(1) + } + + # register our array routines + rd["init"] = "_record_init" + rd["fini"] = "_record_fini" + rd["count"] = "_record_count" + rd["exists"] = "_record_exists" + rd["lookup"] = "_record_lookup" + rd["delete"] = "_record_delete" + rd["store"] = "_record_store" + rd["clear"] = "_record_clear" + rd["fetchall"] = "_record_fetchall" + + rd["path"] = path + if (reclen > 0) + rd["reclen"] = reclen + rd["mode"] = mode == "r+" ? "r+" : "r" + + delete array + bind_array(array, rd) +} |