aboutsummaryrefslogtreecommitdiffstats
path: root/extension/record.awk
diff options
context:
space:
mode:
Diffstat (limited to 'extension/record.awk')
-rw-r--r--extension/record.awk252
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)
+}