From a9990169e9a67c9ed3a9f3860c54e69933d51b1e Mon Sep 17 00:00:00 2001 From: wkh Date: Sun, 11 Oct 2026 20:33:04 +0200 Subject: [PATCH] Rdb: Ruby-Nachbau der MWC /rdb-Werkzeuge --- README.md | 130 +++++++++++ bin/Number | 1 + bin/act | 1 + bin/addcol | 1 + bin/adjust | 1 + bin/append | 1 + bin/ascii | 1 + bin/balance | 1 + bin/blank | 1 + bin/bom | 1 + bin/calcpay | 1 + bin/calculate | 1 + bin/cap | 1 + bin/cashflow | 1 + bin/chartdup | 1 + bin/check.rdb | 1 + bin/chr | 1 + bin/clear.rdb | 1 + bin/close | 1 + bin/column | 1 + bin/commands | 1 + bin/compresss | 1 + bin/compute | 1 + bin/computedate | 1 + bin/consolidate | 1 + bin/cpdir.rdb | 1 + bin/cstate | 1 + bin/cursor | 1 + bin/datatype | 1 + bin/dbdict | 1 + bin/delete | 1 + bin/difference | 1 + bin/display | 1 + bin/domain | 1 + bin/enter | 1 + bin/explode | 1 + bin/fd | 1 + bin/filesize | 1 + bin/fillform | 1 + bin/fixtotable | 1 + bin/foot | 1 + bin/getjournal | 1 + bin/gregorian | 1 + bin/hashkey | 1 + bin/headoff | 1 + bin/headon | 1 + bin/helpme | 1 + bin/howmany | 1 + bin/index | 1 + bin/insertdash | 1 + bin/intersect | 1 + bin/invoice | 1 + bin/jointable | 1 + bin/julian | 1 + bin/justify | 1 + bin/label | 1 + bin/length | 1 + bin/letter | 1 + bin/like | 1 + bin/listtosh | 1 + bin/listtotable | 1 + bin/lock | 1 + bin/lowercase | 1 + bin/man | 1 + bin/maximum | 1 + bin/mean | 1 + bin/menu | 1 + bin/minimum | 1 + bin/not | 1 + bin/number | 1 + bin/pad | 1 + bin/padstring | 1 + bin/paste.rdb | 1 + bin/path | 1 + bin/precision | 1 + bin/project | 1 + bin/prompt | 1 + bin/rdb | 19 ++ bin/record | 1 + bin/rename | 1 + bin/replace | 1 + bin/report | 1 + bin/reportwriter | 1 + bin/rmblank | 1 + bin/rmcore | 1 + bin/row | 1 + bin/sale | 1 + bin/schema | 1 + bin/screen | 1 + bin/search | 1 + bin/searchtree | 1 + bin/see | 1 + bin/seek | 1 + bin/select | 1 + bin/sorttable | 1 + bin/soundex | 1 + bin/splittable | 1 + bin/substitute | 1 + bin/subtotal | 1 + bin/tableorlist | 1 + bin/tabletofact | 1 + bin/tabletofix | 1 + bin/tabletolist | 1 + bin/tabletom4 | 1 + bin/tabletorule | 1 + bin/tabletosed | 1 + bin/tabletostruct | 1 + bin/tabletotbl | 1 + bin/tax | 1 + bin/termput | 1 + bin/testall | 1 + bin/testsearch | 1 + bin/timesearch | 1 + bin/todaysdate | 1 + bin/total | 1 + bin/translate | 1 + bin/trim | 1 + bin/trimblank | 1 + bin/tset | 1 + bin/union | 1 + bin/uniondict | 1 + bin/unlock | 1 + bin/update.inv | 1 + bin/update.rdb | 1 + bin/uppercase | 1 + bin/validate | 1 + bin/ve | 1 + bin/vilock | 1 + bin/vindex | 1 + bin/w2 | 1 + bin/whatis | 1 + bin/whatwill | 1 + bin/widest | 1 + bin/width | 1 + bin/word | 1 + demo/X83 | 16 ++ demo/badinventory | 7 + demo/carexpense | 5 + demo/cash | 6 + demo/colors | 10 + demo/ed.t | 6 + demo/expense | 5 + demo/female | 4 + demo/form | 6 + demo/inventory | 9 + demo/journal | 10 + demo/maillist | 19 ++ demo/male | 4 + demo/name | 10 + demo/orders | 7 + demo/owners | 5 + demo/parent | 5 + demo/parttable | 6 + demo/ruletable | 3 + demo/testall.cmds | 14 ++ demo/testall.old | 136 +++++++++++ lib/rdb.rb | 42 ++++ lib/rdb/cmd/access.rb | 479 +++++++++++++++++++++++++++++++++++++ lib/rdb/cmd/act.rb | 409 ++++++++++++++++++++++++++++++++ lib/rdb/cmd/convert.rb | 142 +++++++++++ lib/rdb/cmd/relational.rb | 341 +++++++++++++++++++++++++++ lib/rdb/cmd/report.rb | 99 ++++++++ lib/rdb/cmd/term.rb | 238 +++++++++++++++++++ lib/rdb/cmd/text.rb | 239 +++++++++++++++++++ lib/rdb/env.rb | 133 +++++++++++ lib/rdb/fast.rb | 331 ++++++++++++++++++++++++++ lib/rdb/table.rb | 97 ++++++++ man/accounting_ter.man | 74 ++++++ man/act.man | 72 ++++++ man/addcol.man | 41 ++++ man/adjust.man | 95 ++++++++ man/append.man | 136 +++++++++++ man/ascii.man | 46 ++++ man/backup.man | 16 ++ man/balance.man | 71 ++++++ man/blank.man | 71 ++++++ man/bom.man | 82 +++++++ man/calcpay.man | 38 +++ man/calculate.man | 30 +++ man/cap.man | 54 +++++ man/cashflow.man | 180 ++++++++++++++ man/chartdup.man | 25 ++ man/check.rdb.man | 156 +++++++++++++ man/chr.man | 41 ++++ man/clear.rdb.man | 95 ++++++++ man/close.man | 36 +++ man/column.man | 125 ++++++++++ man/commands.man | 19 ++ man/compresss.man | 96 ++++++++ man/compute.man | 364 +++++++++++++++++++++++++++++ man/computedate.man | 78 +++++++ man/consolidate.man | 64 +++++ man/cpdir.rdb.man | 47 ++++ man/cstate.man | 63 +++++ man/cursor.man | 136 +++++++++++ man/dashline.man | 37 +++ man/datatype.man | 55 +++++ man/dbase.man | 261 +++++++++++++++++++++ man/dbdict.man | 46 ++++ man/delete.man | 96 ++++++++ man/difference.man | 88 +++++++ man/display.man | 35 +++ man/domain.man | 150 ++++++++++++ man/enter.man | 257 ++++++++++++++++++++ man/explode.man | 76 ++++++ man/fd.man | 129 ++++++++++ man/filesize.man | 64 +++++ man/fillform.man | 62 +++++ man/fixtotable.man | 68 ++++++ man/foot.man | 63 +++++ man/getjournal.man | 35 +++ man/gregorian.man | 171 ++++++++++++++ man/hashkey.man | 44 ++++ man/headline.man | 37 +++ man/headoff.man | 58 +++++ man/headon.man | 69 ++++++ man/helpme.man | 23 ++ man/howmany.man | 17 ++ man/index.man | 202 ++++++++++++++++ man/insertdash.man | 51 ++++ man/intersect.man | 79 +++++++ man/invoice.man | 54 +++++ man/jointable.man | 405 ++++++++++++++++++++++++++++++++ man/julian.man | 19 ++ man/justify.man | 139 +++++++++++ man/label.man | 63 +++++ man/length.man | 54 +++++ man/letter.man | 150 ++++++++++++ man/like.man | 25 ++ man/listtosh.man | 66 ++++++ man/listtotable.man | 120 ++++++++++ man/lock.man | 95 ++++++++ man/lowercase.man | 56 +++++ man/maximum.man | 27 +++ man/mean.man | 37 +++ man/menu.man | 276 ++++++++++++++++++++++ man/minimum.man | 33 +++ man/not.man | 35 +++ man/number.c.man | 27 +++ man/number.man | 70 ++++++ man/pad.man | 103 ++++++++ man/padstring.man | 116 +++++++++ man/paste.rdb.man | 137 +++++++++++ man/path.man | 80 +++++++ man/precision.man | 29 +++ man/project.man | 12 + man/prompt.man | 60 +++++ man/rdb.man | 20 ++ man/rdb.tmac | 27 +++ man/record.man | 72 ++++++ man/rename.man | 118 ++++++++++ man/replace.man | 154 ++++++++++++ man/report.man | 187 +++++++++++++++ man/reportwriter.man | 47 ++++ man/rmblank.man | 111 +++++++++ man/rmcore.man | 28 +++ man/row.man | 189 +++++++++++++++ man/sale.man | 115 +++++++++ man/schema.man | 58 +++++ man/screen.man | 190 +++++++++++++++ man/search.man | 426 +++++++++++++++++++++++++++++++++ man/searchtree.man | 165 +++++++++++++ man/see.man | 68 ++++++ man/seek.man | 124 ++++++++++ man/select.man | 31 +++ man/sorttable.man | 172 ++++++++++++++ man/soundex.man | 108 +++++++++ man/splittable.man | 80 +++++++ man/substitute.man | 65 ++++++ man/subtotal.man | 101 ++++++++ man/tableorlist.man | 50 ++++ man/tabletofact.man | 109 +++++++++ man/tabletofix.man | 82 +++++++ man/tabletolist.man | 68 ++++++ man/tabletom4.man | 101 ++++++++ man/tabletorule.man | 106 +++++++++ man/tabletosed.man | 99 ++++++++ man/tabletostruct.man | 159 +++++++++++++ man/tabletotbl.man | 70 ++++++ man/tax.man | 87 +++++++ man/termput.man | 108 +++++++++ man/testall.man | 64 +++++ man/testsearch.man | 40 ++++ man/timesearch.man | 66 ++++++ man/todaysdate.man | 27 +++ man/total.man | 87 +++++++ man/translate.man | 121 ++++++++++ man/trim.man | 335 ++++++++++++++++++++++++++ man/trimblank.man | 28 +++ man/tset.man | 66 ++++++ man/union.man | 87 +++++++ man/uniondict.man | 55 +++++ man/unlock.man | 103 ++++++++ man/update.inv.man | 67 ++++++ man/update.rdb.man | 360 ++++++++++++++++++++++++++++ man/uppercase.man | 56 +++++ man/validate.man | 146 ++++++++++++ man/ve.man | 381 ++++++++++++++++++++++++++++++ man/vilock.man | 48 ++++ man/vindex.man | 74 ++++++ man/whatis.man | 48 ++++ man/whatwill.man | 60 +++++ man/widest.man | 39 ++++ man/width.man | 26 +++ man/word.man | 73 ++++++ mise.toml | 19 ++ test/rdb_test.rb | 481 ++++++++++++++++++++++++++++++++++++++ test/test_helper.rb | 49 ++++ 308 files changed, 16738 insertions(+) create mode 100644 README.md create mode 120000 bin/Number create mode 120000 bin/act create mode 120000 bin/addcol create mode 120000 bin/adjust create mode 120000 bin/append create mode 120000 bin/ascii create mode 120000 bin/balance create mode 120000 bin/blank create mode 120000 bin/bom create mode 120000 bin/calcpay create mode 120000 bin/calculate create mode 120000 bin/cap create mode 120000 bin/cashflow create mode 120000 bin/chartdup create mode 120000 bin/check.rdb create mode 120000 bin/chr create mode 120000 bin/clear.rdb create mode 120000 bin/close create mode 120000 bin/column create mode 120000 bin/commands create mode 120000 bin/compresss create mode 120000 bin/compute create mode 120000 bin/computedate create mode 120000 bin/consolidate create mode 120000 bin/cpdir.rdb create mode 120000 bin/cstate create mode 120000 bin/cursor create mode 120000 bin/datatype create mode 120000 bin/dbdict create mode 120000 bin/delete create mode 120000 bin/difference create mode 120000 bin/display create mode 120000 bin/domain create mode 120000 bin/enter create mode 120000 bin/explode create mode 120000 bin/fd create mode 120000 bin/filesize create mode 120000 bin/fillform create mode 120000 bin/fixtotable create mode 120000 bin/foot create mode 120000 bin/getjournal create mode 120000 bin/gregorian create mode 120000 bin/hashkey create mode 120000 bin/headoff create mode 120000 bin/headon create mode 120000 bin/helpme create mode 120000 bin/howmany create mode 120000 bin/index create mode 120000 bin/insertdash create mode 120000 bin/intersect create mode 120000 bin/invoice create mode 120000 bin/jointable create mode 120000 bin/julian create mode 120000 bin/justify create mode 120000 bin/label create mode 120000 bin/length create mode 120000 bin/letter create mode 120000 bin/like create mode 120000 bin/listtosh create mode 120000 bin/listtotable create mode 120000 bin/lock create mode 120000 bin/lowercase create mode 120000 bin/man create mode 120000 bin/maximum create mode 120000 bin/mean create mode 120000 bin/menu create mode 120000 bin/minimum create mode 120000 bin/not create mode 120000 bin/number create mode 120000 bin/pad create mode 120000 bin/padstring create mode 120000 bin/paste.rdb create mode 120000 bin/path create mode 120000 bin/precision create mode 120000 bin/project create mode 120000 bin/prompt create mode 100755 bin/rdb create mode 120000 bin/record create mode 120000 bin/rename create mode 120000 bin/replace create mode 120000 bin/report create mode 120000 bin/reportwriter create mode 120000 bin/rmblank create mode 120000 bin/rmcore create mode 120000 bin/row create mode 120000 bin/sale create mode 120000 bin/schema create mode 120000 bin/screen create mode 120000 bin/search create mode 120000 bin/searchtree create mode 120000 bin/see create mode 120000 bin/seek create mode 120000 bin/select create mode 120000 bin/sorttable create mode 120000 bin/soundex create mode 120000 bin/splittable create mode 120000 bin/substitute create mode 120000 bin/subtotal create mode 120000 bin/tableorlist create mode 120000 bin/tabletofact create mode 120000 bin/tabletofix create mode 120000 bin/tabletolist create mode 120000 bin/tabletom4 create mode 120000 bin/tabletorule create mode 120000 bin/tabletosed create mode 120000 bin/tabletostruct create mode 120000 bin/tabletotbl create mode 120000 bin/tax create mode 120000 bin/termput create mode 120000 bin/testall create mode 120000 bin/testsearch create mode 120000 bin/timesearch create mode 120000 bin/todaysdate create mode 120000 bin/total create mode 120000 bin/translate create mode 120000 bin/trim create mode 120000 bin/trimblank create mode 120000 bin/tset create mode 120000 bin/union create mode 120000 bin/uniondict create mode 120000 bin/unlock create mode 120000 bin/update.inv create mode 120000 bin/update.rdb create mode 120000 bin/uppercase create mode 120000 bin/validate create mode 120000 bin/ve create mode 120000 bin/vilock create mode 120000 bin/vindex create mode 120000 bin/w2 create mode 120000 bin/whatis create mode 120000 bin/whatwill create mode 120000 bin/widest create mode 120000 bin/width create mode 120000 bin/word create mode 100644 demo/X83 create mode 100644 demo/badinventory create mode 100644 demo/carexpense create mode 100644 demo/cash create mode 100644 demo/colors create mode 100644 demo/ed.t create mode 100644 demo/expense create mode 100644 demo/female create mode 100644 demo/form create mode 100644 demo/inventory create mode 100644 demo/journal create mode 100644 demo/maillist create mode 100644 demo/male create mode 100644 demo/name create mode 100644 demo/orders create mode 100644 demo/owners create mode 100644 demo/parent create mode 100644 demo/parttable create mode 100644 demo/ruletable create mode 100644 demo/testall.cmds create mode 100644 demo/testall.old create mode 100644 lib/rdb.rb create mode 100644 lib/rdb/cmd/access.rb create mode 100644 lib/rdb/cmd/act.rb create mode 100644 lib/rdb/cmd/convert.rb create mode 100644 lib/rdb/cmd/relational.rb create mode 100644 lib/rdb/cmd/report.rb create mode 100644 lib/rdb/cmd/term.rb create mode 100644 lib/rdb/cmd/text.rb create mode 100644 lib/rdb/env.rb create mode 100644 lib/rdb/fast.rb create mode 100644 lib/rdb/table.rb create mode 100644 man/accounting_ter.man create mode 100644 man/act.man create mode 100644 man/addcol.man create mode 100644 man/adjust.man create mode 100644 man/append.man create mode 100644 man/ascii.man create mode 100644 man/backup.man create mode 100644 man/balance.man create mode 100644 man/blank.man create mode 100644 man/bom.man create mode 100644 man/calcpay.man create mode 100644 man/calculate.man create mode 100644 man/cap.man create mode 100644 man/cashflow.man create mode 100644 man/chartdup.man create mode 100644 man/check.rdb.man create mode 100644 man/chr.man create mode 100644 man/clear.rdb.man create mode 100644 man/close.man create mode 100644 man/column.man create mode 100644 man/commands.man create mode 100644 man/compresss.man create mode 100644 man/compute.man create mode 100644 man/computedate.man create mode 100644 man/consolidate.man create mode 100644 man/cpdir.rdb.man create mode 100644 man/cstate.man create mode 100644 man/cursor.man create mode 100644 man/dashline.man create mode 100644 man/datatype.man create mode 100644 man/dbase.man create mode 100644 man/dbdict.man create mode 100644 man/delete.man create mode 100644 man/difference.man create mode 100644 man/display.man create mode 100644 man/domain.man create mode 100644 man/enter.man create mode 100644 man/explode.man create mode 100644 man/fd.man create mode 100644 man/filesize.man create mode 100644 man/fillform.man create mode 100644 man/fixtotable.man create mode 100644 man/foot.man create mode 100644 man/getjournal.man create mode 100644 man/gregorian.man create mode 100644 man/hashkey.man create mode 100644 man/headline.man create mode 100644 man/headoff.man create mode 100644 man/headon.man create mode 100644 man/helpme.man create mode 100644 man/howmany.man create mode 100644 man/index.man create mode 100644 man/insertdash.man create mode 100644 man/intersect.man create mode 100644 man/invoice.man create mode 100644 man/jointable.man create mode 100644 man/julian.man create mode 100644 man/justify.man create mode 100644 man/label.man create mode 100644 man/length.man create mode 100644 man/letter.man create mode 100644 man/like.man create mode 100644 man/listtosh.man create mode 100644 man/listtotable.man create mode 100644 man/lock.man create mode 100644 man/lowercase.man create mode 100644 man/maximum.man create mode 100644 man/mean.man create mode 100644 man/menu.man create mode 100644 man/minimum.man create mode 100644 man/not.man create mode 100644 man/number.c.man create mode 100644 man/number.man create mode 100644 man/pad.man create mode 100644 man/padstring.man create mode 100644 man/paste.rdb.man create mode 100644 man/path.man create mode 100644 man/precision.man create mode 100644 man/project.man create mode 100644 man/prompt.man create mode 100644 man/rdb.man create mode 100644 man/rdb.tmac create mode 100644 man/record.man create mode 100644 man/rename.man create mode 100644 man/replace.man create mode 100644 man/report.man create mode 100644 man/reportwriter.man create mode 100644 man/rmblank.man create mode 100644 man/rmcore.man create mode 100644 man/row.man create mode 100644 man/sale.man create mode 100644 man/schema.man create mode 100644 man/screen.man create mode 100644 man/search.man create mode 100644 man/searchtree.man create mode 100644 man/see.man create mode 100644 man/seek.man create mode 100644 man/select.man create mode 100644 man/sorttable.man create mode 100644 man/soundex.man create mode 100644 man/splittable.man create mode 100644 man/substitute.man create mode 100644 man/subtotal.man create mode 100644 man/tableorlist.man create mode 100644 man/tabletofact.man create mode 100644 man/tabletofix.man create mode 100644 man/tabletolist.man create mode 100644 man/tabletom4.man create mode 100644 man/tabletorule.man create mode 100644 man/tabletosed.man create mode 100644 man/tabletostruct.man create mode 100644 man/tabletotbl.man create mode 100644 man/tax.man create mode 100644 man/termput.man create mode 100644 man/testall.man create mode 100644 man/testsearch.man create mode 100644 man/timesearch.man create mode 100644 man/todaysdate.man create mode 100644 man/total.man create mode 100644 man/translate.man create mode 100644 man/trim.man create mode 100644 man/trimblank.man create mode 100644 man/tset.man create mode 100644 man/union.man create mode 100644 man/uniondict.man create mode 100644 man/unlock.man create mode 100644 man/update.inv.man create mode 100644 man/update.rdb.man create mode 100644 man/uppercase.man create mode 100644 man/validate.man create mode 100644 man/ve.man create mode 100644 man/vilock.man create mode 100644 man/vindex.man create mode 100644 man/whatis.man create mode 100644 man/whatwill.man create mode 100644 man/widest.man create mode 100644 man/width.man create mode 100644 man/word.man create mode 100644 mise.toml create mode 100644 test/rdb_test.rb create mode 100644 test/test_helper.rb diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ad3e17 --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +# rdb + +Ein Nachbau von **/rdb**, dem relationalen Datenbanksystem der Mark Williams +Company für COHERENT (um 1991), in Ruby. Das Original-Handbuch und die +Man-Pages stammen aus den freigegebenen MWC-Quellen +([nesssoftware.com/home/mwc/source.php](https://www.nesssoftware.com/home/mwc/source.php)). +Der Programmcode von /rdb selbst war darin nicht enthalten. Alle Befehle hier +sind nach dem Handbuch neu geschrieben. + +## Einrichten + +``` +mise install # Ruby 3.4 +mise run install # legt die Symlinks row, column, total ... in bin/ an +mise run test # 76 Tests +``` + +Danach steht `bin/` im PATH, solange man im Projektordner ist (`mise.toml`). +Jeder Befehl ist ein Symlink auf `bin/rdb`, das am Aufrufnamen erkennt, was zu +tun ist. Es geht auch ohne Symlinks: `rdb row 'Cost > 5' < demo/inventory`. + +Vier Namen verdecken im Projektordner Systembefehle: `column`, `rename`, +`display` und `whatis`. + +## Das Prinzip + +Eine **Tabelle** ist eine Textdatei: Kopfzeile mit Spaltennamen, Strichzeile, +dann eine Zeile pro Datensatz, Spalten durch Tabulator getrennt. Jeder Befehl +liest eine Tabelle von stdin und schreibt eine Tabelle nach stdout. Befehle +lassen sich mit Pipes kombinieren. + +``` +cd demo +row 'Cost > 5' < inventory | column Description Cost | sorttable -nr Cost +compute 'Value = Cost * Amount' < inventory | total Value +jointable inventory owners +``` + +Dasselbe gilt für **Listen** (ein Datensatz als `SpalteWert`-Zeilen, die +Datei beginnt mit einer Leerzeile). `tabletolist` und `listtotable` wandeln um, +die meisten Befehle lesen beides. + +`row`, `compute` und `validate` übergeben ihren Ausdruck an awk, nachdem sie +Spaltennamen durch `$N` ersetzt haben. Es gilt also awk-Syntax. + +## Hilfe + +``` +rdb alle Befehle +rdb man search die Original-Man-Page als Text (auch Themen: backup, dbase, dashline ...) +rdb man -g search dieselbe Seite mit groff formatiert (Beispiele sauber eingerückt) +whatis column Beschreibung und Syntax +whatwill total Befehle, die zu einem Stichwort passen +commands Tabelle aller Befehle +``` + +## Befehle + +| Gruppe | Befehle | +|---|---| +| Auswahl | `row` `select` `column` `project` `sorttable` `record` `number` `Number` | +| Spalten | `compute` `addcol` `rename` | +| Mengen, Verbund | `jointable` `union` `intersect` `difference` `paste.rdb` `splittable` | +| Auswertung | `total` `mean` `maximum` `minimum` `subtotal` `width` `widest` `precision` `datatype` `schema` `dbdict` | +| Schnellzugriff | `index` `search` `seek` `append` `delete` `replace` `enter` `lock` `unlock` `vilock` `update.rdb` `ve` `vindex` | +| Prüfen | `check.rdb` `domain` `validate` `fd` `searchtree` `tableorlist` | +| Format | `justify` `trim` `compresss` `pad` `rmblank` `blank` `headon` `headoff` `insertdash` `display` `see` | +| Text | `cap` `uppercase` `lowercase` `substitute` `trimblank` `padstring` `length` `filesize` `ascii` `chr` `word` `prompt` `not` `path` | +| Umwandeln | `tabletolist` `listtotable` `listtosh` `tabletofix` `fixtotable` `tabletostruct` `tabletotbl` `tabletofact` `tabletorule` `tabletom4` `tabletosed` `translate` `uniondict` | +| Datum, Namen | `julian` `gregorian` `computedate` `todaysdate` `soundex` `like` `hashkey` | +| Berichte | `report` `reportwriter` `screen` `label` `letter` | +| Terminal, Hilfe | `termput` `tset` `clear.rdb` `cursor` `menu` `rdb` `commands` `helpme` `whatis` `whatwill` `howmany` `man` | +| Betrieb | `testall` `testsearch` `timesearch` `rmcore` `cpdir.rdb` | +| Buchhaltung (/act) | `act` `chartdup` `foot` `adjust` `balance` `close` `consolidate` `getjournal` `fillform` `calculate` `tax` `cashflow` `calcpay` `w2` `bom` `explode` `sale` `invoice` `cstate` `update.inv` | + +## Schnellzugriff + +Wie im Original sucht `search` über Byte-Offsets. Indizes bauen: + +| Option | Methode | Datei | +|---|---|---| +| `-ms` | sequentiell (Vorgabe) | keine | +| `-mb` | binär: `index` sortiert die Tabelle selbst | keine | +| `-mr` | Datensatznummer | `tabelle.r` | +| `-mi` | invertiert (Schlüssel und Offset, sortiert) | `tabelle.i` | +| `-mh` | Hash, doppelt so viele Plätze wie Zeilen | `tabelle.h` | + +``` +index -mi inventory Description +echo scales | search -mi inventory Description +echo cl | search -mi -x inventory Description # Teiltreffer, Groß/klein egal +echo 5 | seek -mi inventory Item # von bis xvon xbis +``` + +`delete` überschreibt einen Satz mit Leerzeichen, ohne die Datei zu verschieben. +`replace` schreibt an dieselbe Stelle zurück und hängt den Satz hinten an, wenn +er nicht mehr passt (außer mit `-a`). Beide halten die Indizes aktuell. +`rmblank` räumt später auf. + +## Unterschiede zum Original + +- Es gibt kein COHERENT-Programmmaterial, daher sind die **Ausgaben nur anhand der + Man-Pages geprüft**, nicht gegen das echte /rdb. Die Beispiele aus dem + Handbuch sind als Tests enthalten (`test/rdb_test.rb`). +- Die Indexdateien haben ein eigenes, einfaches Format (das Original ist nicht + dokumentiert). Sie sind nicht mit alten /rdb-Dateien austauschbar. +- `jointable` braucht keine vorsortierten Tabellen. +- `ve` ist ein zeilenorientierter Editor, kein Bildschirmeditor. +- `soundex` nutzt den Standard-Algorithmus. Die Beispielwerte im Handbuch + weichen davon ab. +- Die /act-Befehle arbeiten auf Tabellen im aktuellen Ordner. Welche das sind + und welche Spalten sie brauchen, steht jeweils in der Man-Page. Das Paket ist + wie im Original nur ein Beispiel für Datenbankprogrammierung. Die Befehle, zu + denen das Handbuch nichts sagt (`post`, `purchase`, `ship` ...), fehlen. + +## Aufbau + +``` +bin/rdb das eine Programm (und die Symlinks darauf) +lib/rdb.rb Registry der Befehle +lib/rdb/table.rb Tabellen und Listen lesen und schreiben +lib/rdb/fast.rb Schnellzugriff über Byte-Offsets +lib/rdb/cmd/*.rb die Befehle, nach Gruppen +man/ Man-Pages (troff-Quellen aus den MWC-Archiven) +demo/ Beispieltabellen, testall.cmds und testall.old +test/ Tests +``` + +Die Man-Pages und die Beispieltabellen stammen aus den MWC-Quellen. Lizenz der +Originale: BSD, Copyright 1977-1995 Robert Swartz. diff --git a/bin/Number b/bin/Number new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/Number @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/act b/bin/act new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/act @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/addcol b/bin/addcol new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/addcol @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/adjust b/bin/adjust new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/adjust @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/append b/bin/append new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/append @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/ascii b/bin/ascii new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/ascii @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/balance b/bin/balance new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/balance @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/blank b/bin/blank new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/blank @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/bom b/bin/bom new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/bom @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/calcpay b/bin/calcpay new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/calcpay @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/calculate b/bin/calculate new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/calculate @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/cap b/bin/cap new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/cap @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/cashflow b/bin/cashflow new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/cashflow @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/chartdup b/bin/chartdup new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/chartdup @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/check.rdb b/bin/check.rdb new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/check.rdb @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/chr b/bin/chr new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/chr @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/clear.rdb b/bin/clear.rdb new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/clear.rdb @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/close b/bin/close new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/close @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/column b/bin/column new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/column @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/commands b/bin/commands new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/commands @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/compresss b/bin/compresss new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/compresss @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/compute b/bin/compute new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/compute @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/computedate b/bin/computedate new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/computedate @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/consolidate b/bin/consolidate new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/consolidate @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/cpdir.rdb b/bin/cpdir.rdb new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/cpdir.rdb @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/cstate b/bin/cstate new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/cstate @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/cursor b/bin/cursor new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/cursor @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/datatype b/bin/datatype new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/datatype @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/dbdict b/bin/dbdict new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/dbdict @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/delete b/bin/delete new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/delete @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/difference b/bin/difference new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/difference @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/display b/bin/display new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/display @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/domain b/bin/domain new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/domain @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/enter b/bin/enter new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/enter @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/explode b/bin/explode new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/explode @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/fd b/bin/fd new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/fd @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/filesize b/bin/filesize new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/filesize @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/fillform b/bin/fillform new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/fillform @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/fixtotable b/bin/fixtotable new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/fixtotable @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/foot b/bin/foot new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/foot @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/getjournal b/bin/getjournal new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/getjournal @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/gregorian b/bin/gregorian new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/gregorian @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/hashkey b/bin/hashkey new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/hashkey @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/headoff b/bin/headoff new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/headoff @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/headon b/bin/headon new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/headon @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/helpme b/bin/helpme new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/helpme @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/howmany b/bin/howmany new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/howmany @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/index b/bin/index new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/index @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/insertdash b/bin/insertdash new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/insertdash @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/intersect b/bin/intersect new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/intersect @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/invoice b/bin/invoice new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/invoice @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/jointable b/bin/jointable new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/jointable @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/julian b/bin/julian new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/julian @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/justify b/bin/justify new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/justify @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/label b/bin/label new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/label @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/length b/bin/length new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/length @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/letter b/bin/letter new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/letter @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/like b/bin/like new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/like @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/listtosh b/bin/listtosh new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/listtosh @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/listtotable b/bin/listtotable new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/listtotable @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/lock b/bin/lock new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/lock @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/lowercase b/bin/lowercase new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/lowercase @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/man b/bin/man new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/man @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/maximum b/bin/maximum new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/maximum @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/mean b/bin/mean new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/mean @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/menu b/bin/menu new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/menu @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/minimum b/bin/minimum new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/minimum @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/not b/bin/not new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/not @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/number b/bin/number new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/number @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/pad b/bin/pad new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/pad @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/padstring b/bin/padstring new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/padstring @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/paste.rdb b/bin/paste.rdb new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/paste.rdb @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/path b/bin/path new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/path @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/precision b/bin/precision new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/precision @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/project b/bin/project new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/project @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/prompt b/bin/prompt new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/prompt @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/rdb b/bin/rdb new file mode 100755 index 0000000..b077f28 --- /dev/null +++ b/bin/rdb @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# rdb -- every /rdb command is this one program, looking at the name it was +# called by. `row`, `column`, `total` ... are symlinks to it (`rdb install`). + +require_relative '../lib/rdb' + +name = File.basename($PROGRAM_NAME) +args = ARGV.dup +Signal.trap('PIPE', 'SYSTEM_DEFAULT') +if name == 'rdb' + case args.first + when nil, '-h', '--help' then name = 'rdb.list' + when 'install' then name = 'rdb.install'; args.shift + else name = args.shift + end +end +Rdb.run(name, args) diff --git a/bin/record b/bin/record new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/record @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/rename b/bin/rename new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/rename @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/replace b/bin/replace new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/replace @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/report b/bin/report new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/report @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/reportwriter b/bin/reportwriter new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/reportwriter @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/rmblank b/bin/rmblank new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/rmblank @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/rmcore b/bin/rmcore new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/rmcore @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/row b/bin/row new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/row @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/sale b/bin/sale new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/sale @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/schema b/bin/schema new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/schema @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/screen b/bin/screen new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/screen @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/search b/bin/search new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/search @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/searchtree b/bin/searchtree new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/searchtree @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/see b/bin/see new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/see @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/seek b/bin/seek new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/seek @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/select b/bin/select new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/select @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/sorttable b/bin/sorttable new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/sorttable @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/soundex b/bin/soundex new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/soundex @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/splittable b/bin/splittable new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/splittable @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/substitute b/bin/substitute new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/substitute @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/subtotal b/bin/subtotal new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/subtotal @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tableorlist b/bin/tableorlist new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tableorlist @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tabletofact b/bin/tabletofact new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tabletofact @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tabletofix b/bin/tabletofix new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tabletofix @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tabletolist b/bin/tabletolist new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tabletolist @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tabletom4 b/bin/tabletom4 new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tabletom4 @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tabletorule b/bin/tabletorule new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tabletorule @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tabletosed b/bin/tabletosed new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tabletosed @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tabletostruct b/bin/tabletostruct new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tabletostruct @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tabletotbl b/bin/tabletotbl new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tabletotbl @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tax b/bin/tax new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tax @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/termput b/bin/termput new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/termput @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/testall b/bin/testall new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/testall @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/testsearch b/bin/testsearch new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/testsearch @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/timesearch b/bin/timesearch new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/timesearch @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/todaysdate b/bin/todaysdate new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/todaysdate @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/total b/bin/total new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/total @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/translate b/bin/translate new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/translate @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/trim b/bin/trim new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/trim @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/trimblank b/bin/trimblank new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/trimblank @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/tset b/bin/tset new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/tset @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/union b/bin/union new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/union @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/uniondict b/bin/uniondict new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/uniondict @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/unlock b/bin/unlock new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/unlock @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/update.inv b/bin/update.inv new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/update.inv @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/update.rdb b/bin/update.rdb new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/update.rdb @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/uppercase b/bin/uppercase new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/uppercase @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/validate b/bin/validate new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/validate @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/ve b/bin/ve new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/ve @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/vilock b/bin/vilock new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/vilock @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/vindex b/bin/vindex new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/vindex @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/w2 b/bin/w2 new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/w2 @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/whatis b/bin/whatis new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/whatis @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/whatwill b/bin/whatwill new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/whatwill @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/widest b/bin/widest new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/widest @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/width b/bin/width new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/width @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/bin/word b/bin/word new file mode 120000 index 0000000..9570433 --- /dev/null +++ b/bin/word @@ -0,0 +1 @@ +/home/wkh/Development/Coherent/rdb/bin/rdb \ No newline at end of file diff --git a/demo/X83 b/demo/X83 new file mode 100644 index 0000000..966d65d --- /dev/null +++ b/demo/X83 @@ -0,0 +1,16 @@ +income tax percent +------ --- ------- +2300 0 11 +3400 121 12 +4400 251 15 +6500 566 15 +8500 866 17 +10800 1257 19 +12900 1656 21 +15000 2097 24 +18200 2865 28 +23500 4349 32 +28800 6045 36 +34100 7953 40 +41500 10913 45 +55300 17123 50 diff --git a/demo/badinventory b/demo/badinventory new file mode 100644 index 0000000..f2396cb --- /dev/null +++ b/demo/badinventory @@ -0,0 +1,7 @@ +Item# Onhand Cost Value Description +----- ------ ---- ----- ----------- +1 3 50 150 rubber gloves +2 100 X 500 test tubes +3 -5 80 -400 clamps +4 23 19 437 plates +5 -99 24 -2376 cleaning cloth diff --git a/demo/carexpense b/demo/carexpense new file mode 100644 index 0000000..b1e1752 --- /dev/null +++ b/demo/carexpense @@ -0,0 +1,5 @@ +Date Amount Account Ref Description +---- ------ ------- --- ----------- +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate +820114 93.00 car c car registration dmv diff --git a/demo/cash b/demo/cash new file mode 100644 index 0000000..91ec2f3 --- /dev/null +++ b/demo/cash @@ -0,0 +1,6 @@ +Date Amount Balance Description +---- ------ ------- ----------- +890101 512 current balance +890101 -450 rent check +890101 1000 pay check +890115 -300 estimated tax payment diff --git a/demo/colors b/demo/colors new file mode 100644 index 0000000..3825322 --- /dev/null +++ b/demo/colors @@ -0,0 +1,10 @@ +Colors +------ +black +blue +carmel +green +purple +red +silver +white diff --git a/demo/ed.t b/demo/ed.t new file mode 100644 index 0000000..43f92ab --- /dev/null +++ b/demo/ed.t @@ -0,0 +1,6 @@ +English Deutch +------- ------ +I Ich +love liebe +you dich +widgit diff --git a/demo/expense b/demo/expense new file mode 100644 index 0000000..79e9e04 --- /dev/null +++ b/demo/expense @@ -0,0 +1,5 @@ +Date Amount Account Ref Description +---- ------ ------- --- ----------- +820107 14.00 meal v meal with jones +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate diff --git a/demo/female b/demo/female new file mode 100644 index 0000000..5f657d5 --- /dev/null +++ b/demo/female @@ -0,0 +1,4 @@ +Name +---- +Jane +Sally diff --git a/demo/form b/demo/form new file mode 100644 index 0000000..6581ceb --- /dev/null +++ b/demo/form @@ -0,0 +1,6 @@ + + + +< City >, +Hi < Name >: + diff --git a/demo/inventory b/demo/inventory new file mode 100644 index 0000000..56205ad --- /dev/null +++ b/demo/inventory @@ -0,0 +1,9 @@ +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales diff --git a/demo/journal b/demo/journal new file mode 100644 index 0000000..66372f7 --- /dev/null +++ b/demo/journal @@ -0,0 +1,10 @@ +Date Account Debit Credit Description +---- ------- ----- ------ ----------- +861222 101 25000 cash from loan +861222 211.1 25000 loan number #378-14 Bank Amerigold +861223 150.1 10000 test equipment from Zarkoff +861223 101 5000 cash payment +861223 211.2 5000 note payable to Zarkoff Equipment +861224 130 30000 inventory - parts from CCPSC +861224 201.1 15000 accounts payable to CCPSC +861224 101 15000 cash payment to CCPSC for parts diff --git a/demo/maillist b/demo/maillist new file mode 100644 index 0000000..f4a28eb --- /dev/null +++ b/demo/maillist @@ -0,0 +1,19 @@ + +Number 1 +Name Ronald McDonald +Company McDonalds +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 + +Number 2 +Name Chiquita Banana +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 + diff --git a/demo/male b/demo/male new file mode 100644 index 0000000..2a9750e --- /dev/null +++ b/demo/male @@ -0,0 +1,4 @@ +Name +---- +Fred +Mike diff --git a/demo/name b/demo/name new file mode 100644 index 0000000..75f178e --- /dev/null +++ b/demo/name @@ -0,0 +1,10 @@ +Name +---- +Abraham +Abraham +Adams +Ackerman +Manis +Mann +Miller +Moon diff --git a/demo/orders b/demo/orders new file mode 100644 index 0000000..b37e931 --- /dev/null +++ b/demo/orders @@ -0,0 +1,7 @@ +Qty Model Colors Options +--- ----- ------ ------- +1 sedan black 3 +1 sedan green 1 +1 sedan farble 4 +3 sedan red 5 +1 sedan yellow 4 diff --git a/demo/owners b/demo/owners new file mode 100644 index 0000000..8a9a47e --- /dev/null +++ b/demo/owners @@ -0,0 +1,5 @@ +Item Owner +---- ----- +1 ann +3 bob +6 cy diff --git a/demo/parent b/demo/parent new file mode 100644 index 0000000..d488f7f --- /dev/null +++ b/demo/parent @@ -0,0 +1,5 @@ +Parent Child +------ ----- +Sally Fred +Mike Fred +Fred Jane diff --git a/demo/parttable b/demo/parttable new file mode 100644 index 0000000..f99732a --- /dev/null +++ b/demo/parttable @@ -0,0 +1,6 @@ +Part Subpart Count +---- ------- ----- +10001 10010 3 +10001 10020 4 +10010 10100 2 +10020 10100 5 diff --git a/demo/ruletable b/demo/ruletable new file mode 100644 index 0000000..0031aca --- /dev/null +++ b/demo/ruletable @@ -0,0 +1,3 @@ +True If +---- -- +mother(X,Y) female(X), parent(X,Y) diff --git a/demo/testall.cmds b/demo/testall.cmds new file mode 100644 index 0000000..5deba78 --- /dev/null +++ b/demo/testall.cmds @@ -0,0 +1,14 @@ +# One command per line; testall runs them in demo/ and prints the output. +# Save a known-good run: testall > testall.old ; later: testall | diff testall.old - +row 'Cost > 5' < inventory +column Cost Amount Description < inventory +sorttable -nr Cost < inventory +compute 'Value = sprintf("%5.2f", Cost * Amount)' < inventory +total Amount Value < inventory +datatype -l < inventory +subtotal Date Debit Credit < journal +jointable parent female +union expense carexpense +justify < inventory +tabletolist < inventory +julian Date < journal diff --git a/demo/testall.old b/demo/testall.old new file mode 100644 index 0000000..93281cc --- /dev/null +++ b/demo/testall.old @@ -0,0 +1,136 @@ +$ row 'Cost > 5' < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +3 5 8.00 40.00 clamps +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +$ column Cost Amount Description < inventory +Cost Amount Description +---- ------ ----------- +5.00 3 rubber gloves +0.50 100 test tubes +8.00 5 clamps +1.98 23 plates +2.45 99 cleaning cloth +14.75 89 bunsen burners +175.00 5 scales +$ sorttable -nr Cost < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +7 5 175.00 875.00 scales +6 89 14.75 1312.75 bunsen burners +3 5 8.00 40.00 clamps +1 3 5.00 15.00 rubber gloves +5 99 2.45 242.55 cleaning cloth +4 23 1.98 45.54 plates +2 100 0.50 50.00 test tubes +$ compute 'Value = sprintf("%5.2f", Cost * Amount)' < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +$ total Amount Value < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- + 324 2580.84 +$ datatype -l < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +---- ------ ---- ----- ----------- +0 0 2 2 -1 +$ subtotal Date Debit Credit < journal +Date Account Debit Credit Description +---- ------- ----- ------ ----------- +861222 25000 25000 +861223 10000 10000 +861224 30000 30000 +$ jointable parent female +Parent Child +------ ----- +Sally Fred +$ union expense carexpense +Date Amount Account Ref Description +---- ------ ------- --- ----------- +820107 14.00 meal v meal with jones +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate +820114 93.00 car c car registration dmv +$ justify < inventory +Item Amount Cost Value Description +---- ------ ------ ------- -------------- + 1 3 5.00 15.00 rubber gloves + 2 100 0.50 50.00 test tubes + 3 5 8.00 40.00 clamps + 4 23 1.98 45.54 plates + 5 99 2.45 242.55 cleaning cloth + 6 89 14.75 1312.75 bunsen burners + 7 5 175.00 875.00 scales +$ tabletolist < inventory + +Item 1 +Amount 3 +Cost 5.00 +Value 15.00 +Description rubber gloves + +Item 2 +Amount 100 +Cost 0.50 +Value 50.00 +Description test tubes + +Item 3 +Amount 5 +Cost 8.00 +Value 40.00 +Description clamps + +Item 4 +Amount 23 +Cost 1.98 +Value 45.54 +Description plates + +Item 5 +Amount 99 +Cost 2.45 +Value 242.55 +Description cleaning cloth + +Item 6 +Amount 89 +Cost 14.75 +Value 1312.75 +Description bunsen burners + +Item 7 +Amount 5 +Cost 175.00 +Value 875.00 +Description scales + +$ julian Date < journal +Date Account Debit Credit Description +---- ------- ----- ------ ----------- +1752827 101 25000 cash from loan +1752827 211.1 25000 loan number #378-14 Bank Amerigold +1752828 150.1 10000 test equipment from Zarkoff +1752828 101 5000 cash payment +1752828 211.2 5000 note payable to Zarkoff Equipment +1752829 130 30000 inventory - parts from CCPSC +1752829 201.1 15000 accounts payable to CCPSC +1752829 101 15000 cash payment to CCPSC for parts diff --git a/lib/rdb.rb b/lib/rdb.rb new file mode 100644 index 0000000..c274c71 --- /dev/null +++ b/lib/rdb.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +# A Ruby re-creation of /rdb, the relational database tools Mark Williams +# Company sold for COHERENT (~1991). + +require 'open3' +require 'date' +require 'tmpdir' +require 'fileutils' + +module Rdb + ROOT = File.expand_path('..', __dir__) + NUM = /\A\s*[+-]?(\d+\.?\d*|\.\d+)\s*\z/ + + Command = Struct.new(:name, :desc, :syntax, :block) + COMMANDS = {} + + class << self + def die(msg) + abort "#{File.basename($PROGRAM_NAME)}: #{msg}" + end + + # register 'name', 'description', 'syntax' do |args| ... end + def register(name, desc, syntax = nil, &block) + COMMANDS[name] = Command.new(name, desc, syntax || name, block) + end + + def alias_for(name, target) + cmd = COMMANDS.fetch(target) + COMMANDS[name] = Command.new(name, cmd.desc, cmd.syntax.sub(target, name), cmd.block) + end + + def run(name, args) + cmd = COMMANDS[name] or die("unknown command '#{name}'") + Env.new(name).instance_exec(args.dup, &cmd.block) + end + end +end + +require_relative 'rdb/table' +require_relative 'rdb/env' +Dir[File.join(__dir__, 'rdb/cmd/*.rb')].sort.each { |f| require f } diff --git a/lib/rdb/cmd/access.rb b/lib/rdb/cmd/access.rb new file mode 100644 index 0000000..c196015 --- /dev/null +++ b/lib/rdb/cmd/access.rb @@ -0,0 +1,479 @@ +# frozen_string_literal: true + +# Finding, adding, changing and locking records. Many of these take the name +# of the table instead of reading it on stdin, because they change the file. + +require_relative '../fast' + +# Parse the common fast-access options. Stops at the first non-option, which +# is left in args; returns the option hash. +def fast_opts(args) + o = { m: 's', h: false, l: false, s: false, x: false, exact: false, a: false, v: false, o: nil } + until args.empty? + a = args.first + case a + when /\A-m([bhirs]?)\z/ then o[:m] = Regexp.last_match(1).empty? ? 's' : Regexp.last_match(1) + when '+x' then o[:exact] = true + when '-o' then args.shift; o[:o] = args.first + when /\A-[hslxav]+\z/ then a[1..].each_char { |c| o[c.to_sym] = true } + else break + end + args.shift + end + o +end + +def need_index(table, method) + suffix = Rdb::Fast::SUFFIX[method] + return unless suffix && !File.exist?(table + suffix) + + Rdb.die("no index #{table}#{suffix}: run index -m#{method} #{table}") +end + +def each_key(ncols) + if $stdin.tty? + $stdin.each_line { |l| yield l.chomp.split("\t", -1).first(ncols) } + else + Rdb::Fast.read_keys($stdin, ncols).each { |k| yield k } + end +end + +Rdb.register('index', 'Set up table for search', 'index [-m[bhirs]] [-x] [-l] tableorlist [keycol ...]') do |args| + o = fast_opts(args) + table = args.shift or die 'usage: index [-m[bhirs]] table [keycolumn]' + data = Rdb::Fast::Data.new(table) + key = args.first || data.head[0] + data.column(key) + Rdb::Fast.build(table, o[:m], key, fold: o[:x]) + if o[:l] + Rdb::Fast::Data.new(table).rows.each { |r| warn "#{r.from} #{r.to} 0 0" } + end +end + +Rdb.register('search', 'Search a table', + 'search [-m[bhirs]] [-hsnx +x] [-l] tableorlist [keycolumn ...] [< keytable]') do |args| + o = fast_opts(args) + table = args.shift or die 'usage: search [-m[bhirs]] table [keycolumn ...] < keys' + data = Rdb::Fast::Data.new(table) + need_index(table, o[:m]) + keycols = args.empty? ? [data.head[0]] : args + puts data.head.join("\t"), Rdb::Table.dash_row(data.head) unless o[:h] + each_key(keycols.size) do |key| + hits = Rdb::Fast.search(data, o[:m], keycols, key, first: o[:s] || o[:l], partial: o[:x], exact: o[:exact]) + hits.each do |h| + puts h.row.line + warn "#{h.row.from} #{h.row.to} #{h.xfrom} #{h.xto}" if o[:l] + end + $stdout.flush + end +end + +Rdb.register('seek', 'Return the beginning and ending offset of a row', 'seek [-m[bihrs]] [-o outfile] tableorlist [keycol...] < file') do |args| + o = fast_opts(args) + table = args.shift or die 'usage: seek [-m] [-o outfile] table [keycolumn ...]' + data = Rdb::Fast::Data.new(table) + need_index(table, o[:m]) + keycols = args.empty? ? [data.head[0]] : args + each_key(keycols.size) do |key| + Rdb::Fast.search(data, o[:m], keycols, key, first: true).each do |h| + puts "#{h.row.from} #{h.row.to} #{h.xfrom} #{h.xto}" + if o[:o] + File.write(o[:o], data.head.join("\t") + "\n" + Rdb::Table.dash_row(data.head) + "\n" + h.row.line + "\n") + end + end + end +end + +Rdb.register('append', 'Add a row to a table and update index tables', 'append [-h -m[bhirs]] table [keycolumn ...] < tableorrow') do |args| + o = fast_opts(args) + table = args.shift or die 'usage: append [-h] [-m] table [keycolumn] < tableorrow' + data = Rdb::Fast::Data.new(table) + need_index(table, o[:m]) + keycol = args.first || data.head[0] + text = $stdin.read + rows = + if o[:h] then text.lines.map { |l| l.chomp("\n").split("\t", -1) } + else Rdb::Table.parse(text).rows + end + rows.each { |cells| Rdb::Fast.append_row(table, cells, o[:m], keycol) } +end + +Rdb.register('delete', 'Blank record and update index file', 'delete [-m[bhirs]] table keycolumn ... < keytable') do |args| + o = fast_opts(args) + table = args.shift or die 'usage: delete [-m] table keycolumn ... < keytable' + need_index(table, o[:m]) + keycols = args.empty? ? [Rdb::Fast::Data.new(table).head[0]] : args + each_key(keycols.size) do |key| + data = Rdb::Fast::Data.new(table) + Rdb::Fast.search(data, o[:m], keycols, key).each { |h| Rdb::Fast.delete_hit(table, h, o[:m]) } + end +end + +Rdb.register('replace', 'Insert a record into a file at specified location', + 'replace [-a] [-m[bhirs]] tableorlist from to [xfrom xto] < intableorlist') do |args| + o = fast_opts(args) + table = args.shift or die 'usage: replace [-a] [-m] table from to [xfrom xto] < row' + from, to, xfrom, xto = args.map(&:to_i) + die 'usage: replace [-a] [-m] table from to [xfrom xto] < row' unless to + + text = $stdin.read + lines = text.lines.map { |l| l.chomp("\n") } + lines = lines.drop(2) if lines.size >= 2 && Rdb::Table.dash_line?(lines[1].split("\t", -1)) + die 'replace: no input row' if lines.empty? + + keycol = Rdb::Fast::Data.new(table).head[0] + newfrom = Rdb::Fast.replace_row(table, from, to, lines.first.split("\t", -1), o[:m], xfrom.to_i, xto.to_i, keycol, + allow_append: !o[:a]) + die 'replace: record too big for its slot (-a)' unless newfrom +end + +Rdb.register('enter', 'Add rows to a table or list file without an editor', 'enter [-limit] tableorlist') do |args| + limit = args.first&.match?(/\A-\d+\z/) ? args.shift[1..].to_i : nil + file = args.first or die 'usage: enter [-limit] tableorlist' + t = Rdb::Table.open(file) + count = 0 + loop do + break if limit && count >= limit + + vals = [] + i = 0 + eof = false + while i < t.head.size + $stdout.print "#{t.head[i]}: " + $stdout.flush + line = $stdin.gets + if line.nil? + eof = true + break + end + line = line.chomp + if line == '^' + i = [i - 1, 0].max + next + end + vals[i] = line + i += 1 + end + break if eof || vals.first.to_s.empty? + + if t.list + File.open(file, 'a') { |f| t.head.each_with_index { |h, j| f.puts "#{h}\t#{vals[j]}" }; f.puts } + else + Rdb::Fast.append_row(file, vals, 's', t.head[0]) + end + count += 1 + end +end + +# ---- locks ------------------------------------------------------------------- + +def lock_file(name) + File.join(Dir.tmpdir, "L#{File.basename(name)}") +end + +Rdb.register('lock', 'Lock a record or field of a file', 'lock filename processid from to indexfrom indexto') do |args| + die 'usage: lock filename processid from to indexfrom indexto' if args.size < 4 + name, pid, from, to = args + lf = lock_file(name) + held = File.exist?(lf) ? File.readlines(lf, chomp: true).map { |l| l.split.map(&:to_i) } : [] + clash = held.find { |p, f, t| p != pid.to_i && f < to.to_i && from.to_i < t } + if clash + warn "lock: #{name}: bytes #{from}-#{to} are locked by process #{clash[0]}" + exit 1 + end + File.open(lf, 'a') { |f| f.puts args.drop(1).join(' ') } +end + +Rdb.register('unlock', 'Unlock a record or field of a file', 'unlock tableorlist processid from to indexfrom indexto') do |args| + die 'usage: unlock filename processid from to indexfrom indexto' if args.size < 4 + name = args.shift + lf = lock_file(name) + next unless File.exist?(lf) + + keep = File.readlines(lf, chomp: true).reject { |l| l.split.first(3) == args.first(3) } + keep.empty? ? File.delete(lf) : File.write(lf, keep.join("\n") + "\n") +end + +Rdb.register('vilock', 'Lock a table before editing, unlock afterward', 'vilock tableorlist') do |args| + file = args.first or die 'usage: vilock tableorlist' + locked = File.join(File.dirname(file), "LOCK#{File.basename(file)}") + die "#{file} is locked or missing" if File.exist?(locked) || !File.exist?(file) + + File.rename(file, locked) + begin + system(ENV['EDITOR'] || 'vi', locked) + ensure + File.rename(locked, file) + end +end + +# ---- interactive editing -------------------------------------------------------- + +def show_record(data_head, cells) + data_head.each_with_index { |h, i| puts "#{i + 1} #{h}\t#{cells[i].to_s.strip}" } +end + +def edit_cells(head, cells) + require 'tempfile' + Tempfile.create('rdb-edit') do |f| + f.write Rdb::Table.new(head, [cells], list: true).to_s + f.flush + system(ENV['EDITOR'] || 'vi', f.path) + t = Rdb::Table.parse(File.read(f.path)) + return nil if t.rows.empty? + + head.map { |h| (j = t.head.index(h)) ? t.rows[0][j] : '' } + end +end + +Rdb.register('update.rdb', 'Display and edit records in any sized file', + 'update.rdb [-l -m[bhirs] -v] tableorlist [keycolumn ... ]') do |args| + o = fast_opts(args) + table = args.shift or die 'usage: update.rdb [-l -m -v] table [keycolumn]' + need_index(table, o[:m]) + keycols = args.empty? ? [Rdb::Fast::Data.new(table).head[0]] : args + cur = nil + help = lambda do + puts ' or n next record p or - previous record', + ' number go to that record /pattern next record matching pattern', + ' e edit this record d delete this record', + ' anything else look up by key q or CTRL-d quit' + end + loop do + data = Rdb::Fast::Data.new(table) + rows = Rdb::Fast.live_rows(data) + print '> ' + $stdout.flush + line = $stdin.gets or break + cmd = line.strip + case cmd + when '', /\An/i then cur = cur ? cur + 1 : 0 + when /\A-/, /\Ap/i then cur = [(cur || 1) - 1, 0].max + when /\Aq/i then break + when /\Ah/i then help.call; next + when /\A\d+\z/ then cur = cmd.to_i - 1 + when %r{\A/(.*)} + re = Regexp.new(Regexp.last_match(1), Regexp::IGNORECASE) + found = rows.each_index.find { |i| i > (cur || -1) && rows[i].line.match?(re) } + found ? cur = found : (puts 'not found'; next) + when /\Ae/i + next puts('no current record') unless cur && rows[cur] + + r = rows[cur] + cells = r.cells + Rdb::Fast.splice(table, r.from, r.to, Rdb::Fast.blanked(r.line) + "\n") unless o[:l] + new = edit_cells(data.head, cells.map(&:strip)) + if new + hit = Rdb::Fast::Hit.new(r, 0, 0) + xf, xt = 0, 0 + if Rdb::Fast::SUFFIX[o[:m]] + h = Rdb::Fast.search(Rdb::Fast::Data.new(table), o[:m], keycols, cells.values_at(*keycols.map { |c| data.column(c) }), first: true).first + xf, xt = h.xfrom, h.xto if h + end + Rdb::Fast.replace_row(table, hit.row.from, hit.row.to, new, o[:m], xf, xt, keycols[0]) + puts 'record replaced' + else + Rdb::Fast.splice(table, r.from, r.to, r.line + "\n") + puts 'unchanged' + end + next + when /\Ad/i + next puts('no current record') unless cur && rows[cur] + + h = Rdb::Fast::Hit.new(rows[cur], 0, 0) + if Rdb::Fast::SUFFIX[o[:m]] + h = Rdb::Fast.search(data, o[:m], keycols, rows[cur].cells.values_at(*keycols.map { |c| data.column(c) }), first: true).first || h + end + Rdb::Fast.delete_hit(table, h, o[:m]) + puts 'record deleted' + next + else + key = cmd.split("\t", -1) + hit = Rdb::Fast.search(data, o[:m], keycols, key, first: true).first + next puts('not found') unless hit + + cur = rows.index { |r| r.from == hit.row.from } + end + if cur && rows[cur] + puts "-- record #{cur + 1} of #{rows.size}" + show_record(data.head, rows[cur].cells) + else + puts 'no such record' + cur = nil + end + end +end + +Rdb.register('ve', 'Visually edit a table', 've [table [-n[n] -fc -mc -i -s [file] -h [file] -a [file] -v [file]]] [-b]') do |args| + file = args.shift or die 'usage: ve table (a plain prompt-driven editor; see update.rdb for fast access)' + auto = args.find { |a| a.match?(/\A-n\d*\z/) } + unless File.exist?(file) + head = Rdb::Table.new(%w[Item], []).head + print 'Column names for the new table, tab separated: ' + head = ($stdin.gets || '').chomp.split("\t") + File.write(file, Rdb::Table.new(head, []).to_s) + end + cur = 0 + loop do + t = Rdb::Table.open(file) + rows = t.rows + puts "-- #{file}: record #{rows.empty? ? 0 : cur + 1} of #{rows.size}" + show_record(t.head, rows[cur]) if rows[cur] + print '[n]ext [p]rev [g]o N [e]dit [a]dd [d]elete [q]uit > ' + $stdout.flush + line = $stdin.gets or break + case line.strip + when 'n', '' then cur = [cur + 1, rows.size - 1].min + when 'p' then cur = [cur - 1, 0].max + when /\Ag\s*(\d+)/ then cur = [[Regexp.last_match(1).to_i - 1, 0].max, rows.size - 1].min + when 'e' + next unless rows[cur] + + puts 'field number, then the new value:' + print 'field> ' + f = ($stdin.gets || '').to_i - 1 + next puts('no such field') unless (0...t.head.size).cover?(f) + + print "#{t.head[f]}> " + rows[cur][f] = ($stdin.gets || '').chomp + File.write(file, t.to_s) + when 'a' + row = t.head.each_with_index.map do |h, i| + if i.zero? && auto + start = auto[2..].to_i + next ([rows.map { |r| r[0].to_i }.max.to_i + 1, start].max).to_s + end + print "#{h}> " + ($stdin.gets || '').chomp + end + rows << row + File.write(file, t.to_s) + cur = rows.size - 1 + when 'd' + rows.delete_at(cur) if rows[cur] + File.write(file, t.to_s) + cur = [cur, rows.size - 1].min + cur = 0 if cur.negative? + when 'q' then break + end + end +end + +# ---- checking data ------------------------------------------------------------ + +Rdb.register('domain', 'Display invalid values in a column', 'domain domaintable [string ...] [< one-column-table]') do |args| + dom = args.shift or die 'usage: domain domaintable [string ...]' + valid = open_table(dom).rows.map { |r| r[0].strip }.to_h { |v| [v, true] } + if args.any? + args.reject { |s| valid[s.strip] }.each { |s| puts s } + else + t = table_in + t.with(rows: t.rows.reject { |r| valid[r[0].strip] }).write + end +end + +Rdb.register('validate', 'Find invalid data', "validate 'pattern { action } ...' < tableorlist") do |args| + die "usage: validate 'pattern { action } ...' < table" unless args.size == 1 + t = table_in + puts t.head.join("\t"), Rdb::Table.dash_row(t.head) + prog = "BEGIN{OFS=\"\\t\"} #{to_awk(args[0], t.head)} {print}" + out, ok = awk_text(prog, t.rows) + $stdout.write out + exit 1 unless ok +end + +Rdb.register('fd', 'Test for functional dependency of columns', 'fd determinecolumn dependcolumn < table') do |args| + die 'usage: fd determinecolumn dependcolumn < table' unless args.size == 2 + t = table_in + a = t.index_of(args[0]) + b = t.index_of(args[1]) + seen = {} + ok = t.rows.all? { |r| (seen[r[a]] ||= r[b]) == r[b] } + puts ok ? 'true' : 'false' + exit(ok ? 0 : 1) +end + +Rdb.register('searchtree', 'Seek a string node in tree table', 'searchtree [-m[bhirs]] table root goal column1 column2') do |args| + fast_opts(args) + die 'usage: searchtree table root goal column1 column2' unless args.size == 5 + file, root, goal, c1, c2 = args + t = Rdb::Table.open(file) + i1 = t.index_of(c1) + i2 = t.index_of(c2) + seen = { root => true } + queue = [root] + found = root == goal + until found || queue.empty? + node = queue.shift + t.rows.each do |r| + next unless r[i1].strip == node + + child = r[i2].strip + next if seen[child] + + seen[child] = true + found ||= child == goal + queue << child + end + end + puts found ? 'TRUE' : 'FALSE' + exit(found ? 0 : 1) +end + +Rdb.register('vindex', 'Create and display ve look-up tables', 'vindex table [ key-column [: decode-column ] ... ]') do |args| + file = args.shift or die 'usage: vindex table [key-column [: decode-column] ...]' + t = Rdb::Table.open(file) + if args.empty? + Dir["#{File.dirname(file)}/*-[AB]"].sort.each { |f| puts "== #{File.basename(f)}"; $stdout.write File.read(f) } + next + end + until args.empty? + key = args.shift + decode = nil + if args.first == ':' + args.shift + decode = args.shift + end + k = t.index_of(key) + prefix = key.tr(' ', '_')[0, 14] + vals = t.rows.map { |r| [r[k].strip, decode ? r[t.index_of(decode)].strip : nil] }.reject { |v, _| v.empty? }.uniq.sort + File.write(File.join(File.dirname(file), "#{prefix}-A"), Rdb::Table.new([key], vals.map { |v, _| [v] }.uniq).to_s) + File.write(File.join(File.dirname(file), "#{prefix}-B"), Rdb::Table.new([key, decode], vals.map { |v, d| [v, d] }).to_s) if decode + end +end + +# ---- exercising the fast-access methods --------------------------------------------- + +def sample_table(path, n = 2000) + rows = (1..n).map { |i| [i.to_s, "name#{i * 7919 % n}", "city#{i % 97}"] } + File.write(path, Rdb::Table.new(%w[Id Name City], rows).to_s) +end + +def time_methods(n = 2000) + Dir.mktmpdir do |dir| + path = File.join(dir, 'unixtable') + sample_table(path, n) + %w[s b h i r].each do |m| + t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) + Rdb::Fast.build(path, m, 'Name') unless m == 's' + t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC) + data = Rdb::Fast::Data.new(path) + hits = 0 + (1..200).each do |i| + key = m == 'r' ? i.to_s : "name#{i * 13 % n}" + hits += Rdb::Fast.search(data, m, ['Name'], key).size + end + t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC) + yield m, t1 - t0, t2 - t1, hits + end + end +end + +Rdb.register('timesearch', 'Time the fast-access methods', 'timesearch [ > timesearch.new ]') do |_args| + puts 'method index(s) 200 searches(s) rows found' + time_methods { |m, ti, ts, hits| puts format('-m%-5s %8.4f %15.4f %10d', m, ti, ts, hits) } +end + +Rdb.register('testsearch', 'Test the fast-access methods', 'testsearch') do |_args| + time_methods(500) { |m, _ti, _ts, hits| puts "-m#{m}: #{hits.positive? ? 'ok' : 'FAILED'} (#{hits} rows found)" } +end diff --git a/lib/rdb/cmd/act.rb b/lib/rdb/cmd/act.rb new file mode 100644 index 0000000..678664b --- /dev/null +++ b/lib/rdb/cmd/act.rb @@ -0,0 +1,409 @@ +# frozen_string_literal: true + +# The /act accounting package: general ledger, payroll, inventory, sales. +# Like the original, it is an example of data-base programming with /rdb, not +# an accounting package: each command reads and writes plain /rdb tables in +# the current directory. + +ACT_COMMANDS = %w[act adjust balance bom calcpay calculate cashflow chartdup close consolidate cstate explode + fillform foot getjournal invoice sale tax update.inv w2].freeze + +def num(str) + str.to_s.strip.empty? ? 0.0 : str.to_s.strip.to_f +end + +def money(val) + val == val.to_i ? val.to_i.to_s : format('%.2f', val) +end + +def need(file) + File.exist?(file) || Rdb.die("#{file}: no such table (the /act commands work on tables in the current directory)") + Rdb::Table.open(file) +end + +def next_date + ENV['NEXTDATE'] || Date.today.strftime('%y%m%d') +end + +def account_key(acct) + [acct.to_f, acct] +end + +Rdb.register('act', 'List all /act commands', 'act') do |_args| + names = ACT_COMMANDS.sort + w = names.map(&:size).max + 2 + names.each_slice(5) { |row| puts row.map { |n| n.ljust(w) }.join.rstrip } +end + +Rdb.register('chartdup', 'Check for duplicate names and accounts in chart', 'chartdup') do |_args| + t = need('chart') + { 'Account' => 'Account numbers', 'Short' => 'Short names' }.each do |col, label| + next unless (i = t.head.index(col)) + + dups = t.rows.group_by { |r| r[i] }.select { |_, v| v.size > 1 } + next if dups.empty? + + puts "Duplicate #{label} in chart. See chart.#{col}" + dups.each_value { |rows| rows.each { |r| puts r.join("\t") } } + end +end + +Rdb.register('foot', 'Foot or subtotal Debits and Credit of ledger', 'foot') do |_args| + t = need('ledger') + d = t.index_of('Debit') + c = t.index_of('Credit') + out = [] + t.rows.sort_by { |r| account_key(r[0]) }.group_by { |r| r[0] }.each_value do |rows| + out.concat(rows) + out << t.head.map { |h| '-' * [h.size, 1].max } + sub = [''] * t.head.size + sub[0] = rows.first[0] + sub[d] = money(rows.sum { |r| num(r[d]) }) + sub[c] = money(rows.sum { |r| num(r[c]) }) + out << sub + end + t.with(rows: out).write +end + +Rdb.register('adjust', 'Create adjusted trial balance table', 'adjust') do |_args| + tb = need('trialbalance') + ja = need('journaladjust') + meta = {} + (tb.rows.map { |r| tb.head.zip(r).to_h } + ja.rows.map { |r| ja.head.zip(r).to_h }).each do |h| + m = (meta[h['Account']] ||= {}) + h.each { |k, v| m[k] = v if !v.to_s.strip.empty? && !%w[Debit Credit].include?(k) && !m.key?(k) } + end + net = Hash.new(0.0) + [tb, ja].each do |t| + d = t.index_of('Debit') + c = t.index_of('Credit') + t.rows.each { |r| net[r[0]] += num(r[d]) - num(r[c]) } + end + head = tb.head + rows = net.keys.sort_by { |a| account_key(a) }.map do |acct| + n = net[acct] + head.map do |h| + case h + when 'Debit' then n >= 0 ? money(n) : '' + when 'Credit' then n.negative? ? money(-n) : '' + else meta[acct][h].to_s + end + end + end + File.write('adjustedtrial', Rdb::Table.new(head, rows).to_s) +end + +def trial_parts(t) + h = t.head + fmt = h.index('Format') + rows = t.rows.map { |r| { acct: r[0], debit: num(r[h.index('Debit')]), credit: num(r[h.index('Credit')]), fmt: r[fmt].to_s, name: r[h.index('Name')].to_s, row: r } } + rows +end + +Rdb.register('balance', 'Create balance sheet from adjusted trial balance', 'balance') do |_args| + rows = trial_parts(need('adjustedtrial')) + bs = rows.select { |r| r[:fmt].start_with?('B-') } + inc = rows.select { |r| r[:fmt].start_with?('I-') } + assets = bs.select { |r| r[:fmt] == 'B-A' } + others = bs - assets + profit = inc.sum { |r| r[:credit] - r[:debit] } + company = File.exist?('company') ? File.read('company').strip : (ENV['COMPANY'] || 'Company') + out = [] + out << company.center(30).rstrip << Date.today.strftime('%-d %B %Y').center(30).rstrip << 'Balancesheet'.center(30).rstrip + assets.each { |r| out << format('%5s %s', money(r[:debit] - r[:credit]), r[:name]) } + total = assets.sum { |r| r[:debit] - r[:credit] } + out << "#{'-' * 5} #{'-' * 24}" << format('%5s Total Assets', money(total)) + others.each { |r| out << format('%6s %s', money(r[:credit] - r[:debit]), r[:name]) } + out << format('%6s Retained Earnings (Profit)', money(profit)) + out << "#{'-' * 6} #{'-' * 26}" + out << format('%6s Total Liabilities and Equity', money(others.sum { |r| r[:credit] - r[:debit] } + profit)) + File.write('balancesheet', out.join("\n") + "\n") +end + +Rdb.register('close', 'Close accounting period creating journal for next', 'close') do |_args| + rows = trial_parts(need('adjustedtrial')).select { |r| r[:fmt].start_with?('B-') } + date = next_date + out = rows.map { |r| [r[:acct], date, r[:debit].zero? ? '' : money(r[:debit]), r[:credit].zero? ? '' : money(r[:credit]), 'jl', 'brought forward'] } + net = rows.sum { |r| r[:debit] - r[:credit] } + out << ['4997', date, net.negative? ? money(-net) : '', net.negative? ? '' : money(net), 'jl', 'brought forward'] + File.write('journalnext', Rdb::Table.new(%w[Account Date Debit Credit Ref Description], out.map { |r| r }).to_s) +end + +Rdb.register('getjournal', 'Copy journals for sales, purchasing, pay systems', 'getjournal journalname') do |args| + die 'usage: getjournal journalname' unless args.size == 1 + t = need(args[0]) + a = t.index_of('Amount') + total = t.rows.sum { |r| num(r[a]) } + Rdb::Table.new(%w[Date Amount Account Ref Description], + [[next_date, format('%.2f', total), '', t.rows.first&.fetch(t.head.index('Ref') || 0, ''), "#{args[0].delete_prefix('journal')} from #{args[0]}"]]).write +end + +Rdb.register('consolidate', 'Combine all subsidiary journals to general journal', 'consolidate nextdate') do |args| + die 'usage: consolidate nextdate' unless args.size == 1 + skip = %w[journal journaladjust journalnext journallast journalpay] + files = Dir['journal*'].reject { |f| skip.include?(f) || f.include?('.') }.sort + head = %w[Date Account Debit Credit Ref Description] + rows = [] + files.each do |f| + t = Rdb::Table.open(f) + debit_journal = f.match?(/expense|pur|pay/) + t.rows.each do |r| + h = t.head.zip(r).to_h + if t.head.include?('Amount') + if h['Account'].to_s.strip.empty? + warn "consolidate: #{f}: row without an Account skipped" + next + end + amount = money(num(h['Amount']).abs) + h['Debit'], h['Credit'] = debit_journal ? [amount, ''] : ['', amount] + end + rows << head.map { |c| h[c].to_s } + end + end + d = rows.sum { |r| num(r[2]) } + c = rows.sum { |r| num(r[3]) } + rows << [args[0], '1010', '', money(d), '', 'double-entry'] unless d.zero? + rows << [args[0], '1010', money(c), '', '', 'double-entry'] unless c.zero? + File.write('journal', Rdb::Table.new(head, rows).to_s) +end + +Rdb.register('fillform', 'Fill a tax from with adjusted trial balance data', 'fillform form') do |args| + die 'usage: fillform form' unless args.size == 1 + lines = need("taxform.#{args[0]}") + trial = need('adjustedtrial') + ti = trial.index_of('Taxline') + sums = Hash.new(0.0) + trial.rows.each { |r| sums[r[ti]] += (num(r[trial.index_of('Debit')]) - num(r[trial.index_of('Credit')])).abs } + rows = lines.rows.map { |r| [r[0], money(sums[r[0]]), r[1].to_s] } + File.write(args[0], Rdb::Table.new(%w[Taxline Amount Description], rows).to_s) +end + +Rdb.register('calculate', 'Compute each tax form listed', 'calculate taxform ...') do |args| + die 'usage: calculate taxform ...' if args.empty? + args.each do |form| + t = need(form) + script = "#{form}.calc" + File.exist?(script) or die("#{script}: missing (one 'Column = expression' per line)") + prog = "BEGIN{OFS=\"\\t\"} { #{to_awk(File.readlines(script, chomp: true).join('; '), t.head)}; print }" + File.write(form, t.with(rows: awk(prog, t.rows)).to_s) + end +end + +Rdb.register('tax', 'Compute tax from income and tax table', 'tax income < taxtable') do |args| + die 'usage: tax income < taxtable' unless args.size == 1 + income = args[0].to_f + rows = table_in.rows.map { |r| r.map { |c| num(c) } }.sort_by(&:first) + row = rows.reverse.find { |r| r[0] <= income } + puts format('%7d', row ? (row[1] + (income - row[0]) * row[2] / 100.0).round : 0) +end + +Rdb.register('cashflow', 'Compute balance column of cash table', 'cashflow < cashtable') do |_args| + t = table_in + a = t.index_of('Amount') + b = t.index_of('Balance') + bal = 0.0 + rows = t.rows.map do |r| + r = r.dup + if r[a].strip.empty? && !r[b].strip.empty? + bal = num(r[b]) + r[a] = money(bal) + else + bal += num(r[a]) + end + r[b] = money(bal) + r + end + t.with(rows: rows).write +end + +# ---- payroll ------------------------------------------------------------------------- + +Rdb.register('calcpay', 'Post payroll to ledgerpay', 'calcpay') do |_args| + t = need('journalpay') + n = t.index_of('Number') + File.write('ledgerpay', t.with(rows: t.rows.each_with_index.sort_by { |r, i| [r[n].to_f, i] }.map(&:first)).to_s) +end + +Rdb.register('w2', 'Produce W-2 totals per employee from ledgerpay', 'w2') do |_args| + t = need('ledgerpay') + n = t.index_of('Number') + cols = %w[Gross Federal State Net].select { |c| t.head.include?(c) } + rows = t.rows.group_by { |r| r[n] }.map do |emp, rs| + [emp] + cols.map { |c| money(rs.sum { |r| num(r[t.index_of(c)]) }) } + end + Rdb::Table.new(['Number'] + cols, rows).write +end + +# ---- operations ----------------------------------------------------------------------- + +Rdb.register('bom', 'Produce bill-of-materials from parts list', 'bom') do |_args| + part = need('part') + items = need('saleitem') + qty = Hash.new(0.0) + items.rows.each { |r| qty[r[items.index_of('Code')]] += num(r[items.index_of('Qty')]) } + need_total = Hash.new(0.0) + part.rows.each do |r| + code = r[part.index_of('Code')] + need_total[r[part.index_of('Subpart')]] += qty[code] * num(r[part.index_of('Count')]) if qty.key?(code) + end + File.write('bom', Rdb::Table.new(%w[Code Qty], need_total.sort.map { |k, v| [k, money(v)] }).to_s) +end + +Rdb.register('explode', 'Produce table of subparts and their count for a part', 'explode Part Amount [ table ]') do |args| + die 'usage: explode Part Amount [table]' if args.size < 2 + t = need(args[2] || 'parttable') + totals = Hash.new(0.0) + queue = [[args[0], args[1].to_f]] + until queue.empty? + part, amount = queue.shift + totals[part] += amount + t.rows.each { |r| queue << [r[1], amount * num(r[2])] if r[0] == part } + end + Rdb::Table.new(%w[Part Amount], totals.sort_by { |k, _| [k.to_f, k] }.map { |k, v| [k, money(v)] }).write +end + +# ---- sales ------------------------------------------------------------------------------ + +def ask(prompt) + print prompt + $stdout.flush + ($stdin.gets || '').strip +end + +def address_block(customers, number) + c = customers.rows.find { |r| r[0] == number } + return ["Customer Number #{number}"] unless c + + h = customers.head.zip(c).to_h + ["Mailing Address for Customer Number #{number}", + [h['Title'], h['First'], h['Last'], h['Position']].compact.join(' ').strip, + h['Company'], h['Street'], [h['City'], h['State'], h['Country']].compact.join(' ')].compact.reject(&:empty?) +end + +Rdb.register('invoice', 'Print invoice for a sale order', 'invoice') do |_args| + orders = need('salesorder') + items = need('saleitem') + customers = File.exist?('customer') ? Rdb::Table.open('customer') : Rdb::Table.new(%w[Number], []) + loop do + n = ask('Please enter order number (or Return to exit): ') + break if n.empty? + + o = orders.rows.find { |r| r[0] == n } + next puts('no such order') unless o + + oh = orders.head.zip(o).to_h + puts ' Invoice', ' -------' + puts ENV['COMPANY'] || 'Company' + puts "Order Number #{n}", "Date Ordered #{oh['Date']}", "Date Shipped #{oh['Shipped']}" + puts address_block(customers, oh['Cust']) + mine = items.rows.select { |r| r[0] == n } + items.with(rows: mine).write + puts format('%-40s Gross %.2f', '', num(oh['Gross'])) + puts format('%-40s Tax %.2f', '', num(oh['Tax'])) + puts format('%-40s Total %.2f', '', num(oh['Total'])) + end +end + +Rdb.register('cstate', 'Produce customer statement', 'cstate') do |_args| + orders = need('salesorder') + deposits = File.exist?('deposit') ? Rdb::Table.open('deposit') : Rdb::Table.new(%w[Cust Date Amount Ref], []) + customers = File.exist?('customer') ? Rdb::Table.open('customer') : Rdb::Table.new(%w[Number], []) + loop do + c = ask('Please enter customer number (or all or Return to exit): ') + break if c.empty? + + chosen = c == 'all' ? orders.rows.map { |r| r[1] }.uniq : [c] + chosen.each do |cust| + lines = orders.rows.select { |r| r[1] == cust }.map { |r| [cust, r[2], format('%.2f', -num(r[orders.index_of('Total')])), r[0]] } + + deposits.rows.select { |r| r[0] == cust }.map { |r| [cust, r[1], format('%.2f', num(r[2])), r[3]] } + balance = lines.sum { |l| num(l[2]) } + puts ' Customer Statement', ' ------------------' + puts address_block(customers, cust) + puts "Now Due and Payable is your balance of: #{format('%.2f', -balance)}" + puts 'Details of Your Orders and Payments' + Rdb::Table.new(%w[Cust Date Amount Ref], lines.sort_by { |l| l[1] }).write + puts format('%-20s %s', '', format('%.2f', balance)) + end + end +end + +Rdb.register('sale', 'Enter sale order, and item, update customer', 'sale') do |_args| + products = need('product') + customers = File.exist?('customer') ? Rdb::Table.open('customer') : Rdb::Table.new(%w[Number Company], []) + File.write('salesorder', Rdb::Table.new(%w[Number Cust Date Shipped Gross Tax Total], []).to_s) unless File.exist?('salesorder') + File.write('saleitem', Rdb::Table.new(%w[Order Number Code Backord Qty Price Total Name], []).to_s) unless File.exist?('saleitem') + loop do + cust = ask('Enter Customer Number (? for new, /string search, Return to exit) ') + break if cust.empty? + + if cust == '?' + row = customers.head.map { |h| ask("#{h}: ") } + cust = row[0] + customers.rows << row + File.write('customer', customers.to_s) + elsif cust.start_with?('/') + re = Regexp.new(cust[1..], Regexp::IGNORECASE) + customers.rows.select { |r| r.join(' ').match?(re) }.each { |r| puts r.join("\t") } + next + end + puts address_block(customers, cust) + n = Rdb::Table.open('salesorder').rows.map { |r| r[0].to_i }.max.to_i + 1 + gross = 0.0 + items = [] + loop do + code = ask('Code (Return when done): ') + break if code.empty? + + p = products.rows.find { |r| r[0] == code } + next puts('no such product') unless p + + q = ask('Qty: ').to_i + price = num(p[products.index_of('Price')]) + items << [n.to_s, (items.size + 1).to_s, code, '10', q.to_s, money(price), format('%.2f', price * q), p[products.index_of('Name')]] + gross += price * q + end + next if items.empty? + + tax = gross * 0.05 + order = [n.to_s, cust, Date.today.strftime('%y%m%d'), '0', format('%.2f', gross), format('%.2f', tax), format('%.2f', gross + tax)] + puts Rdb::Table.new(%w[Number Cust Date Shipped Gross Tax Total], [order]).to_s + if ask('Is this correct? (y, n) ').downcase.start_with?('y') + File.write('salesorder', Rdb::Table.open('salesorder').tap { |t| t.rows << order }.to_s) + File.write('saleitem', Rdb::Table.open('saleitem').tap { |t| t.rows.concat(items) }.to_s) + end + end +end + +Rdb.register('update.inv', 'Multi-user update with screen form and record locking', 'update.inv') do |_args| + file = ENV['INV'] || 'inv' + File.exist?(file) or die("#{file}: no such table (set INV to your inventory table)") + Rdb::Fast.build(file, 'i', 'Item') unless File.exist?("#{file}.i") + puts "Inventory Update #{Date.today.strftime('%a %b %-d, %Y')}" + loop do + item = ask('Item Number: (or hit Return key to exit.) ') + break if item.empty? + + data = Rdb::Fast::Data.new(file) + hit = Rdb::Fast.search(data, 'i', ['Item'], item, first: true).first + next puts('no such item') unless hit + + pid = Process.pid.to_s + unless system(File.join(Rdb::ROOT, 'bin', 'lock'), file, pid, hit.row.from.to_s, hit.row.to.to_s, hit.xfrom.to_s, hit.xto.to_s) + puts 'record is being updated by someone else' + next + end + begin + cells = hit.row.cells + h = data.head + puts h.zip(cells).map { |a, b| "#{a}=#{b.strip}" }.join(' ') + amount = ask("Amount #{h.include?('Onhand') ? 'Onhand' : 'Amount'}: ") + unless amount.empty? + cells[h.index('Onhand') || h.index('Amount')] = amount + cells[h.index('Value')] = money(num(amount) * num(cells[h.index('Cost')])) if h.include?('Value') && h.include?('Cost') + Rdb::Fast.replace_row(file, hit.row.from, hit.row.to, cells, 'i', hit.xfrom, hit.xto, 'Item') + end + ensure + system(File.join(Rdb::ROOT, 'bin', 'unlock'), file, pid, hit.row.from.to_s, hit.row.to.to_s) + end + end +end diff --git a/lib/rdb/cmd/convert.rb b/lib/rdb/cmd/convert.rb new file mode 100644 index 0000000..84fda77 --- /dev/null +++ b/lib/rdb/cmd/convert.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +# Converting tables to other formats, dates, and name matching. + +Rdb.register('tabletolist', 'Convert a table to list format', 'tabletolist [ -l ] < table') do |args| + t = table_in + text = t.with(rows: t.rows).tap { |x| x.list = true }.to_s + $stdout.write args.include?('-l') ? text.sub(/\A\n/, '') : text +end + +Rdb.register('listtotable', 'Convert from list to table format', 'listtotable [ -el ] < listfile') do |_args| + t = Rdb::Table.parse_list($stdin.read.sub(/\A(?!\n)/, "\n")) + t.list = false + t.write +end + +Rdb.register('listtosh', 'Convert list format to shell variable', 'listtosh < list') do |_args| + t = Rdb::Table.parse_list($stdin.read.sub(/\A(?!\n)/, "\n")) + t.rows.each do |r| + t.head.each_with_index { |h, i| puts "#{h.gsub(/\W/, '_')}='#{r[i].gsub("'", %q('\\\\''))}'" } + end +end + +Rdb.register('tabletofix', 'Convert /rdb table format to fixed-length format', 'tabletofix column=n[r] ... < tableorlist') do |args| + t = table_in + specs = args.map do |a| + name, w = a.split('=', 2) + die "bad column spec '#{a}'" unless w + [t.index_of(name), w.to_i, w.end_with?('r')] + end + t.rows.each do |r| + next if blank_row?(r) + + puts specs.map { |i, w, right| right ? r[i].strip[0, w].rjust(w) : r[i].strip[0, w].ljust(w) }.join + end +end + +Rdb.register('fixtotable', 'Converts fixed length format to /rdb table format', 'fixtotable column[=n1,n2] ... < fixtdb') do |args| + specs = args.map do |a| + name, pos = a.split('=', 2) + [name, pos ? pos.split(',').map(&:to_i) : nil] + end + rows = $stdin.each_line.map { |l| l.chomp("\n") }.reject { |l| l.strip.empty? }.map do |l| + specs.map { |_, pos| pos ? (l[pos[0] - 1, pos[1]] || '').strip : '' } + end + Rdb::Table.new(specs.map(&:first), rows).write +end + +Rdb.register('tabletostruct', 'Convert table to C-language struct declaration', 'tabletostruct tag name < table > table.h') do |args| + die 'usage: tabletostruct tag name < table' unless args.size == 2 + t = table_in + puts "struct #{args[0]}", '{' + t.head.each { |h| puts "char *#{h};" } + puts "} #{args[1]} [] =", '{ ' + t.rows.map { |r| r.map { |c| %("#{c.strip}") }.join(',') }.join(" },\n{ ") + ' }', ';' +end + +Rdb.register('tabletotbl', 'Convert /rdb table format to UNIX tbl/nroff format', 'tabletotbl < tablefile > tblfile') do |_args| + t = table_in + fmt = t.head.each_index.map { |i| t.rows.all? { |r| r[i].empty? || numeric?(r[i]) } ? 'n' : 'l' }.join(' ') + puts '.TS', "#{fmt}.", t.head.join("\t"), '_' + t.rows.each { |r| puts r.join("\t") } + puts '.TE' +end + +Rdb.register('tabletofact', 'Converts a table to PROLOG fact-file format', 'tabletofact table ... > prologfactfile') do |args| + die 'usage: tabletofact table ...' if args.empty? + args.each do |f| + open_table(f).rows.each { |r| puts "#{File.basename(f)}(#{r.map(&:strip).join(',')})." } + end +end + +Rdb.register('tabletorule', 'Convert a table to PROLOG rule-file format', 'tabletorule table ... > prologrulefile') do |args| + die 'usage: tabletorule table ...' if args.empty? + args.each do |f| + open_table(f).rows.each { |r| puts "#{r[0].strip} :- #{r[1].strip}." } + end +end + +# ---- dates ------------------------------------------------------------------ + +{ 'julian' => 'Convert column of dates for arithmetic and format change', + 'gregorian' => 'Convert column of dates for arithmetic and format change' }.each do |cmd, desc| + Rdb.register(cmd, desc, "#{cmd} [ -ceu ] [ Column ... ] < tableorlist") do |args| + fmt = 'c' + args.reject! { |a| a.match?(/\A-[ceu]\z/) && (fmt = a[1]) } + t = table_in + sel = args.empty? ? [0] : args.map { |a| t.index_of(a) } + rows = t.rows.map do |r| + r = r.dup + sel.each { |i| r[i] = cmd == 'julian' ? to_julian(r[i], fmt).to_s : from_julian(r[i], fmt) } + r + end + t.with(rows: rows).write + end +end + +Rdb.register('computedate', 'Add given number of days to a date', 'computedate date days') do |args| + die 'usage: computedate date days' unless args.size == 2 + puts from_julian(to_julian(args[0]) + args[1].to_i) +end + +Rdb.register('todaysdate', "Print today's date in YYMMDD format", 'todaysdate') do |_args| + puts Date.today.strftime('%y%m%d') +end + +# ---- names that sound alike ---------------------------------------------------- + +def soundex_code(word) + codes = { 'BFPV' => '1', 'CGJKQSXZ' => '2', 'DT' => '3', 'L' => '4', 'MN' => '5', 'R' => '6' } + letters = word.upcase.gsub(/[^A-Z]/, '').chars + return '' if letters.empty? + + code = ->(c) { codes.find { |k, _| k.include?(c) }&.last } + out = letters[0].dup + last = code.(letters[0]) + letters.drop(1).each do |c| + d = code.(c) + out << d if d && d != last + last = d unless 'HW'.include?(c) + break if out.size == 4 + end + out.ljust(4, '0') +end + +Rdb.register('soundex', 'Convert a name into soundex code', 'soundex < file > file.x ; index -mb file.x Soundex') do |_args| + t = table_in + Rdb::Table.new([t.head[0], 'Soundex'], t.rows.map { |r| [r[0], soundex_code(r[0])] }).write +end + +Rdb.register('like', 'Find names that sound like another name', 'like similar-name file [ name-column ]') do |args| + die 'usage: like similar-name file [name-column]' if args.size < 2 + t = open_table("#{args[1]}.x") + col = args[2] ? t.index_of(args[2]) : 0 + want = soundex_code(args[0]) + s = t.index_of('Soundex') + Rdb::Table.new([t.head[col]], t.rows.select { |r| r[s] == want }.map { |r| [r[col]] }).write +end + +Rdb.register('hashkey', 'Return the hash offset for key strings', 'hashkey rows keystring ...') do |args| + die 'usage: hashkey rows keystring ...' if args.size < 2 + puts args.drop(1).join.delete(" \t").bytes.sum % args[0].to_i +end diff --git a/lib/rdb/cmd/relational.rb b/lib/rdb/cmd/relational.rb new file mode 100644 index 0000000..caddc09 --- /dev/null +++ b/lib/rdb/cmd/relational.rb @@ -0,0 +1,341 @@ +# frozen_string_literal: true + +# Selecting, combining and summarising tables. + +Rdb.register('row', 'Make a new table where rows match logical condition', + "row 'column condition column-or-value ..' < table") do |args| + die "usage: row 'condition' < table" unless args.size == 1 + t = table_in + prog = "BEGIN{OFS=\"\\t\"} #{to_awk(args[0], t.head)} {print}" + t.with(rows: awk(prog, t.rows)).write +end +Rdb.alias_for('select', 'row') + +Rdb.register('column', 'Display columns of a table in any order', 'column [ Column ... ] < tableorlist') do |args| + t = table_in + next t.write if args.empty? + + idx = args.map { |a| t.head.index(a) } + t.with(head: args, rows: t.rows.map { |r| idx.map { |i| i ? r[i] : '' } }).write +end +Rdb.alias_for('project', 'column') + +Rdb.register('sorttable', 'Sort a table by one or more columns', + 'sorttable [-nrf] [columnname ...] < tableorlist') do |args| + t = table_in + numeric = reverse = fold = false + cols = [] + args.each do |a| + if a.match?(/\A-[nrf]+\z/) + numeric ||= a.include?('n') + reverse ||= a.include?('r') + fold ||= a.include?('f') + else + cols << t.index_of(a) + end + end + cols = (0...t.head.size).to_a if cols.empty? + key = ->(r) { cols.map { |i| numeric ? r[i].to_f : (fold ? r[i].downcase : r[i]) } } + sorted = t.rows.each_with_index.sort_by { |r, n| [key.(r), n] }.map(&:first) + sorted.reverse! if reverse + t.with(rows: sorted).write +end + +Rdb.register('compute', 'Calculate columns of a table', "compute 'column = expression [ ; ... ]' < tableorlist") do |args| + die "usage: compute 'column = expression [; ...]' < table" unless args.size == 1 + t = table_in + prog = "BEGIN{OFS=\"\\t\"} { #{to_awk(args[0], t.head)}; print }" + t.with(rows: awk(prog, t.rows)).write +end + +Rdb.register('addcol', 'Add a column to a table', 'addcol newcolumn < table') do |args| + die 'usage: addcol newcolumn ... < table' if args.empty? + t = table_in + t.with(head: t.head + args, rows: t.rows.map { |r| r + [''] * args.size }).write +end + +Rdb.register('rename', 'Rename a column', 'rename [ oldcolumn newcolumn ... ] < tableorlist') do |args| + die 'usage: rename oldcolumn newcolumn ... < table' if args.empty? || args.size.odd? + t = table_in + head = t.head.dup + args.each_slice(2) { |old, new| head[t.index_of(old)] = new } + t.with(head: head).write +end + +{ 'number' => 'number', 'Number' => 'Number' }.each do |cmd, title| + Rdb.register(cmd, 'Insert a column-row number into a table or list', "#{cmd} < tableorlist") do |_args| + t = table_in + t.with(head: [title] + t.head, rows: t.rows.each_with_index.map { |r, i| [(i + 1).to_s] + r }).write + end +end + +Rdb.register('record', 'Find and output a record from a table', 'record number < table') do |args| + die 'usage: record number < table' unless args.size == 1 + t = table_in + row = t.rows[args[0].to_i - 1] + t.with(rows: row ? [row] : []).write +end + +# ---- combining tables ------------------------------------------------------ + +Rdb.register('jointable', 'Join two tables into one where keys match', + 'jointable [-a[1|2]] [-j[1|2] column]... [-n] [-] table1 [table2]') do |args| + all1 = all2 = false + k1 = k2 = nil + files = [] + until args.empty? + a = args.shift + case a + when '-a' then all1 = all2 = true + when '-a1' then all1 = true + when '-a2' then all2 = true + when '-j' then k1 = k2 = args.shift + when '-j1' then k1 = args.shift + when '-j2' then k2 = args.shift + when '-n' then nil + else files << a + end + end + die 'usage: jointable [-a[1|2]] [-j[1|2] column] table1 [table2]' if files.empty? || files.size > 2 + + files << '-' if files.size == 1 + t1, t2 = files.map { |f| open_table(f) } + i1 = k1 ? t1.index_of(k1) : 0 + i2 = k2 ? t2.index_of(k2) : 0 + keep2 = (0...t2.head.size).to_a - [i2] + by_key = Hash.new { |h, k| h[k] = [] } + t2.rows.each { |r| by_key[r[i2]] << r } + used = {} + out = [] + t1.rows.each do |r| + if (m = by_key[r[i1]]).any? + used[r[i1]] = true + m.each { |r2| out << r + keep2.map { |i| r2[i] } } + elsif all1 + out << r + [''] * keep2.size + end + end + if all2 + t2.rows.each do |r2| + next if used[r2[i2]] + + blank = [''] * t1.head.size + blank[i1] = r2[i2] + out << blank + keep2.map { |i| r2[i] } + end + end + Rdb::Table.new(t1.head + keep2.map { |i| t2.head[i] }, out).write +end + +Rdb.register('union', 'Concatenate tables', 'union tableorlist ... [-] [< tableorlist]') do |args| + args = ['-'] if args.empty? + tabs = args.map { |a| open_table(a) } + tabs.first.with(rows: tabs.flat_map(&:rows)).write +end + +Rdb.register('intersect', 'Write table of rows that are in both input tables', 'intersect table1 table2') do |args| + die 'usage: intersect table1 table2' unless args.size == 2 + a, b = args.map { |x| open_table(x) } + seen = b.rows.to_h { |r| [r, true] } + a.with(rows: a.rows.select { |r| seen[r] }.uniq).write +end + +Rdb.register('difference', 'Output table of rows that are in only one table', 'difference table1 table2') do |args| + die 'usage: difference table1 table2' unless args.size == 2 + a, b = args.map { |x| open_table(x) } + sb = b.rows.to_h { |r| [r, true] } + a.with(rows: a.rows.reject { |r| sb[r] }.uniq).write +end + +Rdb.register('paste.rdb', 'Paste together two or more tables', 'paste.rdb table1 table2 ...') do |args| + die 'usage: paste.rdb table1 table2 ...' if args.size < 2 + tabs = args.map { |a| open_table(a) } + n = tabs.map { |t| t.rows.size }.max + head = tabs.flat_map(&:head) + rows = (0...n).map { |i| tabs.flat_map { |t| t.rows[i] || [''] * t.head.size } } + Rdb::Table.new(head, rows).write +end + +Rdb.register('splittable', 'Divide a table horizontally', 'splittable [-n] table') do |args| + n = 50 + n = args.shift[1..].to_i if args.first&.match?(/\A-\d+\z/) + die 'usage: splittable [-n] table' unless args.size == 1 + t = open_table(args[0]) + suffix = ('aa'..'zz').each + t.rows.each_slice(n) do |chunk| + File.write("#{args[0]}#{suffix.next}", t.with(rows: chunk).to_s) + end +end + +# ---- aggregates ------------------------------------------------------------ +# total, mean, maximum, minimum, width, precision and datatype are one program +# that decides what to compute from the name it was called by. + +STATS = { + 'total' => 'Sum a column', + 'mean' => 'Display the mean of a column', + 'maximum' => 'Display the maximum value in a column', + 'minimum' => 'Display the minimum of each column selected', + 'width' => 'Display the width of each column', + 'precision' => 'Display the precision of a column', + 'datatype' => 'Display the data type of each column selected' +}.freeze + +STATS.each do |cmd, desc| + Rdb.register(cmd, desc, "#{cmd} [ -l ] [ column ... ] < table") do |args| + list = args.first == '-l' + args.shift if list + t = table_in + sel = args.empty? ? (0...t.head.size).to_a : args.map { |a| t.index_of(a) } + result = Array.new(t.head.size, '') + sel.each do |i| + vals = t.rows.map { |r| r[i] } + filled = vals.reject { |v| v.strip.empty? } + nums = filled.all? { |v| numeric?(v) } ? vals.map { |v| v.strip.to_f } : nil + dec = filled.map { |v| decimals(v) }.max.to_i + result[i] = + case name + when 'width' then (vals.map(&:size) + [t.head[i].size]).max.to_s + when 'precision' then nums ? dec.to_s : '' + when 'datatype' then filled.empty? || !nums ? '-1' : dec.to_s + when 'total' then nums ? fmt(nums.sum, dec) : '' + when 'mean' then nums && !nums.empty? ? fmt(nums.sum / nums.size, [dec, 2].max) : '' + when 'maximum' then nums && !nums.empty? ? fmt(nums.max, dec) : '' + when 'minimum' then nums && !nums.empty? ? fmt(nums.min, dec) : '' + end + end + if list + $stdout.write t.to_s + puts Rdb::Table.dash_row(t.head) + puts result.join("\t") + else + Rdb::Table.new(t.head, [result]).write + end + end +end + +Rdb.register('widest', 'Output the width of the widest entries in a table', 'widest < table') do |_args| + t = table_in + res = t.head.each_index.map { |i| (t.rows.map { |r| r[i].size } + [t.head[i].size]).max.to_s } + Rdb::Table.new(t.head, [res]).write +end + +Rdb.register('subtotal', 'Output subtotals of columns in a table', + 'subtotal [ -l ] [ break-column column ... ] < table') do |args| + list = args.first == '-l' + args.shift if list + t = table_in + brk = args.empty? ? 0 : t.index_of(args.shift) + sel = args.empty? ? (0...t.head.size).to_a - [brk] : args.map { |a| t.index_of(a) } + out = [] + group = [] + flush = lambda do + next if group.empty? + + sub = Array.new(t.head.size, '') + sub[brk] = group.first[brk] + sel.each do |i| + vals = group.map { |r| r[i] }.reject { |v| v.strip.empty? } + next if vals.empty? || !vals.all? { |v| numeric?(v) } + + sub[i] = fmt(vals.sum { |v| v.strip.to_f }, vals.map { |v| decimals(v) }.max) + end + if list + out.concat(group) + out << t.head.map { |h| '-' * [h.size, 1].max } + end + out << sub + group = [] + end + t.rows.each do |r| + flush.() if group.any? && group.first[brk] != r[brk] + group << r + end + flush.() + t.with(rows: out).write +end + +# ---- describing tables ------------------------------------------------------ + +Rdb.register('schema', "Print a table's schema", 'schema tableorlist ...') do |args| + die 'usage: schema tableorlist ...' if args.empty? + rows = args.flat_map do |f| + open_table(f).head.each_with_index.map { |h, i| [File.basename(f), (i + 1).to_s, h] } + end + Rdb::Table.new(%w[Table Field Name], rows).write +end + +Rdb.register('dbdict', 'Print a data-base dictionary', 'dbdict < table') do |_args| + t = table_in + Rdb::Table.new(%w[field name], t.head.each_with_index.map { |h, i| [(i + 1).to_s, h] }).write +end + +Rdb.register('tableorlist', 'Report whether a file has table or list format', + 'tableorlist [tableorlist ...] [< tableorlist]') do |args| + texts = args.empty? ? [$stdin.read] : args.map { |a| File.read(a) } + status = 0 + texts.each do |tx| + if tx.start_with?("\n") + puts 'list' + status = 1 + else + puts 'table' + end + end + exit status +end + +Rdb.register('check.rdb', 'Report any rows in which columns do not match head line', 'check.rdb < tableorlist') do |_args| + lines = $stdin.read.lines.map { |l| l.chomp("\n") } + expected = lines.first.to_s.count("\t") + 1 + bad = 0 + lines.each_with_index do |l, n| + next if n.zero? || l.count("\t") + 1 == expected + + bad += 1 + warn "check.rdb: Columns (#{l.count("\t") + 1}) do not equal headline columns (#{expected}) in row #{n - 1} (line #{n + 1})." + warn l + end + exit(bad.zero? ? 0 : 1) +end + +# ---- head and dash lines ------------------------------------------------- + +Rdb.register('headon', 'Add an /rdb header to a table', 'headon [column ...] < table') do |args| + rows = $stdin.each_line.map { |l| l.chomp("\n").split("\t", -1) } + n = rows.map(&:size).max || 0 + head = args.dup + head << (head.size + 1).to_s while head.size < n + Rdb::Table.new(head, rows.map { |r| r.fill('', r.size...head.size) }).write +end + +Rdb.register('headoff', 'Remove an /rdb head from both table and list file', 'headoff < tableorlist') do |_args| + text = $stdin.read + lines = text.lines + $stdout.write lines.drop(text.start_with?("\n") ? 1 : 2).join +end + +Rdb.register('insertdash', 'Insert dash line as second line in table', 'insertdash < table-without-dashline') do |_args| + lines = $stdin.read.lines + head = lines.shift.to_s.chomp("\n") + puts head + puts head.gsub(/[^\t]/, '-') + $stdout.write lines.join +end + +Rdb.register('display', 'Write table or list file to standard output', 'display < tableorlist') do |_args| + $stdout.write $stdin.read +end + +Rdb.register('see', 'Display nonprinting as well as printing characters', 'see < tableorlist') do |_args| + $stdin.read.each_char do |c| + o = c.ord + $stdout.write( + if c == "\n" then "$\n" + elsif o < 32 then "^#{(o + 64).chr}" + elsif o == 127 then '^?' + else c + end + ) + end +end diff --git a/lib/rdb/cmd/report.rb b/lib/rdb/cmd/report.rb new file mode 100644 index 0000000..5deabc5 --- /dev/null +++ b/lib/rdb/cmd/report.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +# Reports, forms and mailing lists. + +# Fill in a form for one row: is replaced by the value, +# by the output of the shell command, and !> passes data to it. +def fill_form(form, head, row) + values = head.zip(row).to_h { |h, v| [h.strip, v.to_s] } + form.gsub(/|<([^<>!\n]*)>/m) do + if Regexp.last_match(1) + cmd = Regexp.last_match(1).gsub(/<([^<>!\n]*)>/) { values.fetch(Regexp.last_match(1).strip, Regexp.last_match(0)) } + `#{cmd}` + else + name = Regexp.last_match(2).strip + values.key?(name) ? values[name] : Regexp.last_match(0) + end + end +end + +Rdb.register('report', 'Write reports using a form and a table', 'report form < tableorlist') do |args| + die 'usage: report form < tableorlist' unless args.size == 1 + ENV['PATH'] = "#{File.join(Rdb::ROOT, 'bin')}:#{ENV['PATH']}" + form = File.read(args[0]) + t = table_in + t.rows.each { |r| $stdout.write fill_form(form, t.head, r) } +end + +Rdb.register('reportwriter', 'Sample program to write standard reports', 'reportwriter [-lines] [title] < table') do |args| + lines = args.first&.match?(/\A-\d+\z/) ? args.shift[1..].to_i : 20 + title = args.join(' ') + t = table_in + date = Date.today.strftime('%-d %b %Y') + w = t.head.each_index.map { |i| (t.rows.map { |r| r[i].strip.size } + [t.head[i].size]).max } + line = ->(cells) { cells.each_with_index.map { |c, i| c.strip.ljust(w[i]) }.join(' ').rstrip } + pages = t.rows.each_slice(lines).to_a + pages = [[]] if pages.empty? + pages.each_with_index do |chunk, n| + puts title.center(w.sum + 3 * (w.size - 1)).rstrip unless title.empty? + puts "Report Date: #{date}".center(w.sum + 3 * (w.size - 1)).rstrip, '' + puts line.(t.head), line.(t.head.map { |h| '-' * h.size }) + chunk.each { |r| puts line.(r) } + puts '', " Page #{n + 1}" + puts "\f" if n < pages.size - 1 + end +end + +Rdb.register('screen', 'Convert a form into a screen-input shell program', 'screen < form > shellprogram') do |_args| + names = [] + reads = [] + painted = $stdin.read.lines.each_with_index.map do |line, n| + line = line.chomp("\n") + out = line.dup + line.scan(/|<([^<>!]*)>/) do + m = Regexp.last_match + col = m.begin(0) + 1 + if m[1] + reads << format('cursor %2d %2d ; %s;', n + 1, col, m[1].strip) + else + names << m[2].strip + reads << format('cursor %2d %2d ; read %s;', n + 1, col, m[2].strip) + end + out[m.begin(0), m[0].size] = ' ' * m[0].size + end + out + end + puts ': paint crt screen', 'exec 1>/dev/tty', 'cat <&2' + puts %(echo "#{names.join("\t")}"), %(echo "#{names.map { |x| '-' * x.size }.join("\t")}") + puts ': append row', %(echo "#{names.map { |x| "$#{x}" }.join("\t")}") +end + +def label_lines(head, row) + h = head.zip(row).to_h + [h['Name'], h['Company'], h['Street'], "#{h['City']}, #{h['State']} #{h['ZIP']}".strip].compact +end + +Rdb.register('label', 'Print mailing labels from a mailing list', 'label < list') do |_args| + t = table_in + t.rows.each { |r| puts label_lines(t.head, r) } +end + +Rdb.register('letter', 'Print form letters from a mailing list', 'letter letter.1 ... < maillist') do |args| + die 'usage: letter letter.1 letter.2 < maillist' if args.empty? + parts = args.map { |f| File.readlines(f, chomp: true) } + t = table_in + wrap = lambda do |text| + text.split.each_with_object(['']) { |w, ls| (ls.last + ' ' + w).strip.size > 65 ? ls << w : ls[-1] = (ls.last + ' ' + w).strip } + end + t.rows.each do |r| + h = t.head.zip(r).to_h + puts label_lines(t.head, r), '', "Dear #{h['Name']}:", '' + first = parts.first.join(' ') + rest = parts.drop(1).flatten + close = rest.index { |l| l.end_with?(',') } || rest.size + para = [first, "#{h['Company']}.", rest.first(close).join(' ')].reject(&:empty?).join(' ') + puts wrap.(para), *rest.drop(close), "\n" + end +end diff --git a/lib/rdb/cmd/term.rb b/lib/rdb/cmd/term.rb new file mode 100644 index 0000000..9d77811 --- /dev/null +++ b/lib/rdb/cmd/term.rb @@ -0,0 +1,238 @@ +# frozen_string_literal: true + +require 'shellwords' + +# Help, manual pages, terminal helpers, menus and housekeeping. + +ESC = "\e" +TERM_CAPS = { + 'cl' => "#{ESC}[H#{ESC}[2J", 'ho' => "#{ESC}[H", 'ce' => "#{ESC}[K", 'cd' => "#{ESC}[J", + 'cm' => "#{ESC}[%i%d;%dH", 'so' => "#{ESC}[7m", 'se' => "#{ESC}[m", 'us' => "#{ESC}[4m", + 'ue' => "#{ESC}[m", 'mb' => "#{ESC}[5m", 'md' => "#{ESC}[1m", 'me' => "#{ESC}[m", + 'as' => "#{ESC}(0", 'ae' => "#{ESC}(B", 'up' => "#{ESC}[A", 'do' => "#{ESC}[B", 'bs' => "\b" +}.freeze + +def command_rows + Rdb::COMMANDS.values.reject { |c| c.name.include?('.list') || c.name == 'rdb.install' }.sort_by(&:name) +end + +def syntax_of(cmd) + cmd.syntax.sub(/\A\S+\s*/, '') +end + +Rdb.register('rdb.list', 'List all /rdb programs', 'rdb') do |_args| + names = command_rows.map(&:name) + w = names.map(&:size).max + 2 + cols = [80 / w, 1].max + names.each_slice((names.size / cols.to_f).ceil).to_a.then do |columns| + columns.first.each_index { |i| puts columns.map { |c| (c[i] || '').ljust(w) }.join.rstrip } + end +end +Rdb.alias_for('rdb', 'rdb.list') + +Rdb.register('rdb.install', 'Symlink every command into a directory', 'rdb install [directory]') do |args| + me = File.realpath(File.join(Rdb::ROOT, 'bin', 'rdb')) + dir = File.expand_path(args.first || File.dirname(me)) + FileUtils.mkdir_p(dir) + made = 0 + command_rows.each do |c| + link = File.join(dir, c.name) + next if File.exist?(link) || File.symlink?(link) + + File.symlink(me, link) + made += 1 + end + puts "linked #{made} new commands into #{dir} (#{command_rows.size} total)" +end + +Rdb.register('commands', 'Describe /rdb commands', 'commands') do |_args| + Rdb::Table.new(%w[Command Description Syntax], command_rows.map { |c| [c.name, c.desc, syntax_of(c)] }).write +end + +Rdb.register('helpme', 'List the help commands available', 'helpme') do |_args| + rows = [['menu', 'a menu with some commands'], ['rdb', 'a list of the available commands'], + ['commands', 'description and syntax of all the commands'], + ['whatis command', 'description and syntax of command'], + ['whatwill feature', 'info on commands with that feature'], + ['rdb man command', 'the manual page for that command']] + Rdb::Table.new(%w[Command Description], rows.map { |a, b| [%("#{a}"), b] }).write +end + +show_cmd = lambda do |c| + puts format(' %-9s - %s', c.name, c.desc.sub(/\A./, &:downcase)) + puts format(' %-9s %s', c.name, c.syntax) +end + +Rdb.register('whatis', 'Display the command description and syntax', 'whatis command') do |args| + die 'usage: whatis command' if args.empty? + args.each { |a| (c = Rdb::COMMANDS[a]) ? show_cmd.(c) : warn("whatis: #{a}: not an /rdb command") } +end + +Rdb.register('whatwill', 'Display commands with functions in description', 'whatwill feature') do |args| + die 'usage: whatwill feature' if args.empty? + re = Regexp.new(Regexp.escape(args.join(' ')), Regexp::IGNORECASE) + command_rows.select { |c| [c.name, c.desc, c.syntax].any? { |s| s.match?(re) } }.each { |c| show_cmd.(c) } +end + +Rdb.register('howmany', 'Display the number of commands', 'howmany') do |_args| + puts command_rows.size +end + +# ---- manual pages --------------------------------------------------------------- + +def troff_clean(str) + str.gsub(/\\f[BIRP]/, '').gsub('\\e', '\\').gsub('\\(em', ' -- ').gsub('\\(aa', "'") + .gsub('\\-', '-').gsub('\\&', '').gsub('\\*(UX', 'UNIX').gsub('``', '"').gsub("''", '"').gsub('\\ ', ' ') + .gsub(/\\\(..|\\\*\(..|\\[a-z]/, '').gsub('\\\\', '\\') +end + +def render_man(src) + out = [] + para = [] + verbatim = false + flush = lambda do + unless para.empty? + words = para.join(' ').split + line = '' + words.each { |w| line.size + w.size + 1 > 72 ? (out << line; line = w) : line = (line.empty? ? w : "#{line} #{w}") } + out << line unless line.empty? + para.clear + end + end + src.each_line do |raw| + l = raw.chomp + if l.start_with?('.') + macro, rest = l[1..].split(/\s+/, 2) + rest = rest.to_s.strip.delete_prefix('"').delete_suffix('"') + case macro + when 'TH', 'II', 'ta', 'ne', 'R', 'RE', 'SO', 'B1', 'B2', 'nf', 'fi', 'br' then nil + when '\\"' then nil + when 'PC' then flush.(); out << '' + when 'SH' then flush.(); out << '' << troff_clean(rest).upcase + when 'PP', 'sp' then flush.(); out << '' + when 'DM', 'DS' then flush.(); verbatim = true + when 'DE' then verbatim = false + when 'IP' then flush.(); out << ''; para << troff_clean(rest.split(/\s+\d/).first.to_s) + when 'B', 'I', 'BR', 'IR', 'RB', 'RI' then para << troff_clean(rest.delete('"')).gsub(/\s+/, ' ') unless verbatim + else nil + end + elsif verbatim + out << " #{troff_clean(l)}" + elsif l.start_with?("\t", ' ') + flush.() + out << " #{troff_clean(l.strip)}" + elsif l.strip.empty? + flush.() + else + para << troff_clean(l.strip) + end + end + flush.() + out.join("\n").squeeze("\n") +end + +Rdb.register('man', 'Display the manual page for a command', 'man [-g] command') do |args| + groff = args.first == '-g' + args.shift if groff + die 'usage: rdb man [-g] command (-g formats with groff)' if args.empty? + if groff + args.each do |a| + f = File.join(Rdb::ROOT, 'man', "#{a == 'Number' ? 'number.c' : a}.man") + File.exist?(f) or die("no manual page for '#{a}'") + cmd = "groff -man -mrdb -M#{Rdb::ROOT}/man -Tutf8 #{f.shellescape}" + $stdout.tty? ? system("#{cmd} | #{ENV['PAGER'] || 'less -R'}") : system(cmd) + end + next + end + args.each do |a| + f = File.join(Rdb::ROOT, 'man', "#{a == 'Number' ? 'number.c' : a}.man") + File.exist?(f) or die("no manual page for '#{a}'") + desc = File.read(f)[/^\.PC\s+"?(.*?)"?\s*$/, 1] + puts "#{a}#{desc ? " -- #{troff_clean(desc)}" : ''}", '=' * 60 + puts render_man(File.read(f)), '' + end +end + +# ---- terminal --------------------------------------------------------------------- + +Rdb.register('termput', 'Get terminal capability from /etc/termcap file', 'termput capability') do |args| + die 'usage: termput capability' unless args.size == 1 + $stdout.print TERM_CAPS.fetch(args[0]) { die "no such capability '#{args[0]}'" } +end + +Rdb.register('tset', 'Fetch termcap entry for a terminal type', 'tset [ TERM ]') do |args| + term = args.first || ENV['TERM'] || 'ansi' + puts ":#{TERM_CAPS.map { |k, v| "#{k}=#{v.inspect[1..-2]}" }.join(':')}:co##{ENV['COLUMNS'] || 80}:li##{ENV['LINES'] || 24}:tc=#{term}:" +end + +Rdb.register('clear.rdb', "Clear the terminal's screen", 'clear.rdb') do |_args| + $stdout.print TERM_CAPS['cl'] +end + +Rdb.register('cursor', 'Move the cursor to the row and column requested', 'cursor row col [ CURSOR ]') do |args| + die 'usage: cursor row col [CURSOR]' if args.size < 2 + $stdout.print "#{ESC}[#{args[0].to_i};#{args[1].to_i}H" + $stdout.flush +end + +Rdb.register('menu', 'Root menu with some commands', 'menu') do |_args| + items = [['exit', 'leave menu or return to higher menu', nil], + ['sh', 'get a shell', ENV['SHELL'] || 'sh'], + ['vi', 'edit a file', ENV['EDITOR'] || 'vi'], + ['cal', 'see your calendar', 'cal'], + ['who', 'see who is on the system', 'who'], + ['ls', 'list the files in this directory', 'ls'], + ['cat', 'display a file on the screen', 'cat'], + ['rdb', 'display rdb commands', 'rdb']] + loop do + Rdb::Table.new(%w[Number Name For], items.each_with_index.map { |(n, d, _), i| [i.to_s, n, d] }).write + print 'Enter a number or name for the action you wish or DEL to exit: ' + $stdout.flush + choice = ($stdin.gets || '').strip + break if choice.empty? || %w[0 exit].include?(choice) + + item = items[choice.to_i] if choice.match?(/\A\d+\z/) + item ||= items.find { |n, _, _| n == choice } + next puts('?') unless item + + cmd = item[2] + if item[0] == 'cat' + print 'file: ' + f = ($stdin.gets || '').strip + system('cat', f) unless f.empty? + elsif item[0] == 'vi' + print 'file: ' + f = ($stdin.gets || '').strip + system(cmd, f) unless f.empty? + elsif item[0] == 'rdb' + Rdb.run('rdb.list', []) + else + system(cmd) + end + end +end + +# ---- housekeeping -------------------------------------------------------------------- + +Rdb.register('rmcore', 'Remove all core files', 'rmcore') do |_args| + Dir.glob('**/core').select { |f| File.file?(f) }.each { |f| puts "removed #{f}"; File.delete(f) } +end + +Rdb.register('cpdir.rdb', 'Copy one directory tree to another directory', 'cpdir.rdb fromdirectory todirectory') do |args| + die 'usage: cpdir.rdb fromdirectory todirectory' unless args.size == 2 + FileUtils.mkdir_p(args[1]) + FileUtils.cp_r(File.join(args[0], '.'), args[1]) +end + +Rdb.register('testall', 'Test all /rdb programs in directory $RDB/demo', 'testall [ > testall.new ]') do |_args| + ENV['PATH'] = "#{File.join(Rdb::ROOT, 'bin')}:#{ENV['PATH']}" + demo = File.join(ENV['RDB'] || Rdb::ROOT, 'demo') + Dir.chdir(demo) + script = File.join(demo, 'testall.cmds') + File.exist?(script) or die "#{script}: missing" + File.readlines(script, chomp: true).reject { |l| l.strip.empty? || l.start_with?('#') }.each do |cmd| + puts "$ #{cmd}" + $stdout.flush + puts `#{cmd} 2>&1` + end +end diff --git a/lib/rdb/cmd/text.rb b/lib/rdb/cmd/text.rb new file mode 100644 index 0000000..8115e65 --- /dev/null +++ b/lib/rdb/cmd/text.rb @@ -0,0 +1,239 @@ +# frozen_string_literal: true + +# Formatting tables and working with text. + +def justify_numbers(cells) + parts = cells.map { |c| c.empty? ? ['', nil] : c.split('.', 2) } + int_w = parts.map { |i, _| i.size }.max + frac_w = parts.map { |_, f| f ? f.size : 0 }.max + cells.zip(parts).map do |c, (i, f)| + next ' ' * (int_w + (frac_w.zero? ? 0 : frac_w + 1)) if c.empty? + + s = i.rjust(int_w) + s += f ? ".#{f.ljust(frac_w)}" : ' ' * (frac_w.zero? ? 0 : frac_w + 1) + s + end +end + +Rdb.register('justify', 'Left or right justify the columns of a table', "justify [-elrct'c'] [ column ... ] < table") do |args| + expand = false + mode = nil + sep = "\t" + names = [] + args.each do |a| + case a + when '-e' then expand = true + when '-l', '-r', '-c' then mode = a[1] + when /\A-t(.)\z/ then sep = Regexp.last_match(1) + else names << a + end + end + t = table_in + forced = {} + sel = names.empty? ? (0...t.head.size).to_a : names.map { |n| t.index_of(n) } + sel.each { |i| forced[i] = mode } if mode + cols = t.head.each_index.map { |i| t.rows.map { |r| r[i].strip } } + head = t.head.map(&:strip) + new_cols = [] + widths = [] + t.head.each_index do |i| + unless names.empty? || sel.include?(i) + new_cols << cols[i] + widths << nil + next + end + filled = cols[i].reject(&:empty?) + numeric = !filled.empty? && filled.all? { |c| c.match?(Rdb::NUM) } + style = forced[i] || (numeric ? 'r' : 'l') + cells = (numeric && style == 'r') ? justify_numbers(cols[i]) : cols[i] + w = (cells.map(&:size) + [head[i].size]).max + w = ((w + 1 + 7) / 8 * 8 - 1) if expand + new_cols << cells.map do |c| + case style + when 'r' then c.rjust(w) + when 'c' then c.center(w) + else c.ljust(w) + end + end + head[i] = case style + when 'r' then head[i].rjust(w) + when 'c' then head[i].center(w) + else head[i].ljust(w) + end + widths << w + end + dash = head.each_index.map { |i| '-' * (widths[i] || [head[i].size, 1].max) } + puts head.join(sep) + puts dash.join(sep) + t.rows.each_index { |r| puts new_cols.map { |c| c[r] }.join(sep) } +end + +Rdb.register('trim', 'Trim excess white space from a table', "trim [-[l|n|ln] col...][-r[n] col...][-t'c']... < table") do |args| + sep = ' ' + limits = {} + mode = nil + n = nil + t = table_in + args.each do |a| + case a + when /\A-t(.)\z/ then sep = Regexp.last_match(1) + when /\A-(l?)(\d+)\z/ then mode = :left; n = Regexp.last_match(2).to_i + when /\A-r(\d+)\z/ then mode = :right; n = Regexp.last_match(1).to_i + else limits[t.index_of(a)] = [mode, n] if mode + end + end + cols = t.head.each_index.map do |i| + cells = t.rows.map { |r| r[i].strip } + if (m, k = limits[i]) + cells = cells.map { |c| m == :right ? c[-k..] || c : c[0, k] } + end + cells + end + heads = t.head.map(&:strip) + widths = t.head.each_index.map { |i| (cols[i].map(&:size) + [heads[i].size]).max } + puts heads.each_with_index.map { |h, i| h.ljust(widths[i]) }.join(sep) + puts widths.map { |w| '-' * w }.join(sep) + t.rows.each_index { |r| puts cols.each_with_index.map { |c, i| c[r].ljust(widths[i]) }.join(sep) } +end + +Rdb.register('compresss', 'Squeeze out all leading and trailing blanks', 'compresss [-b] < tableorlist') do |args| + drop = args.include?('-b') + $stdin.each_line do |l| + l = l.chomp("\n") + next if drop && l.strip.empty? + + puts l.split("\t", -1).map(&:strip).join("\t") + end +end + +Rdb.register('pad', 'Add extra spaces at end of last column', 'pad [-number] < tableorlist') do |args| + n = args.first&.match?(/\A-\d+\z/) ? args.first[1..].to_i : 70 + t = table_in + if t.list + t.with(rows: t.rows.map { |r| r[0...-1] + [r.last + ' ' * n] }).write + else + $stdout.write t.head.join("\t"), "\n", Rdb::Table.dash_row(t.head), "\n" + t.rows.each { |r| puts (r[0...-1] + [r.last + ' ' * n]).join("\t") } + end +end + +Rdb.register('rmblank', 'Remove blank rows from a table', 'rmblank < table') do |_args| + t = table_in + t.with(rows: t.rows.reject { |r| blank_row?(r) }).write +end + +Rdb.register('blank', 'Replace all data in a record with spaces', 'blank < tableorlist') do |_args| + t = table_in + t.with(rows: t.rows.map { |r| r.map { |c| ' ' * c.size } }).write +end + +Rdb.register('cap', 'Convert first letter of each word to upper case', 'cap < textfile') do |_args| + $stdout.write $stdin.read.gsub(/\b[a-z]/, &:upcase) +end + +Rdb.register('uppercase', 'Convert input to all upper-case characters', 'uppercase [string ...] [< file]') do |args| + puts args.empty? ? $stdin.read.upcase : args.join(' ').upcase +end + +Rdb.register('lowercase', 'Transform text to lower case', 'lowercase [string ...] [< file]') do |args| + puts args.empty? ? $stdin.read.downcase : args.join(' ').downcase +end + +Rdb.register('substitute', 'Replace old string with new string', 'substitute oldstring newstring file ...') do |args| + die 'usage: substitute oldstring newstring file ...' if args.size < 3 + old, new, *files = args + files.each { |f| File.write(f, File.read(f).gsub(old, new)) } +end + +Rdb.register('trimblank', 'Remove leading and trailing blanks from a string', 'trimblank [string] [< file]') do |args| + puts (args.empty? ? $stdin.read : args.join(' ')).strip +end + +Rdb.register('padstring', 'Return string with blanks to fill a field', 'padstring [-]length string') do |args| + die 'usage: padstring [-]length string' unless args.size == 2 + w = args[0].to_i + puts w.negative? ? args[1].ljust(-w) : args[1].rjust(w) +end + +Rdb.register('length', 'Return the length of its argument or input file', 'length [string] [< file]') do |args| + puts args.empty? ? $stdin.read.bytesize : args[0].size +end + +Rdb.register('filesize', 'Return the number of characters in a file', 'filesize file ...') do |args| + die 'usage: filesize file ...' if args.empty? + if args.size == 1 + puts File.size(args[0]) + else + Rdb::Table.new(%w[Offset Filename], args.map { |f| [File.size(f).to_s, f] }).write + end +end + +Rdb.register('ascii', 'Return the ASCII value of a character', 'ascii character...') do |args| + die 'usage: ascii character...' if args.empty? + puts args[0].bytes.join(' ') + ' ' +end + +Rdb.register('chr', 'Display the character corresponding to a number', 'chr number ...') do |args| + die 'usage: chr number ...' if args.empty? + puts args.map { |a| a.to_i.chr }.join +end + +Rdb.register('word', 'Convert text file into list of unique words', 'word < text > table') do |_args| + puts $stdin.read.scan(/[A-Za-z0-9_']+/).uniq.sort +end + +Rdb.register('prompt', 'Echo a string on the standard output', 'prompt string ...') do |args| + $stdout.print args.join(' ') +end + +Rdb.register('not', 'Logical not, to reverse return status of command', 'not command ...') do |args| + die 'usage: not command ...' if args.empty? + system(*args) + exit($?.success? ? 255 : 0) +end + +Rdb.register('path', 'Find the full path of a command', 'path [ command ... ]') do |args| + args.each do |c| + dir = ENV['PATH'].to_s.split(':').find { |d| File.executable?(File.join(d, c)) && !File.directory?(File.join(d, c)) } + puts dir if dir + end +end + +# ---- translation tables ----------------------------------------------------- + +Rdb.register('tabletom4', 'Convert a table to m4 define-file format', 'tabletom4 < table > definefile') do |_args| + table_in.rows.each { |r| puts "define(#{r[0].strip},#{r[1].strip})" unless r[1].to_s.strip.empty? } +end + +Rdb.register('tabletosed', 'Convert table format to sed file format', 'tabletosed < table > sedfile') do |_args| + table_in.rows.each { |r| puts "s/#{r[0].strip}/#{r[1].strip}/g" unless r[1].to_s.strip.empty? } +end + +Rdb.register('translate', 'Word-for-word substitution using a translation file', 'translate language < text > translatedtext') do |args| + die 'usage: translate language < text' unless args.size == 1 + lang = args[0] + text = $stdin.read + sed = lambda do |file, s| + File.readlines(file, chomp: true).reduce(s) do |acc, l| + m = l.match(%r{\As/(.*?)/(.*?)/g\z}) + m ? acc.gsub(m[1], m[2]) : acc + end + end + text = sed.("#{lang}.sed.1", text) if File.exist?("#{lang}.sed.1") + if File.exist?(lang) + defs = File.readlines(lang, chomp: true).filter_map { |l| l.match(/\Adefine\((.*?),(.*)\)\z/)&.captures }.to_h + re = Regexp.union(defs.keys.sort_by { |k| -k.size }.map { |k| /\b#{Regexp.escape(k)}\b/ }) + text = text.gsub(re) { |w| defs[w] } unless defs.empty? + end + text = sed.("#{lang}.sed.2", text) if File.exist?("#{lang}.sed.2") + $stdout.write text +end + +Rdb.register('uniondict', 'Combine three tables into translation dictionary', 'uniondict langtolang > definefile') do |args| + die 'usage: uniondict langtolang' unless args.size == 1 + l = args[0] + low = Rdb::Table.open("#{l}.low") + cap = Rdb::Table.open("#{l}.allcap") + initial = low.rows.map { |r| r.map(&:capitalize) } + (low.rows + initial + cap.rows).each { |r| puts "define(#{r[0].strip},#{r[1].strip})" unless r[1].to_s.strip.empty? } +end diff --git a/lib/rdb/env.rb b/lib/rdb/env.rb new file mode 100644 index 0000000..03852af --- /dev/null +++ b/lib/rdb/env.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +module Rdb + # Command blocks run inside an Env, which holds the helpers they share. + class Env + attr_reader :name + + def initialize(name) + @name = name + end + + def die(msg) + Rdb.die(msg) + end + + def table_in + Table.read($stdin) + end + + def open_table(path) + File.exist?(path) || path == '-' or die("#{path}: no such table") + Table.open(path) + end + + def numeric?(str) + str.match?(NUM) + end + + def decimals(str) + str.strip[/\.(\d+)\z/, 1].to_s.size + end + + def fmt(num, dec) + dec.zero? ? num.round.to_s : format("%.#{dec}f", num) + end + + def blank_row?(row) + row.all? { |c| c.strip.empty? } + end + + # Replace column names in an awk expression with $N, leaving strings, + # regexes and other identifiers alone. + def to_awk(expr, head) + names = head.each_with_index.sort_by { |n, _| -n.size } + out = +'' + i = 0 + while i < expr.size + c = expr[i] + prev = out.rstrip[-1].to_s + if c == '"' + j = i + 1 + j += expr[j] == '\\' ? 2 : 1 while j < expr.size && expr[j] != '"' + out << expr[i..j] + i = j + 1 + elsif c == '/' && (prev.empty? || '~(!&|,={;'.include?(prev)) + j = i + 1 + j += expr[j] == '\\' ? 2 : 1 while j < expr.size && expr[j] != '/' + out << expr[i..j] + i = j + 1 + elsif c.match?(/[A-Za-z_#]/) && (i.zero? || !expr[i - 1].match?(/[\w$]/)) + hit = names.find { |n, _| !n.empty? && expr[i, n.size] == n && !expr[i + n.size].to_s.match?(/\w/) } + if hit + out << "$#{hit[1] + 1}" + i += hit[0].size + else + m = expr[i..][/\A\w+/] || c + out << m + i += m.size + end + else + out << c + i += 1 + end + end + out + end + + # Run an awk program over rows (tab separated) and return the output rows. + def awk(prog, rows) + input = rows.map { |r| r.join("\t") }.join("\n") + input += "\n" unless input.empty? + out, err, st = Open3.capture3('awk', '-F', "\t", prog, stdin_data: input) + $stderr.print err unless err.empty? + exit 1 unless st.success? + out.each_line.map { |l| l.chomp.split("\t", -1) } + end + + # Raw output of awk, for programs that print their own text. + def awk_text(prog, rows) + input = rows.map { |r| r.join("\t") }.join("\n") + input += "\n" unless input.empty? + out, err, st = Open3.capture3('awk', '-F', "\t", prog, stdin_data: input) + $stderr.print err unless err.empty? + [out, st.success?] + end + + def sh(cmd, stdin: nil) + out, _err, _st = Open3.capture3('sh', '-c', cmd, stdin_data: stdin.to_s) + out + end + + # YYMMDD <-> day number. The day number counts from the same epoch as + # /rdb's julian command (1986-12-22 is 1752827). + JULIAN_OFFSET = 693_960 + + def to_julian(str, fmt = 'c') + s = str.strip + return '' if s.empty? + + y, m, d = + case fmt + when 'e' then s.split('/').values_at(2, 1, 0).map(&:to_i) + when 'u' then s.split('/').values_at(2, 0, 1).map(&:to_i) + else + s.size == 8 ? [s[0, 4], s[4, 2], s[6, 2]].map(&:to_i) : [s[0, 2], s[2, 2], s[4, 2]].map(&:to_i) + end + y += (y >= 50 ? 1900 : 2000) if y < 100 + (Date.new(y, m, 1) + (d - 1)).jd - JULIAN_OFFSET + end + + def from_julian(num, fmt = 'c') + s = num.to_s.strip + return '' if s.empty? + + dt = Date.jd(s.to_i + JULIAN_OFFSET) + case fmt + when 'e' then format('%02d/%02d/%02d', dt.day, dt.month, dt.year % 100) + when 'u' then format('%02d/%02d/%02d', dt.month, dt.day, dt.year % 100) + else format('%02d%02d%02d', dt.year % 100, dt.month, dt.day) + end + end + end +end diff --git a/lib/rdb/fast.rb b/lib/rdb/fast.rb new file mode 100644 index 0000000..12e5dd4 --- /dev/null +++ b/lib/rdb/fast.rb @@ -0,0 +1,331 @@ +# frozen_string_literal: true + +module Rdb + # Fast-access methods for tables. Rows are found by byte offset, so a record + # can be blanked or replaced in place without rewriting the file. + # + # -ms sequential (no index file) -mb binary search on a sorted table + # -mr record numbers table.r -mi inverted index table.i + # -mh hash table.h + # + # table.r and table.h hold fixed-width entries (a head line, a dash line, + # then "%8d\n" per entry), so one entry can be rewritten in place. + module Fast + ENTRY_WIDTH = 9 + SUFFIX = { 'h' => '.h', 'i' => '.i', 'r' => '.r' }.freeze + FIXED_HEAD = "Offset\n------\n" + + Row = Struct.new(:from, :to, :line) do + def cells + @cells ||= line.split("\t", -1) + end + + def blank? + line.delete("\t ").empty? + end + end + + Hit = Struct.new(:row, :xfrom, :xto) + + # A table file read as bytes with the offset of every row. + class Data + attr_reader :path, :head, :rows + + def initialize(path) + @path = path + File.exist?(path) or Rdb.die("#{path}: no such table") + bytes = File.binread(path) + lines = [] + off = 0 + bytes.each_line("\n") do |l| + lines << Row.new(off, off + l.bytesize, l.chomp("\n")) + off += l.bytesize + end + Rdb.die("#{path}: is a list; fast access needs a table") if bytes.start_with?("\n") + @head = lines.shift&.line.to_s.split("\t", -1) + lines.shift if lines.first && Table.dash_line?(lines.first.cells) + @rows = lines + @by_from = lines.to_h { |r| [r.from, r] } + end + + def live + @live ||= @rows.reject(&:blank?) + end + + def column(name) + @head.index(name) || Rdb.die("#{path}: no column named '#{name}'") + end + + def row_at(offset) + @by_from[offset] + end + end + + module_function + + def index_path(path, method) + SUFFIX[method] ? path + SUFFIX[method] : nil + end + + def key_of(cells, idx) + cells[idx].to_s + end + + def norm(str, fold) + s = str.to_s.strip + fold ? s.downcase : s + end + + def fmt_entry(num) + format("%8d\n", num) + end + + def hash_slot(slots, key) + key.delete(" \t").bytes.sum % slots + end + + def live_rows(data) + data.live + end + + # ---- building indexes ------------------------------------------------- + + def build(path, method, keycol, fold: false) + data = Data.new(path) + rows = live_rows(data) + case method + when 'r' + File.binwrite(path + '.r', FIXED_HEAD + rows.map { |r| fmt_entry(r.from) }.join) + when 'i' + k = data.column(keycol) + ents = rows.map { |r| [r.cells[k].to_s, r.from] } + ents = ents.sort_by { |key, off| [fold ? key.downcase : key, off] } + File.binwrite(path + '.i', "#{keycol}\tOffset\n#{'-' * keycol.size}\t------\n" + ents.map { |key, off| "#{key}\t#{off}\n" }.join) + when 'h' + k = data.column(keycol) + slots = [rows.size * 2, 2].max + table = Array.new(slots, 0) + rows.each { |r| table[free_slot(table, hash_slot(slots, r.cells[k].to_s))] = r.from } + File.binwrite(path + '.h', FIXED_HEAD + table.map { |o| fmt_entry(o) }.join) + when 'b' + k = data.column(keycol) + body = live_rows(data).sort_by { |r| r.cells[k].to_s }.map { |r| r.line + "\n" } + File.binwrite(path, data.head.join("\t") + "\n" + Table.dash_row(data.head) + "\n" + body.join) + end + end + + def free_slot(table, start) + i = start + i = (i + 1) % table.size while table[i] != 0 && table[i] != -1 + i + end + + # ---- searching ---------------------------------------------------------- + + def read_fixed(path) + File.binread(path).byteslice(FIXED_HEAD.bytesize..).to_s.lines.map { |l| l.to_i } + end + + def read_inverted(path) + off = 0 + ents = [] + File.binread(path).each_line("\n").with_index do |l, n| + ents << { from: off, to: off + l.bytesize, key: l.chomp("\n").split("\t", 2)[0], offset: l.chomp("\n").split("\t", 2)[1].to_i } if n >= 2 + off += l.bytesize + end + ents + end + + def matches?(cell, key, partial, exact) + if partial + norm(cell, true).start_with?(norm(key, true)) + elsif exact + cell == key + else + cell.strip == key.strip + end + end + + # Find rows for one key. method is one of s b h i r; keycols are column names. + def search(data, method, keycols, key, first: false, partial: false, exact: false) + path = data.path + idxs = keycols.map { |c| data.column(c) } + keyvals = Array(key) + hits = [] + case method + when 'b' + rows = live_rows(data) + k = idxs[0] + lo = rows.bsearch_index { |r| (partial ? norm(r.cells[k], true)[0, keyvals[0].strip.size] : r.cells[k].strip) >= (partial ? norm(keyvals[0], true) : keyvals[0].strip) } + if lo + rows[lo..].each do |r| + break unless matches?(r.cells[k], keyvals[0], partial, exact) + + hits << Hit.new(r, 0, 8) + break if first + end + end + when 'r' + ents = read_fixed(path + '.r') + n = keyvals[0].to_i + if n.positive? && n <= ents.size + r = data.row_at(ents[n - 1]) and hits << Hit.new(r, FIXED_HEAD.bytesize + ENTRY_WIDTH * (n - 1), FIXED_HEAD.bytesize + ENTRY_WIDTH * n) + end + when 'i' + ents = read_inverted(path + '.i') + lo = ents.bsearch_index { |e| (partial ? norm(e[:key], true)[0, keyvals[0].strip.size] : e[:key].strip) >= (partial ? norm(keyvals[0], true) : keyvals[0].strip) } + if lo + ents[lo..].each do |e| + break unless matches?(e[:key], keyvals[0], partial, exact) + + r = data.row_at(e[:offset]) + hits << Hit.new(r, e[:from], e[:to]) if r + break if first + end + end + when 'h' + ents = read_fixed(path + '.h') + slots = ents.size + i = hash_slot(slots, keyvals[0]) + slots.times do + o = ents[i] + break if o.zero? + + if o.positive? && (r = data.row_at(o)) && matches?(r.cells[idxs[0]], keyvals[0], false, exact) + hits << Hit.new(r, FIXED_HEAD.bytesize + ENTRY_WIDTH * i, FIXED_HEAD.bytesize + ENTRY_WIDTH * (i + 1)) + break if first + end + i = (i + 1) % slots + end + else + live_rows(data).each do |r| + c = r.cells + next unless idxs.each_with_index.all? { |ci, n| matches?(c[ci].to_s, keyvals[n].to_s, partial, exact) } + + hits << Hit.new(r, 0, 8) + break if first + end + end + hits + end + + # ---- changing rows -------------------------------------------------------- + + def blanked(line) + line.gsub(/[^\t]/, ' ') + end + + def splice(path, from, to, text) + b = File.binread(path) + File.binwrite(path, b.byteslice(0, from) + text + b.byteslice(to..).to_s) + end + + # Remove a record's entry from the secondary index for method. + def index_remove(path, method, row_from) + case method + when 'r' + ip = path + '.r' + ents = read_fixed(ip) + ents.delete(row_from) + File.binwrite(ip, FIXED_HEAD + ents.map { |o| fmt_entry(o) }.join) + when 'h' + ip = path + '.h' + ents = read_fixed(ip).map { |o| o == row_from ? -1 : o } + File.binwrite(ip, FIXED_HEAD + ents.map { |o| fmt_entry(o) }.join) + when 'i' + ip = path + '.i' + e = read_inverted(ip).find { |x| x[:offset] == row_from } + splice(ip, e[:from], e[:to], '') if e + end + end + + def index_add(path, method, row_from, cells, data_head, keycol) + case method + when 'r' + File.binwrite(path + '.r', File.binread(path + '.r') + fmt_entry(row_from)) + when 'i' + ip = path + '.i' + name = File.binread(ip).lines.first.split("\t")[0] + key = cells[data_head.index(name)].to_s + ents = read_inverted(ip) + pos = ents.find { |e| e[:key] > key } + line = "#{key}\t#{row_from}\n" + if pos then splice(ip, pos[:from], pos[:from], line) + else File.binwrite(ip, File.binread(ip) + line) + end + when 'h' + ip = path + '.h' + ents = read_fixed(ip) + if ents.any? { |o| o <= 0 } + key = cells[data_head.index(keycol)].to_s + ents[free_slot(ents, hash_slot(ents.size, key))] = row_from + File.binwrite(ip, FIXED_HEAD + ents.map { |o| fmt_entry(o) }.join) + else + build(path, 'h', keycol) + end + when 'b' + build(path, 'b', keycol) + end + end + + def append_row(path, cells, method, keycol) + line = cells.join("\t") + b = File.binread(path) + b += "\n" unless b.empty? || b.end_with?("\n") + from = b.bytesize + File.binwrite(path, b + line + "\n") + index_add(path, method, from, cells, Data.new(path).head, keycol) if SUFFIX[method] || method == 'b' + from + end + + def delete_hit(path, hit, method) + r = hit.row + splice(path, r.from, r.to, blanked(r.line) + "\n") + index_remove(path, method, r.from) + end + + # Put cells where the record at hit was. If the new record is too long + # even after trimming blanks, append it (unless append is false). + def replace_row(path, from, to, cells, method, xfrom, xto, keycol, allow_append: true) + room = to - from - 1 + line = cells.join("\t") + line = cells.map(&:strip).join("\t") if line.bytesize > room + if line.bytesize <= room + splice(path, from, to, line + ' ' * (room - line.bytesize) + "\n") + return from + end + return nil unless allow_append + + old = File.binread(path).byteslice(from, room) + splice(path, from, to, blanked(old) + "\n") + newfrom = append_row(path, cells.map(&:strip), 'x', keycol) + reindex_entry(path, method, xfrom, xto, newfrom, cells, keycol) + newfrom + end + + def reindex_entry(path, method, xfrom, xto, newfrom, cells, keycol) + case method + when 'r', 'h' + splice(path + SUFFIX[method], xfrom, xto, fmt_entry(newfrom)) + when 'i' + ip = path + '.i' + name = File.binread(ip).lines.first.split("\t")[0] + head = Data.new(path).head + splice(ip, xfrom, xto, "#{cells[head.index(name)].to_s.strip}\t#{newfrom}\n") + end + end + + # Read keys: a table of keys, or one key per line. + def read_keys(io, ncols) + text = io.read.to_s + return [] if text.empty? + + lines = text.lines.map { |l| l.chomp("\n") } + if lines.size >= 2 && Table.dash_line?(lines[1].split("\t", -1)) + lines.drop(2).reject(&:empty?).map { |l| l.split("\t", -1).first(ncols) } + else + lines.reject(&:empty?).map { |l| l.split("\t", -1).first(ncols) } + end + end + end +end diff --git a/lib/rdb/table.rb b/lib/rdb/table.rb new file mode 100644 index 0000000..718fe1a --- /dev/null +++ b/lib/rdb/table.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +module Rdb + # A table is a head line of column names, a dash line, then one + # tab-separated row per line. A list is the same data turned sideways: + # it starts with a newline and holds "Columnvalue" lines. + class Table + attr_reader :head, :rows + attr_accessor :list + + def initialize(head, rows, list: false) + @head = head + @rows = rows + @list = list + end + + def self.dash_line?(cells) + cells.all? { |c| c.match?(/\A\s*-*\s*\z/) } && cells.any? { |c| c.include?('-') } + end + + def self.parse(text) + return parse_list(text) if text.start_with?("\n") + + lines = text.lines.map { |l| l.chomp("\n") } + head = (lines.shift || '').split("\t", -1) + lines.shift if lines.first && dash_line?(lines.first.split("\t", -1)) + rows = lines.map { |l| l.split("\t", -1) } + new(head, rows.map { |r| r.fill('', r.size...head.size) }) + end + + def self.parse_list(text) + head = [] + records = [] + rec = nil + text.sub(/\A\n/, '').each_line do |line| + line = line.chomp("\n") + if line.strip.empty? + rec = nil + next + end + name, value = line.split("\t", 2) + value ||= '' + head << name unless head.include?(name) + rec = nil if rec && name == head.first && rec.key?(name) + unless rec + rec = {} + records << rec + end + rec[name] = value + end + new(head, records.map { |r| head.map { |h| r.fetch(h, '') } }, list: true) + end + + def self.read(io) + parse(io.read) + end + + def self.open(path) + path == '-' ? read($stdin) : parse(File.read(path)) + end + + def index_of(name) + @head.index(name) || Rdb.die("no column named '#{name}'") + end + + def to_s + return list_text if @list + + out = +'' + out << @head.join("\t") << "\n" + out << Table.dash_row(@head) << "\n" + @rows.each { |r| out << r.join("\t") << "\n" } + out + end + + def self.dash_row(head) + head.map { |h| '-' * [h.size, 1].max }.join("\t") + end + + def list_text + out = +"\n" + @rows.each do |r| + @head.each_with_index { |h, i| out << h << "\t" << r[i].to_s << "\n" } + out << "\n" + end + out + end + + def write(out = $stdout) + out.write to_s + end + + def with(head: @head, rows: @rows) + Table.new(head, rows, list: @list) + end + end +end diff --git a/man/accounting_ter.man b/man/accounting_ter.man new file mode 100644 index 0000000..74051b4 --- /dev/null +++ b/man/accounting_ter.man @@ -0,0 +1,74 @@ +.TH "accounting terms" "" "" Definition +.PC +.PP +The following defines several of the more commonly used accounting +terms. +These are included to help you understand the descriptions of \fB/rdb\fR's +accounting package. +.PP +Financial information that records the day-to-day operation of a +business is recorded in an +.IR account . +A simple account is made up of three parts: +.IP \fB1.\fR 0.3i +A title that describes the name of the information being stored +in the account; +.IP \fB2.\fR +The left side of the account, used to record a debit; and +.IP \fB3.\fR +The right side of the account, used to record a credit. +.PP +This simple configuration is referred to as a `T' account because its +shape resembles a capital T: +.DM + Title + =================================== + Debit | Credit +.DE +.PP +A +.I ledger +is the combination of all the accounts maintained by a business. +Transactions are posted to accounts through a +.IR "journal entry" , +as in the following example: +.DM + Cash | 500.00 | + Sales | | 500.00 +.DE +.PP +Entries recorded on the far left are debits; +entries indented from the left are credits. +.PP +Accounts fall into the following classifications: +.IP \fBAssets\fR +Anything that is owned and has a monetary value. +.IP \fBLiabilities\fR +Amounts owed to others. +.IP \fBCapital\fR +This is the owner's equity in the business. +It is equal to the total assets minus the total liabilities. +.IP \fBRevenue\fR +Proceeds obtained in the course of doing business. +.IP \fBExpenses\fR +Costs incurred in the course of doing business. +.PP +Further information on accounting terms may be obtained from any +textbook on accounting principles. +Your local public library should contain several examples. +.SH "See Also" +.B +act +.R +.SH Notes +Please note that +.BR /rdb 's +accounting system is meant to serve as an example of data-base programming +with +.BR /rdb . +It is not designed to be an exhaustive accounting package, nor is it +designed to teach you how to perform accounting. +Mark Williams technical support will help you if you have a problem with an +.BR /rdb +command, but not with problems that involve the principles of accounting. +.IR "Caveat utilitor" . diff --git a/man/act.man b/man/act.man new file mode 100644 index 0000000..d9ad9f7 --- /dev/null +++ b/man/act.man @@ -0,0 +1,72 @@ +.TH act "" "" "/rdb Command" +.PC "List all /act commands" +.B act +.PP +.II "accounting system" +.B /rdb +comes with a set of commands that implement a basic accounting system, +suitable for running a small business. +These commands, called +.B /act +commands, +are kept in directory +.BR $RDB/act . +.PP +The accounting system consists of the following sub-systems: +.II "general ledger" +.IP \fBgl\fR 0.3i +General ledger +.II inventory +.IP \fBinv\fR +Inventory +.II operations +.IP \fBopr\fR +Operations, for manufacturing +.II payroll +.IP \fBpay\fR +Payroll +.II "accounts payable" +.II purchasing +.IP \fBpur\fR +Accounts payable and purchasing +.II sales +.IP \fBsales\fR +Accounts payable and sales +.PP +The command +.B act +lists all +.B /act +commands. +.SH Example +Typing +.B act +displaying the following on your screen: +.DM +act close income postap shorttoaccount +adjust consolidate invoice postar start +balance cstate makecatalog postpay tax.calc +bom fillform onhand purchase trial +calculate foot po sale vstate +chartdup getjournal post ship w2 +.DE +.PP +Many +.B act +commands have their own entries in this manual. +.SH "See Also" +.B +accounting terms +.R +.SH Notes +Please note that +.BR /rdb 's +accounting system is meant to serve as an example of data-base programming +with +.BR /rdb . +It is not designed to be an exhaustive accounting package, nor is it +designed to teach you how to perform accounting. +Mark Williams technical support will help you if you have a problem with an +.BR /rdb +command, but not with problems that involve the principles of accounting. +.IR "Caveat utilitor" . diff --git a/man/addcol.man b/man/addcol.man new file mode 100644 index 0000000..f9db565 --- /dev/null +++ b/man/addcol.man @@ -0,0 +1,41 @@ +.TH addcol "" "" "/rdb Command" +.PC "Add a column to a table" +\fBaddcol \fInewcolumn \fB< \fItable\fR +.PP +.II "add column to table" +.II "column^add to table" +The command +.B addcol +appends one or more new columns to +.IR table . +The column is initialized to blank spaces, and is given the name +.IR newcolumn . +.SH Example +The command +.DM + addcol New < inventory +.DE +.PP +modifies table +.B inventory +to appear as follows: +.DM +Item Amount Cost Value Description New +---- ------ ---- ----- -------------- ------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +Note that column +.B New +has been added to the table. +.SH "See Also" +.B +jointable, +number +.R diff --git a/man/adjust.man b/man/adjust.man new file mode 100644 index 0000000..d61c1f1 --- /dev/null +++ b/man/adjust.man @@ -0,0 +1,95 @@ +.TH adjust "" "" "/act/gl Command" +.PC "Create adjusted trial balance table" +.B adjust +.PP +.II "adjust balance sheet" +The command +.B adjust +is part of the accounting/general ledger system +that is included with +.BR /rdb . +It produces the adjusted trial balance sheet, called +.BR adjusttrial , +by adding the trial-balance table +.B trialbalance +with the journal-of-adjustments table +.BR journaladjust . +.PP +The accountant makes +adjustments at the end of the accounting period, +to reflect changes in inventory, depreciation, depletion, amortization, +and so on. +These are computed costs of doing business that are +assigned to each accounting period. +.SH Example +The following gives an example of table +.BR trialbalance : +.DM +Account Debit Credit Format Taxline Short Name +------- ----- ------ ------ ------- ------- ----------------- +1010 11178 B-A 4L01 cash Cash +3010 13500 I-I 101a sales Sales +3080 250 I-I 106 royalty Gross royalties +3100 3286 I-I 108 income Other Income +4020 3601 I-COGS 102-A2 merch Misc Merchandise +4030 2100 I-COGS 102-A3 wages Salary and Wages +4370 2 I-E 122 parking Business parking +4380 41 I-E 122 travel Business travel +4384 114 I-E 122 hotel Business hotel +.DE +.PP +The following gives an example of table +.BR journaladjust : +.DM +Account Debit Credit Format Taxline Name +------- ------- ------- ------- ------- --------------------- +1110 20000 B-A 4L06 Beginning Inventory +1110 22130 B-A 4L06 Ending Inventory +1310 0 B-A 4L09a Accum Depreciation +1330 0 B-A 4L10a Accum Depletion +1420 0 B-A 4L12a Accum Amortization +4010 20000 I-COGS 102-A1 Beginning Inventory +4080 22130 I-COGS 102-A4 Ending Inventory +4170 0 I-E 121 Depreciation +4180 0 I-E 118 Depletion +4215 0 I-E 122 Amortization +.DE +.PP +The command +.B adjust +then produces the table +.B adjustedtrial +by combining +.B trialbalance +with +.BR journaladjust . +In this example, +.B adjustedtrial +appears as follows: +.DM +Account Debit Credit Format Taxline Short Name +------- ----- ------ ------ ------- ------- ----------------- +1010 11178 B-A 4L01 cash Cash +1110 2130 B-A 4L06 inv Inventory +1310 0 B-A 4L09a adeprec Accum Depreciation +1330 0 B-A 4L10a adeplet Accum Depletion +1420 0 B-A 4L12a aamort Accum Amortization +3010 13500 I-I 101a sales Sales +3080 250 I-I 106 royalty Gross royalties +3100 3286 I-I 108 income Other Income +4010 20000 I-COGS 102-A1 beginv Begin Inventory +4020 3601 I-COGS 102-A2 merch Misc Merchandise +4030 2100 I-COGS 102-A3 wages Salary and Wages +4080 22130 I-COGS 102-A4 endinv Ending Inventory +4170 0 I-E 121 deprec Depreciation +4180 0 I-E 118 deplet Depletion +4215 0 I-E 122 amor Amortization +4370 2 I-E 122 parking Business parking +4380 41 I-E 122 travel Business travel +4384 114 I-E 122 hotel Business hotel +.DE +.SH "See Also" +.B +act, +balance +.SO adjust diff --git a/man/append.man b/man/append.man new file mode 100644 index 0000000..001467c --- /dev/null +++ b/man/append.man @@ -0,0 +1,136 @@ +.TH append "" "" "/rdb Command" +.PC "Add a row to a table and update index tables" +\fBappend [\-h \-m[bhirs]] \fItable \fB[\fIkeycolumn ...\fB] < \fItableorrow\fR +.PP +.II "row^append to table" +.II "table^append row" +.II "append row to table" +The command +.B append +appends the row in table +.I tableorrow +onto the end of +.IR table . +It also updates the appropriate index table so that the +next search will find it. +For the +.I record +method of indexing, +.B append +adds the new record's offset to the end of the index table. +For the +.I inverted +method of indexing, it adds the record and then sorts the table. +.SH Options +.B append +recognizes the following options: +.IP \fB\-h\fR +.I tableorrow +has no +.B /rdb +head line and dash line. +This option is useful when you only have a row to append to a table, +but it does not have a head line. +.IP \fB\-m[bhirs]\fR +Use a fast-access method. +See the manual page for +.B search +for a description of the fast-access methods and how to invoke them. +.SH Example +Consider the table +.BR inventory , +which is defined as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- ----------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +This table has an index table, called +.BR record , +which appears as follows: +.DM + Offset +-------- + 76 + 113 + 147 + 177 + 207 + 245 + 283 +.DE +.PP +Finally, consider the table +.BR newrecord , +which is as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- ----------- + 8 35 105 0 pipettes +.DE +.PP +Note that +.B newrecord +has the same columnar layout as +.BR inventory . +.PP +The following command appends +.B newrecord +onto the end of +.BR inventory: +.DM + append -mr inventory < newrecord +.DE +.PP +After +.B append +has done its work, +.B inventory +appears as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales + 8 35 105 0 pipettes +.DE +.PP +.BR inventory 's +index table now appears as follows: +.DM + Offset +-------- + 76 + 113 + 147 + 177 + 207 + 245 + 283 + 313 +.DE +.PP +The new ``313'' entry in the secondary index table +.B inventory.r +is the offset to record 8 \(em that is, +the number of bytes for the beginning of the file to record 8. +.SH "See Also" +.B +delete, +index, +replace, +search +.R +.SO append diff --git a/man/ascii.man b/man/ascii.man new file mode 100644 index 0000000..eee68cf --- /dev/null +++ b/man/ascii.man @@ -0,0 +1,46 @@ +.TH ascii "" "" "/rdb Command" +.PC "Return the ASCII value of a character" +\fBascii \fIcharacter...\fR +.PP +.II ASCII +The command +.B ascii +converts each of the characters in its first argument +to a space-delimited series of numbers. +You can use this command to find the internal computer representation +of a character. +Any strings following the first are ignored. +If you have spaces in your string, be sure to put +quotation marks around them. +.SH Example +The command +.DM + ascii aA1 +.DE +.PP +produces: +.DM + 97 65 49 +.DE +.PP +The command +.DM + ascii ' ' +.DE +.PP +produces: +.DM + 32 +.DE +.PP +Note that the single space character, enclosed within apostrophes, +is converted to its ASCII code in decimal (32). +.SH "See Also" +.B +chr +.R +.br +COHERENT Lexicon: +.B +ASCII +.R diff --git a/man/backup.man b/man/backup.man new file mode 100644 index 0000000..dbb87df --- /dev/null +++ b/man/backup.man @@ -0,0 +1,16 @@ +.TH backup "" "" "Technical Information" +.PC +.PP +When you are using +.B /rdb +to store and manipulate your data, you must back up your data regularly. +If you do not back up your data and your disk should fail for whatever +reason, your data will be irretrievably lost. +.PP +See the COHERENT manual section 2, \fIUsing the COHERENT System\fR, for +a detailed description of how to back up your data. +Also see the Lexicon articles on +.B ustar +and +.B cpio +for descriptions of how to use these tools to back up your data. diff --git a/man/balance.man b/man/balance.man new file mode 100644 index 0000000..774c563 --- /dev/null +++ b/man/balance.man @@ -0,0 +1,71 @@ +.TH balance "" "" "/act/gl Command" +.PC "Create balance sheet from adjusted trial balance" +.B balance +.PP +.II "balance sheet" +The command +.B balance +is part of the accounting/general-ledger system included with +.BR /rdb . +It reads table +.B adjustedtrial +and writes a balance sheet into table +and writes it into file +.BR balancesheet . +.SH Example +Consider the table +.BR adjustedtrial , +as follows: +.DM +Account Debit Credit Format Taxline Short Name +------- ----- ------ ------ ------- ------- ------------------ +1010 11178 B-A 4L01 cash Cash +1110 2130 B-A 4L06 inv Inventory +1310 0 B-A 4L09a adeprec Accum Depreciation +1330 0 B-A 4L10a adeplet Accum Depletion +1420 0 B-A 4L12a aamort Accum Amortization +3010 13500 I-I 101a sales Sales +3080 250 I-I 106 royalty Gross royalties +3100 3286 I-I 108 income Other Income +4010 20000 I-COGS 102-A1 beginv Begin Inventory +4020 3601 I-COGS 102-A2 merch Misc Merchandise +4030 2100 I-COGS 102-A3 wages Salary and Wages +4080 22130 I-COGS 102-A4 endinv Ending Inventory +4170 0 I-E 121 deprec Depreciation +4180 0 I-E 118 deplet Depletion +4215 0 I-E 122 amor Amortization +4370 2 I-E 122 parking Business parking +4380 41 I-E 122 travel Business travel +4384 114 I-E 122 hotel Business hotel +.DE +.PP +The command +.B balance +writes the balance sheet into +.BR balancesheet , +which appears as follows: +.DM + Makeapile, Inc. + 31 December 1985 + Balancesheet +.DE +.DM +11178 Cash + 2130 Inventory + 0 Accumulated Depreciation + 0 Accumulated Depletion + 0 Accumulated Amortization +----- ------------------------ +13308 Total Assets +.DE +.DM + 13308 Retained Earnings (Profit) +------ -------------------------- + 13308 Total Liabilities and Equity +.DE +.SH "See Also" +.B +act, +adjust +.R +.SO balance diff --git a/man/blank.man b/man/blank.man new file mode 100644 index 0000000..58a17bc --- /dev/null +++ b/man/blank.man @@ -0,0 +1,71 @@ +.TH blank "" "" "/rdb Command" +.PC "Replace all data in a record with spaces" +\fBblank < \fItableorlist\fR +.PP +.II "blank a row" +.II "row^blank out" +The command +.B blank +replaces with spaces every values in each record of +.IR tableorlist . +.PP +The command +.B update +uses +.B blank +when it updates a record. +.B update +temporarily replaces the record it is updating with a blank record. +This blank record serves as a record lock; this ensures +that other users cannot read +or update the record while it is being updated, but it leaves the +rest of the table available for use. +.PP +Please note that when you pull up a blank record, +it means that that record is being updated by someone else. +Come back to it later. +.SH Example +To blank out the data fields in table +.B maillist +and then +.B see +the result, you could type the following: +.DM +blank < maillist | see +.DE +.PP +This command produces the following output: +.DM +Number^I $ +Name^I $ +Company^I $ +Street^I $ +City^I $ +State^I $ +ZIP^I $ +Phone^I $ +$ +Number^I $ +Name^I $ +Company^I $ +Street^I $ +City^I $ +State^I $ +ZIP^I $ +Phone^I $ +.DE +.PP +Note the spaces between the tab (^I) and the dollar sign `$' +that indicates the end of the line. +The spaces replaced the +characters, including spaces, that were in the original records. +.PP +Note too that the column names are untouched, so that this +is a perfectly correct file as far as the +.B /rdb +commands are concerned, it just contains no data. +.SH "See Also" +.B +see, +update +.R diff --git a/man/bom.man b/man/bom.man new file mode 100644 index 0000000..77d2cf6 --- /dev/null +++ b/man/bom.man @@ -0,0 +1,82 @@ +.TH bom "" "" "/act/opr Command" +.PC "Produce bill-of-materials from parts list" +.B bom +.PP +.II "bill of materials" +The command +.B bom +is part of the accounting/operations system included with +.BR /rdb . +It creates a bill-of-materials table from table +.BR part , +which holds all of the items that are required to make a product, +and the table +.BR saleitem , +which holds all sales items from the sales department. +This tells how many items must be manufactured or purchased +to meet current sales orders. +.PP +.B bom +writes its output into table +.BR bom . +.SH Example +Assume the table +.B saleitem +contains the following data: +.DM +Order Number Code Backord Qty Price Total Name +----- ------ ---- ------- --- ----- ------- ---- + 1 1 rdb 10 1 1500 1500.00 /rdb + 2 1 rdb 10 2 1500 3000.00 /rdb + 3 1 rdb 10 5 1500 7500.00 /rdb + 3 2 act 10 10 1500 15000.00 /act + 4 1 rdb 10 5 1500 7500.00 /rdb + 4 2 rdb 10 19 1500 28500.00 /rdb + 5 1 rdb 10 5 1500 7500.00 /rdb + 5 2 act 10 9 1500 13500.00 /act +.DE +.PP +Assume, too, that the table +.B part +contains the following +data: +.DM +Code Subpart Count +------- ------- ------- +act binder 2 +act actdoc 2 +act f1 10 +rdb binder 2 +rdb rdbdoc 2 +rdb f1 10 +.DE +.PP +The command +.B bom +reads these tables to produce the following summary table: +.DM +Code Qty +------- ------- +actdoc 38 +binder 112 +f1 560 +rdbdoc 74 +.DE +.PP +You can see that we have orders for 19 copies of +.BR act s +in the +.B salesitem +table. +Each package has two documents, for a total of 38 +.BR actdoc s. +Note that table +.B bom +lists 560 floppies ((19 act x 10) + (37 rdb x 10)). +We can order these, regardless +of which software packages are being put onto them. +.SH "See Also" +.B +act +.R +.SO bom diff --git a/man/calcpay.man b/man/calcpay.man new file mode 100644 index 0000000..d36cd34 --- /dev/null +++ b/man/calcpay.man @@ -0,0 +1,38 @@ +.TH calcpay "" "" "/act/pay Command" +.PC "Post payroll to ledgerpay" +.B calcpay +.PP +.II "post payroll" +.II "payroll^calculate" +The command +.B calcpay +is part of the accounting/payroll system that is included with +.BR /rdb . +It posts the payroll-journal table +.B journalpay +to the payroll-ledger table +.BR ledgerpay . +This ledger groups the pay checks by employee, and is used to create +the W2 form. +.SH Example +Assume that table +.B journalpay +has the following data: +.DM +Date Number Hours Salary Rate Gross Federal State Net +---- ------- ------ ------ ---- ------ ------- ----- --- +860518 1 80 1000 0 1000 70 10 920 +860518 2 80 0 10 800 42 6 752 +860518 3 30 0 10 300 0 0 300 +.DE +.PP +Typing +.B calcpay +copies it to table +.BR calcpay . +.SH "See Also" +.B +act, +getjournal +.R +.SO calcpay diff --git a/man/calculate.man b/man/calculate.man new file mode 100644 index 0000000..bf9e885 --- /dev/null +++ b/man/calculate.man @@ -0,0 +1,30 @@ +.TH calculate "" "" "/act/gl Command" +.PC "Compute each tax form listed" +\fBcalculate \fItaxform ...\fR +.PP +.II "tax form^calculate" +.II "calculate tax form" +The command +.B calculate +is part of the accounting/general-ledger system that is included with +.BR /rdb . +It is a simple shell script that calculates each tax form +listed on its command line. +.SH Example +The following command calculates the IRS 1040 form +.DM + calculate 1040 +.DE +.SH "See Also" +.B +act, +fillform +.R +.SH Notes +Please note that the tax tables included with +.B /rdb +are from 1987. +They are included solely to serve as examples, and no claim is made as to +their accuracy when calculating current taxes. +.IR "Caveat utilitor" . +.SO calculate diff --git a/man/cap.man b/man/cap.man new file mode 100644 index 0000000..cdd8b2b --- /dev/null +++ b/man/cap.man @@ -0,0 +1,54 @@ +.TH cap "" "" "/rdb Command" +.PC "Convert first letter of each word to upper case" +\fBcap < \fItextfile\fR +.PP +.II "text^capitalize" +.II "capitalize text" +The command +.B cap +capitalizes the first letter of each word in +.IR textfile . +It writes its results to the standard output. +.SH Example +Assume that table +.B Inventory +has the following data: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +As you can see, the column +.B Descriptions +has consists of words all in lower case. +To capitalize the first letter of each word in +.B Inventory +file, type: +.DM + cap < Inventory +.DE +.PP +This writes the following on the screen: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 Rubber Gloves + 2 100 5 500 Test Tubes + 3 5 80 400 Clamps + 4 23 19 437 Plates + 5 99 24 2376 Cleaning Cloth + 6 89 147 13083 Bunsen Burners + 7 5 175 875 Scales +.DE +.SH "See Also" +.B +lowercase, +uppercase +.R diff --git a/man/cashflow.man b/man/cashflow.man new file mode 100644 index 0000000..0d0f207 --- /dev/null +++ b/man/cashflow.man @@ -0,0 +1,180 @@ +.TH cashflow "" "" "/rdb Command" +.PC "Compute balance column of cash table" +\fBcashflow < \fIcashtable\fR +.PP +.II "compute cash flow" +.II "cash flow" +The command +.B cashflow +helps you manage your cash flow. +It reads +.IR cashtable , +which you design, +then computes a running balance in a +.I Balance +column (which must be column 3) from an +.I Amount +column (which must be column 2). +.PP +Cash-flow analysis lets you see if you will always have enough money +in hand to cover each day's bills. +It makes cash management possible by +telling you whether to put off an expense until a check arrives, +ask for an advance, get to work and make more money, +or control your spending. +.SH Example +The following gives an example +.IR cashtable , +called +.BR cash : +.DM +Date Amount Balance Description +------ ------ ------- ----------- +890101 512 current balance +890101 -450 rent check +890101 1000 pay check +890115 -300 estimated tax payment +890115 1000 pay check +890115 -1000 living expenses +890120 -900 big purchase +.DE +.DM +890201 1000 pay check +890201 -450 rent check +890215 1000 pay check +890115 -1000 living expenses +.DE +.PP +The command +.B cashflow +needs two columns, named +.B Amount +and +.B Balance. +(If you use different column names, you must edit the +.B cashflow +shell script). +All other columns are optional. +Each row in your table represents +a projected income item (positive) or a projected expense (negative). +This is your flow of cash. +To summarize the cash flow in our example table +.BR cash , +type: +.DM + cashflow < cash +.DE +.PP +This prints the following on your screen: +.DM +Date Amount Balance Description +------ ------ ------- ----------- +890101 512 512 current balance +890101 -450 62 rent check +890101 1000 1062 pay check +890115 -300 762 estimated tax payment +890115 1000 1762 pay check +890115 -1000 762 living expenses +890120 -900 -138 big purchase +.DE +.DM +890201 1000 862 pay check +890201 -450 412 rent check +890215 1000 1412 pay check +890115 -1000 412 living expenses +.DE +.PP +That +.B "big purchase" +gives us a negative balance in 890120. +Now that you know, you can do something about it. +One move is to put the big purchase off +until the first of February. +After you edit table +.B cash +to reflect this change, it appears as follows: +.DM +Date Amount Balance Description +------ ------ ------- ----------- +890101 512 current balance +890101 -450 rent check +890101 1000 pay check +890115 -300 estimated tax payment +890115 1000 pay check +890115 -1000 living expenses +890201 1000 pay check +890201 -900 big purchase +890201 -450 rent check +890215 1000 pay check +890115 -1000 living expenses +.DE +.PP +Running +.B cashflow +again will tell us if this strategy works: +.DM +Date Amount Balance Description +------ ------ ------- ----------- +890101 512 512 current balance +890101 -450 62 rent check +890101 1000 1062 pay check +890115 -300 762 estimated tax payment +890115 1000 1762 pay check +890115 -1000 762 living expenses +890201 1000 1762 pay check +890201 -900 862 big purchase +890201 -450 412 rent check +890215 1000 1412 pay check +890115 -1000 412 living expenses +.DE +.PP +As you can see, this works \(em there's no negative cash flow for any +period. +You can also try other options. +.PP +You can do all of the inputting and editing in the text editor. +You can execute commands in +.B vi +by using the exclamation `!' shell feature; +or from MicroEMACS by typing \fB!\fR. +You also might want to write out to a +.I tmp +file and use +.B mv +to overwrite your +.B cash +table so that it is +file is up to date. +For example, in +.B vi +you can type: +.DM + :!cashflow < cashtable > tmp ; mv tmp cashtable + :e! +.DE +.PP +The first line computes the +.B Balance +column, writes the result into a +.B tmp +file, and moves +.B tmp +to be the new +.B cashtable. +All of this is necessary because +in COHERENT you cannot have one file +as both input and output without wiping out the file. +The second line, +.B :e! +pulls the new file into the +.B vi +editor and displays it on your screen. +.SH "See Also" +COHERENT Lexicon: +.B +ksh, +me, +sh, +vi +.R +.SO cashflow diff --git a/man/chartdup.man b/man/chartdup.man new file mode 100644 index 0000000..5819257 --- /dev/null +++ b/man/chartdup.man @@ -0,0 +1,25 @@ +.TH chartdup "" "" "/act/gl Command" +.PC "Check for duplicate names and accounts in chart" +.B chartdup +.PP +.II "duplicate name^check for" +The command +.B chartdup +is part of the accounting/general-ledger system that is included with +.BR /rdb . +It checks to see if there are any duplicates among the +account numbers or the short names within the chart of accounts. +This is the most common error that people make when they update the +chart of accounts. +.SH Example +The following gives example output of +.BR chartdup : +.DM +Duplicate Account numbers in chart. See chart.Account +4150 I-E 115 taxes Taxes +4150 I-E 115 taxes Taxes +Duplicate Short names in chart. See chart.Short +4150 I-E 115 taxes Taxes +4150 I-E 115 taxes Taxes +.DE +.SO chartdup diff --git a/man/check.rdb.man b/man/check.rdb.man new file mode 100644 index 0000000..5d6d956 --- /dev/null +++ b/man/check.rdb.man @@ -0,0 +1,156 @@ +.TH check.rdb "" "" "/rdb Command" +.PC "Report any rows in which columns do not match head line" +\fBcheck.rdb < \fItableorlist\fR +.PP +.II "column^count" +.II "count columns" +.II "column^verify" +.II "verify columns +The command +.B check.rdb +counts the columns (number of tabs plus one) in the head line of an +.B /rdb +table or list formatted file. +Then it displays information on each row that does not match +the number of columns in the first head-line row. +You should use it to see if your data entry is correct. +If your head-line row is incorrect, then all of your rows will +be reported as errors. +.PP +.B check.rdb +returns zero when all is correct, and nonzero when +it has found an error. +You can use this status value (\fB$?\fR) in +a shell script to take different actions, depending upon +the validity of the table or list file. +.SH Examples +Here is a bad table, called +.BR badtable : +.DM +Date Account Debit Credit Description +------ ------- ------ ------ ----------- +820102 101 25000 cash from loan +820102 211.1 25000 loan #378-14 Bank Amerigold +820103 150.1 10000 test equipment from Zarkoff +820103 101 5000 cash payment +820103 211.2 5000 note payable to Zarkoff +820104 130 30000 inventory - parts from CCPSC +820104 201.1 15000 accounts payable to CCPSC +820104 101 15000 cash payment to CCPSC for parts +.DE +.PP +It is hard to see anything wrong with it. +However, when we run the command +.DM + check.rdb < badtable +.DE +.PP +we see the following output: +.DM +check.rdb: Columns (4) do not equal headline columns (5) in row 3 (line 5). +820103 150.1 10000 test equipment from Zarkoff +.DE +.PP +The command +.DM + see < badtable +.DE +.PP +gives us the following output: +.DM +Date^IAccount^IDebit^ICredit^IDescription$ +----^I ------^I-----^I------^I-----------$ +820102^I101^I25000^I^Icash from loan$ +820102^I211.1^I^I25000^Iloan number #378-14 Bank Amerigold$ +820103^I150.1\ \ \ 10000^I^Itest equipment from Zarkoff$ +820103^I101^I^I5000^Icash payment$ +820103^I211.2^I^I5000^Inote payable to Zarkoff Equipment$ +820104^I130^I30000^I^Iinventory - parts from CCPSC$ +820104^I201.1^I^I15000^Iaccounts payable to CCPSC^I$ +820104^I101^I15000^I^Icash payment to CCPSC for parts$ +.DE +.PP +Note that a tab is missing from row 3 (line 5) of +.BR badtable , +between values 150.1 and 10000. +The spaces hide the fact that it is missing. +.PP +When you work with a large file, run +.B check.rdb +first. +It will find the tab problems and give you the information you need to +find them. +Then use a text editor to correct the problem. +.\"When using +.\".BR vi , +.\"turn on the number +.\"option so you can see line numbers: +.\".DM +.\" vi table +.\".sp +.\" :set number +.\".DE +.PP +You can move down to the first bad line by typing the following +.B vi +command +.BR :5 . +This takes you to line 5. +Then to see the line, type the list command +.BR l: . +(This is the letter +.BR el , +not the number one). +Or to do it in one step (move and list), type +.BR :5l . +Or you can using the following colon command +to set all of the lines in the file to display tabs: +.DM + :set list +.DE +.PP +To return it to normal display, use the command: +.DM + :set nolist +.DE +.PP +Edit in whatever changes you need, then go to the next bad line. +You can change a space to a tab with the following command: +.DM + :s/ /^I/ +.DE +.PP +where the +.B ^I +is the key on your keyboard that produces a tab. +If you want to list three lines above and below the bad line, type: +.DM + :-3,+3l +.DE +.PP +If you use \fBve\fP to enter your data, you will +have fewer problems like this. +Data from foreign sources should +be checked carefully before you use it with \fB/rdb\fR. +.SH "See Also" +.B +see, +ve +.R +.br +COHERENT Lexicon: +.B +elvis, +me, +vi +.R +.SH Notes +This command is named +.B check.rdb +rather than +.BR check , +as in other implementations of +.BR /rdb , +to avoid clashing with the COHERENT command +.BR check , +which does something very different. diff --git a/man/chr.man b/man/chr.man new file mode 100644 index 0000000..6122ffd --- /dev/null +++ b/man/chr.man @@ -0,0 +1,41 @@ +.TH chr "" "" "/rdb Command" +.PC "Display the character corresponding to a number" +\fBchr \fInumber ...\fR +.PP +.II "integer^convert to ASCII" +.II "convert integer to ASCII" +.II "ASCII^convert from integer" +.B chr +converts each of the integers on +its command line to its corresponding ASCII character. +You can use it to send special characters to the screen. +This gives you a good way to produce special characters that are +hard to type or are interpreted by the shell. +.SH Example +Here we convert several characters. +.DM + chr 97 65 49 + aA1 +.DE +.DM + chr 7 + [beep] +.DE +.DM + echo `chr 7`Wake up + [beep]Wake up +.DE +.PP +The +.B [beep] +is the sound. +It does not print on your terminal. +.SH "See Also" +.B +ascii +.R +.br +COHERENT Lexicon: +.B +ASCII +.R diff --git a/man/clear.rdb.man b/man/clear.rdb.man new file mode 100644 index 0000000..3c65c32 --- /dev/null +++ b/man/clear.rdb.man @@ -0,0 +1,95 @@ +.TH clear.rdb "" "" "/rdb Command" +.PC "Clear the terminal's screen" +.B clear.rdb +.PP +.II "screen^clear" +.II "clear screen" +The command +.B clear.rdb +clears your terminal screen of all characters and leaves the +cursor in the upper left corner. +It is useful for menus and forms that look better on a clear +screen. +.PP +.II termcap +.II TERM +.B clear.rdb +uses the +.B termcap +file of terminal capabilities to find the string of special +characters that are needed to clear your screen. +To work correctly, the +.B TERM +environmental variable must be set correctly to the name of your +terminal as listed in +.BR /etc/termcap . +.PP +The COHERENT command +.B clear.rdb +also clears the screen. +.PP +The +.B /rdb +command +.B termput +can also clear the screen, as follows: +.DM + termput cl +.DE +.\".PP +.\"The difference is that +.\".B termput +.\"uses +.\".BR /etc/termcap , +.\"whereas +.\".B tput +.\"use the +.\".I terminfo +.\"directory. +.SH Speed +.B clear.rdb +is quite fast, but there is a much faster way. +Set a shell variable +.B CLEAR +to the output of the command +.B clear.rdb +like this: +.DM + export CLEAR=`clear` +.DE +.PP +Then you can use: +.DM + echo "$CLEAR" +.DE +.PP +in your shell programs for a fast clear. +Put it into your +.B .profile +so that it will always be available. +Use quotation marks around the shell variables in case they +have special characters in them. +.SH "See Also" +.B +termput +.R +.br +COHERENT Lexicon: +.B +clear, +export, +\&.profile, +sh, +TERM, +termcap +.R +.SH Notes +This command is named +.B clear.rdb +rather than +.BR clear , +as in other implementations of +.BR /rdb , +to avoid clashing with the COHERENT command +.BR clear . +.SO clear diff --git a/man/close.man b/man/close.man new file mode 100644 index 0000000..738ead0 --- /dev/null +++ b/man/close.man @@ -0,0 +1,36 @@ +.TH close "" "" "/act/gl Command" +.PC "Close accounting period creating journal for next" +.B close +.PP +.II "accounting period^close" +.II ledger^close +The command +.B close +is part of the accounting/general-ledger system that is included with +.BR /rdb . +It closes an accounting period, such as a month, quarter, or year, and +creates the next period's journal, the table +.BR journalnext . +When you start the next period, carry the +.B journalnext +forward by \fBmv\fRing it to file +.BR journallast . +.SH Example +The command +.B close +creates a file whose output resembles the following: +.DM +Account Date Debit Credit Ref Description +------- ------- ----- ------ ------- --------------- +1010 850101 11178 jl brought forward +1110 850101 22130 jl brought forward +1310 850101 0 jl brought forward +1330 850101 0 jl brought forward +1420 850101 0 jl brought forward +4997 850101 33308 jl brought forward +.DE +.SH "See Also" +.B +act +.R +.SO close diff --git a/man/column.man b/man/column.man new file mode 100644 index 0000000..be93db0 --- /dev/null +++ b/man/column.man @@ -0,0 +1,125 @@ +.TH column "" "" "/rdb Command" +.PC "display columns of a table in any order" +\fBcolumn [ \fIColumn ...\fB ] < \fItableorlist\fR +.PP +.II "column^select" +.II "column^reorder" +.II "select columns" +.II "reorder columns" +The command +.B column +reads +.I tableorlist +and writes a new table (or list) that consists +of each +.IR Column , +in the order you list them +on the command line. +.PP +If you give a column name that is not one of the columns for +.IR tableorlist , +.B column +creates a new column with that name, in that +location, and leaves it empty. +This lets you to create new columns. +If you want to compute a column that does not exist, use this +.B column +facility to create it, then use the +.B compute +command to compute it. +However, if you misspell a column name, you will get an empty column. +.PP +If you name no +.I Column +on the command line, +.B column +writes all of +.I tableorlist +to the standard output without change. +.SH Example +Assume that you have an inventory table, as follows: +.DM +Item Amount Cost Value Description +---- ------ ----- ----- ----------- + 1 3 5.00 0 rubber gloves + 2 100 0.50 0 test tubes + 3 5 8.00 0 clamps + 4 23 1.98 0 plates + 5 99 2.45 0 cleaning cloth + 6 89 14.75 0 bunsen burners + 7 5 175 0 scales +.DE +.PP +The command: +.DM + column Cost Amount Description < inventory +.DE +.PP +produces the following output: +.DM + Cost Amount Description +----- ------ ----------- + 5.00 3 rubber gloves + 0.50 100 test tubes + 8.00 5 clamps + 1.98 23 plates + 2.45 99 cleaning cloth +14.75 89 bunsen burners + 175 5 scales +.DE +.PP +Note that the columns +.B Cost +and +.B Amount +have been reversed. +We only have the columns we asked for and in the order we requested. +.PP +.B column +can also manipulate list-formatted files. +For example, the mailing list called +.I maillist +looks like this: +.DM +Number 1 +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.sp +Number 2 +Name Chiquita Banana +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 +.DE +.PP +If you want only the names and numbers, type: +.DM + column Name Phone < maillist +.DE +.PP +This produces the following output: +.DM +Name Ronald McDonald +Phone (111) 222-3333 +.sp +Name Chiquita Banana +Phone 1234 +.DE +.SH "See Also" +.B +project, +row +.R +.br +COHERENT Lexicon: +.B +awk +.R diff --git a/man/commands.man b/man/commands.man new file mode 100644 index 0000000..1946a69 --- /dev/null +++ b/man/commands.man @@ -0,0 +1,19 @@ +.TH commands "" "" "/rdb Command" +.PC "Describe /rdb commands" +.B commands +.PP +.II "command^show /rdb commands" +The command +.B commands +displays a table of +.B /rdb +commands, with a one-line description and the syntax of each command. +.SH "See Also" +.B +act, +helpme, +rdb, +whatis, +whatwill +.R +.SO commands diff --git a/man/compresss.man b/man/compresss.man new file mode 100644 index 0000000..99b51c6 --- /dev/null +++ b/man/compresss.man @@ -0,0 +1,96 @@ +.TH compresss "" "" "/rdb Command" +.PC "Squeeze out all leading and trailing blanks" +\fBcompresss [-b] < \fItableorlist\fR +.PP +.II "table^compress" +.II "compress table" +The command +.B compresss +removes all leading and trailing blanks from every column or field in +.IR tableorlist . +You an use it to save disk space. +.PP +Please note that the name \fBcompresss\fR is not a typographical error. +The command is spelled in this way to avoid collision with the +COHERENT command +.BR compress , +which behaves very differently from +.BR compresss . +.PP +.B compresss +is almost the opposite of the command +.BR justify . +Your files are not as pretty when compressed as they are when +justified, but they are smaller \(em +usually one third the size or less. +Therefore, they take less disk space and are much faster to process; +after all, +most of the time used to execute data-base programs consists +of moving data from the disk to the memory and, sometimes, back to +the disk. +.PP +Multiple blanks that separate words are left alone. +.SH Options +.B compresss +recognizes the following option: +.IP \fB\-b\fR +Drop blank lines. +Don't use this option with list files! +.SH Example +For example, consider table +.BR oldjournal , +which is as follows: +.DM +Date Account Debit Credit Description +------ ------- ----- ------ ----------- +890102 101 25000 cash from loan +890102 211.1 25000 loan number #378-14 Bank Amerigold +890103 150.1 10000 test equipment from Zarkoff +890103 101 5000 cash payment +890103 211.2 5000 note payable to Zarkoff Equipment +890104 130 30000 inventory - parts from CCPSC +890104 201.1 15000 accounts payable to CCPSC +890104 101 15000 cash payment to CCPSC for parts +.DE +.PP +After we run the command +.DM + compresss number) column2 = \e + sprintf("%d", column2+1)' < tableorlist +.DE +.SH Expressions +As you can see, you can write rather complex expressions for +.BR compute . +By +.I expression, +we mean many kinds of equations. +For example, a variable or a column is an expression. +So is variable + variable. So are rather complex equations. +The following symbols indicate some of the more complex expressions: +.IP \fBcolumn\fR 0.3i +Name of a column, exactly as it appears at the top of the column. +.IP \fB\(eq\fR +Make the column on the left equal the expression on the right. +.IP \fB+\fR +Add +.IP \fB\-\fR +Subtract +.IP \fB*\fR +Multiply +.IP \fB/\fR +Divide. +.IP \fB%\fR +Modulo (zero if left value is equally divisible by right). +.IP \fB;\fR +Statement separator needed if more than one equation +.IP \fBlog()\fR +Natural logarithm of value or column within parentheses. +.IP \fBlength()\fR +String length of string in column. +.PP +You can also use C language-like statements: +.IP "\fBif (\fIexpression\^\fB) \fIequation\fB [ else \fIequation\fB ];\fR" +For example: +.DM +if (Value > 10000) Status = "Special"; else Status = "Normal"; +.DE +.IP "\fBwhile (\fIexpression\fB) \fIequation\fB;\fR +For example: +.DM +while (column => 0) column -= 1 ; +.DE +.IP "\fBfor (\fIexpression\fB; \fIcondition\fB; \fIexpression\fB) \fIequation\fB;\fR" +For example: +.DM +for (i = 0; i < column1; i++) array [i] = column2 + i ; +.DE +.PP +You can also use the +.B awk +format \fIpattern \fB{ \fIaction \fB}\fR. +For example: +.DM + compute 'length > 80 { print NR, "Line too long." }' < table +.DE +.PP +.B awk +recognizes +.I length +as a function that returns the length of the row and +.B NR +as the line number. +Note that the line numbers of a file starts with the head line, +while row numbers of a table starts after the dash line (line 3). +.SH "What Is a Column Name?" +.B compute +looks up, in the column header of the input file, +each word that it finds in its command-line program. +If there is a match, +.B compute +converts the column name to its position (first, second, third, etc.) +depending upon the relative location of the column. +These column numbers are needed by +.BR awk . +.PP +.B compute +defines a column name to be a string of the following characters: +.IP \fB1.\fR 0.3i +Upper and lower letters, numbers, and the underscore `_'. +.IP \fB2.\fR +Any string that is enclosed by apostrophes or quotation marks. +.PP +Therefore, if you want special characters in your column names, +put quotation marks around them when you use them. +For example: +.DM + compute ' "Item#" = NR ' < inventory +.DE +.PP +Note the quotation marks around the column head +.B Item# +because of the number character `#'. +Also, note that we used quotation marks, +because the whole program was enclosed in apostrophes. +If you use one kind of mark around the whole program, +use the other kind of mark around the column names +that have special characters in them. +.SH "Reserved Words to Avoid in Column Names" +Some words are understood by +.BR awk . +Therefore, they should not be used in column names. +A simple way to avoid a conflict is to start your column +names with a capital letter. +.B awk +does not confuse +.B Print +with +.BR print . +Here is a list of +.BR awk 's +built-in functions. +.nf +.sp \n(pDu +.ta 1.6i +\fIReserved Words Description\fR +.tc . +\fBBEGIN\fR Pattern that matches before first input record +\fBEND\fR Pattern that matches after last input record +\fBbreak\fR Get out of for or while loop +\fBcontinue\fR Go to next iteration of for or while loop +\fBelse\fR Used in "if then else" expression +\fBexit\fR Leave program entirely as if end of input +\fBexp\fR Raise number to a power +\fBfor\fR for ( expression ; condition ; expression ) statement +\fBgetline\fR Get next input line +\fBif\fR if ( condition ) statement [ else statement ] +\fBin\fR for ( variable in array ) statement +\fBindex\fR index ( string1, string2 ) +\fBint\fR Truncate argument to integer +\fBlength\fR Return current line length, or length of argument +\fBlog\fR Return log (to base 2) of argument +\fBnext\fR Skip to next record and reexecute all commands +\fBprint\fR Output variables +\fBprintf\fR printf ("format", variable, ... ) +\fBsplit\fR split ( string, arrayname, separator) +\fBsprintf\fR sprintf ("format", variable, ... ) +\fBsqrt\fR Return square root of argument +\fBsubstr\fR substr ( string, start, number ) +\fBwhile\fR while ( condition ) statement +.tc +.fi +.SH "Using Shell Variables in Programs" +Often you want to use a shell variable in a +.B compute +command. +It is easy, if you understand what is going on. +For example, the following commands: +.DM + DATE=860101 + compute "Date = $DATE" < journal +.DE +.PP +produce the following output: +.DM +Date Account Debit Credit Description +------ ------- ----- ------ ---------------------------------- +860101 101 25000 cash from loan +860101 211.1 25000 loan number #378-14 Bank Amerigold +860101 150.1 10000 test equipment from Zarkoff +860101 101 5000 cash payment +860101 211.2 5000 note payable to Zarkoff Equipment +860101 130 30000 inventory - parts from CCPSC +860101 201.1 15000 accounts payable to CCPSC +860101 101 15000 cash payment to CCPSC for parts +.DE +.PP +We set a shell variable named +.B DATE +to a date. +Then we used the variable in the +.B compute +command line program. +But note that we had to use quotation marks (") instead of our +usual apostrophes ('). +To the shell, apostrophes protect absolutely. +With apostrophes, the shell would not see the dollar sign `$' in front of +.B DATE +and would not convert it to its value. +The quotation mark protects the enclosed from the shell, also, +except for shell variables and command substitution (`cmd`). +With quotation marks, the shell will still replace variables +(which start with a dollar sign character). +.PP +There is one further complication. +The previous worked fine because the value of date was a number. +But if we want to use strings in +.BR awk , +they must have quotation marks (not apostrophes) around them. +For example: +.DM +COMPANY='Makeapile, Inc.' +compute "Company = \e"$COMPANY\e"" < maillist +.DE +.PP +produces the following: +.DM +.ta 1.0i +Number 1 +Name Ronald McDonald +Company Makeapile, Inc. +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.DE +.DM +.ta 1.0i +Number 2 +Name Chikeeta Banana +Company Makeapile, Inc. +Street Uno Avenito De La Revolution +City San Jose +State El Salvadore +ZIP 123456789 +Phone 1234 +.DE +.PP +Here we set a shell variable +.B COMPANY +to a company name. +We want to update the mail list because these characters +have been hired by a new company. +Note what we had to do. +We needed quotation marks around the +whole program to let the shell substitute the value of the +shell variable. +But we also needed quotation marks around +the string ``Makeapile, Inc.'' for +.BR awk . +So we have to protect the innermost quotation marks with backslashes. +.PP +There is another way, which opens up a little window in +the program for the shell to insert strings. +But it fails when there are spaces in the window string. +So use the method previously discussed. +.SH "Problem With awk Floating-Point Format" +With some implementations of +.BR awk , +the value column is computed to much greater precision +than you are likely to want. +For example, version produces this table: +.DM +Item# Amount Cost Value Description +------- ------- ------- ------- --------------- + 1 3 5.00 15 rubber gloves + 2 100 0.50 50 test tubes + 3 5 8.00 40 clamps + 4 23 1.98 45.539997100830078125 plates + 5 99 2.45 242.5499725341796875 cleaning cloth + 6 89 14.75 1312.75 bunsen burners + 7 5 175.00 875 scales +.DE +.PP +To control the precision of the +floating-point values in a column, you can use the +.B sprintf +function from the C programming language that has been implemented in +.BR awk . +For example: +.DM +compute 'Value = sprintf ("%7.2f", ( Cost * Amount ))' < inventory +.DE +.PP +These commands will produce the first table under Examples, above. +.SH "See Also" +COHERENT Lexicon: +.B +awk, +ksh, +sh +.R diff --git a/man/computedate.man b/man/computedate.man new file mode 100644 index 0000000..d48849c --- /dev/null +++ b/man/computedate.man @@ -0,0 +1,78 @@ +.TH computedate "" "" "/rdb Command" +.PC "Add given number of days to a date" +\fBcomputedate \fIdate days\fR +.PP +.II "date^arithmetic" +.II "arithmetic^date" +The command +.B computedate +adds days to a date, and displays the new date. +.I date +must be the format \fIYYMMDD\fR; +.I days +can be negative or positive. +.B computedate +displays the new date in the same format. +This is the best format for entering dates in a column +because they sort correctly and take only six characters. +.PP +Internally, +.B computedate +converts the date to +.BR julian , +adds days, then converts the date back to +.BR gregorian . +Suppose we want to know the date 45 days from March 31, 1989. +We can type: +.DM + computedate 890331 45 +.DE +.PP +This command returns the string: +.DM + 890515 +.DE +.PP +That is May 15, 1989. +.PP +Another trick is to combine the +.B todaysdate +and the +.B computedate +programs. +.B todaysdate +gives the date of today in the preferred format of \fIYYMMDD\fR. +For example, the following command shows us the date 45 days from today: +.DM + computedate `todaysdate` 45 +.DE +.SH "Data Validation" +You can use +.B computedate +to validate a date. +If you add zero days to a date and get a different date, then +the original date was not valid. +For example, let's test the 32nd day of January 1989: +.DM + computedate 890132 0 +.DE +.PP +This returns the string +.B 890201 +or February 1, 1989. +As we expected, we received a different date. +We can test this with the following shell program: +.DM + if test $DATE = `computedate $DATE 0` + then + echo OK + else + echo Invalid DATE + fi +.DE +.SH "See Also" +.B +gregorian, +julian, +todaysdate +.R diff --git a/man/consolidate.man b/man/consolidate.man new file mode 100644 index 0000000..d33af55 --- /dev/null +++ b/man/consolidate.man @@ -0,0 +1,64 @@ +.TH consolidate "" "" "/act/gl Command" +.PC "Combine all subsidiary journals to general journal" +\fBconsolidate \fInextdate\fR +.PP +.II "consolidate journals" +.II "journal^consolidate" +The command +.B consolidate +is part of the accounting/general-ledger system that is included with +.BR /rdb . +It assembles all of the subsidiary journals, such as +.BR journalincome , +.BR journalexpense , +and +.BR journaldeposit , +into the general journal named +.BR journal . +It also converts from single-entry to double-entry bookkeeping +by totaling +.B Debits +and +.B Credits +and inserting counterbalancing adjustments to the cash account. +It needs the +.B nextdate +for these accounts. +.SH Example +The command +.DM + consolidate $NEXTDATE +.DE +.PP +writes into file +.B journal +data that resemble the following: +.DM +Date Account Debit Credit Ref Description +------ ------ ------- ------- ------ ---------------------- +890101 4370 2 r parking at clients +890103 4380 15 c115 shuttle bus to airport +890104 4380 13 v meal +890105 4380 13 c116 meal +890106 4384 114 r hotel +890118 3100 3286 c101 consulting +890125 3080 250 c1459 book fees +891231 1010 5858 double-entry +891231 1010 17036 double-entry +891231 3010 13500 deposit from sales/deposit +891231 4020 3601 check from pur/check +891231 4030 2100 payroll from pay/journalpay +.DE +.PP +Note the double-entry adjustments, and the totals of journals +from sales, purchasing, and pay departments. +The +.B Date +came from the +.B NEXTDATE +shell variable, which you must set in your +.B .profile. +.SH "See Also" +.B +getjournal +.R diff --git a/man/cpdir.rdb.man b/man/cpdir.rdb.man new file mode 100644 index 0000000..26d82ca --- /dev/null +++ b/man/cpdir.rdb.man @@ -0,0 +1,47 @@ +.TH cpdir.rdb "" "" "/rdb Command" +.PC "Copy one directory tree to another directory" +\fBcpdir.rdb \fIfromdirectory todirectory\fR +.PP +.II cpdir +.II "copy directory" +.II directory^copy +The command +.B cpdir.rdb +copies a directory tree \(em that is, all directories and +their files \(em from +.I fromdirectory +to +.IR todirectory . +.PP +.B cpdir.rdb +uses the tar command in a special way. +It is a shell program +so you can read it and see how it does it. +It performs almost exactly the same work as the COHERENT command +.BR cpdir . +.SH Example +You might use this to back up a large directory: +.DM + cpdir.rdb /usr/rdb /usr/rdb.backup +.DE +.PP +The following moves a directory to a preferred place: +.DM + cpdir.rdb /usr/he.left/goodstuff /usr/project/goodstuff +.DE +.SH "See Also" +COHERENT Lexicon: +.B +cpdir, +tar +.R +.SH Notes +This command is named +.B cpdir.rdb +rather than +.BR cpdir , +as in other implementations of +.BR /rdb , +to avoid clashing with the COHERENT command +.BR cpdir . +.SO cpdir.rdb diff --git a/man/cstate.man b/man/cstate.man new file mode 100644 index 0000000..1ebe351 --- /dev/null +++ b/man/cstate.man @@ -0,0 +1,63 @@ +.TH cstate "" "" "/act/sales" +.PC "Produce customer statement" +.B cstate +.PP +.II "customer statement" +The command +.B cstate +is part of the accounting/sales system that is included with +.BR /rdb . +It generates the customer statement from the tables +.B salesorder +and +.BR deposit . +It shows what the customer has ordered and paid, and the net. +The result is printed as a form to be sent to the customer as a bill. +.SH Example +The following gives an example of the output of +.BR cstate : +.DM +Please enter customer number (or all or Return to exit): \fB3\fP + Customer Statement + ------------------ +Makeapile, Inc., 123 Bigbucks Blvd., Dallas, TX 12345, 1-800-SOF-WARE +.DE +.DM +Mailing Address for Customer Number 3 +.DE +.DM +Ms Ute Unix Ph.D. President +.DE +.DM +UniUniUniUni Software Sellers +12345 Nixuni Street +Union, New Jersey 11111 USA +.DE +.DM +Now Due and Payable is your balance of: 107550.00 +.DE +.DM +Details of Your Orders and Payments +.DE +.DM +Cust Date Amount Ref +---- ------ --------- ---- + 3 850902 -6300.00 17 + 3 850902 -14175.00 16 + 3 850902 -22050.00 15 + 3 850902 -23625.00 12 + 3 850902 -37800.00 13 + 3 850903 -12600.00 23 + 3 860105 9000.00 3347 +---- ------ --------- ---- + -107550.00 +.DE +.DM +Please enter customer number (or all or Return to exit): +.DE +.SH "See Also" +.B +act, +vstate +.R +.SO cstate diff --git a/man/cursor.man b/man/cursor.man new file mode 100644 index 0000000..842899a --- /dev/null +++ b/man/cursor.man @@ -0,0 +1,136 @@ +.TH cursor "" "" "/rdb Command" +.PC "Move the cursor to the row and column requested" +\fBcursor \fIrow col \fB[ \fICURSOR \fB]\fR +.PP +.II cursor^move +.II "move cursor" +.II "reposition cursor" +The command +.B cursor +move the cursor to screen position +.I row +and +.IR column . +You can +.B cursor +and the +.B /rdb +command +.B clear.rdb +to perform complex screen handling from within a shell script. +You can paint your screen with cursor moves and +.B echo +commands; +then use the +.B read +command to read the user's input; and finally use various +COHERENT and +.B /rdb +commands to examine and confirm the input. +You can also clear fields of the screen by moving the cursor +and writing blanks. +.PP +You must give the row and column you want. +.B cursor +also needs the cursor movement (\fBcm\fR) entry from file +.BR /etc/termcap . +This can be given as an argument, but the easiest way +is to set up the shell variable called +.B CURSOR ; +then +.B cursor +will find it in its environment automatically. +To set up the +.B CURSOR +variable, do the following: +.DM + CURSOR=`termput cm` +.DE +.PP +.\"On UNIX System 5 Release 2, use +.\".B tput: +.\".sp +.\".nf +.\"$ CURSOR=`tput cup` +.\".fi +.\".sp +Another way is to use your text editor to find the entry in +.B /etc/termcap +and copy its +.B cm +entry for your terminal to your +.BR .profile : +.DM + CURSOR=^[[%p1%d;%p2%dH +.DE +.PP +This is the entry for the +.B pc +terminal. +The uparrow bracket (^[) is the ESC character (\fB\fR). +The function +.I tgoto +function from the +.B termcap +library +converts it into the proper escape string. +.SH Example +The following moves the cursor to the middle of an 80-by-24 screen: +.DM + cursor 11 38 +.DE +.SH Speed +.B cursor +is fast, but there is a much faster way. +Set a shell variable like +.B L22 +to move to the 22nd line: +.DM + L22=`cursor 23 0` +.DE +.PP +Then you can use: +.DM + echo "$L22$MESSAGE\ec" +.DE +.PP +in your shell programs for a fast cursor movement and to print a message. +Note the quotation marks around the messages in case there are +special characters. +Also note the +.IR \ec , +which tells +.B echo +not to print a carriage return. +.\"In older versions of UNIX +.\"it is +.\".I -n +.\"instead of +.\".I \ec. +.PP +An even better trick is to use the shell feature that tests +to see if a variable has been set and only gives it a value +when it needs one: +.DM + echo "${L22:=`cursor 22 0`}$MESSAGE\ec" +.DE +.PP +All of this magic causes the shell to check if +.B L22 +has a value. +If not, it executes the +.B cursor +command and assigns the value to +.B L22. +Then, if this line is called in a loop, the next time the +value will have been set and will be about 20 times faster. +.SH "See Also" +.B +termput +.R +.br +COHERENT Lexicon: +.B +\&.profile, +termcap +.R diff --git a/man/dashline.man b/man/dashline.man new file mode 100644 index 0000000..5f03d14 --- /dev/null +++ b/man/dashline.man @@ -0,0 +1,37 @@ +.TH "dash line" "" "" "/rdb Definition" +.PC +.PP +The +.I "dash line" +is the second line in an +.B /rdb +table. +It underscores each entry in the table's head line. +Each entry in the dash line must be separated by a tab character. +.SH Example +The table +.B inventory +appears as follows: +.DM +Item Amount Cost Value Description New +---- ------ ---- ----- -------------- ------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +The dash line is: +.DM +---- ------ ---- ----- -------------- ------- +.DE +.PP +Note that there is one entry in the dash line for each entry in the +head line. +.SH "See Also" +.B +head line, listtotable, tabletolist +.R diff --git a/man/datatype.man b/man/datatype.man new file mode 100644 index 0000000..2141e0a --- /dev/null +++ b/man/datatype.man @@ -0,0 +1,55 @@ +.TH datatype "" "" "/rdb Command" +.PC "Display the data type of each column selected" +\fBdatatype [ \-l ] [ \fIcolumn ... \fB] < \fItable\fR +.PP +.II "data type" +.II "type of data" +The command +.B datatype +displays the type of data contained in each +.IR column . +If you do not name a +.I column +on the command line, +.B datatype +calculates and displays all of +.IR table 's +columns. +.SH Options +The \fB\-l\fR option tells +.B datatype +to print the entire contents of +.IR table , +in addition to information about each +.IR column . +.SH Code +.IP \fB\-1\fR +Character strings with nonnumeric data. +.IP \fB0\fR +Integer number. +.IP \fB1,2...\fR +Floating-point number (number of digits to the right of the decimal). +.SH Example +The following shows a sample output of +.BR datatype : +.DM +Int Char Float2 Float4 +--- ---- ------ ------ +1 a 1.2 1.1234 +745 zzzz 8.54 12.3 +--- ---- ------ ------ + 0 -1 2 4 +.DE +.PP +Note that the +.I Int +column has only integers in it (code 0), the +.I Char +column has characters in it (code -1), and the two +.I Float +columns have different precisions (positive numbers codes). +Note that the float precision is the maximum. +.SH "See Also" +.B +ascii, chr, dbdict +.R diff --git a/man/dbase.man b/man/dbase.man new file mode 100644 index 0000000..3cf8c43 --- /dev/null +++ b/man/dbase.man @@ -0,0 +1,261 @@ +.TH "dBASE crossreference" "" "" "Technical Information" +.PC +.PP +.II dBASE +.II Ashton-Tate +.II "Borland International" +dBASE was developed and marketed by Ashton-Tate; +this company was purchased recently by Borland International. +.PP +The following table gives the +.B /rdb +and \*(UX equivalents for most common dBASE commands. +The slug in \fBbold\fR characters gives the dBASE command; the following +description gives the +equivalent in +.B /rdb +and \*(UX +commands: +.II "command^dBASE to /rdb" +.nf +.sp \n(pDu +\fBACCEPT Comment TO Variable\fR +.II echo +.II read + echo Comment ; read Variable +.sp \n(pDu +\fBAPPEND BLANK\fR + echo >> Fileout +.sp \n(pDu +\fBAPPEND FROM Filename FOR Condition\fR +.II row + row 'Condition' < Filename >> Fileout +.sp \n(pDu +\fBBROWSE\fR +.II more +.II scat +.II update + more, scat, update, or any text editor +.sp \n(pDu +\fBCANCEL\fR +.II stty + DEL (any character set with \fBstty\fR) +.sp \n(pDu +\fBCHANGE Range FIELD Fieldname FOR Condition\fR +.II compute + compute 'Range && Condition {Fieldname = Value}' < Filein +.sp \n(pDu +\fBCLEAR\fR +\fBCLEAR GETS\fR +.II clear + clear +.sp \n(pDu +\fBCONTINUE\fR +.II continue + continue [shell statement] +.sp \n(pDu +\fBCOPY TO Filename FIELD Fieldnames FOR Condition\fR +.II row +.II column + row 'Condition' < Filein | column Fieldnames > Filename +.sp \n(pDu +\fBCOUNT FOR Condition TO Variable FOR Condition\fR + row 'Condition' < Filein | tail +3 | wc -l +.sp \n(pDu +\fBCREATE Filename\fR +.II vi +.II ve + > Filename ; vi Filename ; ve Filename ; cmd > Filename +.sp \n(pDu +\fBDELETE RECORD Number\fR +.II sed +.II tail + (sed $NUM+1}q Filename ; tail +$NUM+3}) > tmp; mv tmp Fieldname +.sp \n(pDu +\fBDELETE NEXT Number\fR +\fBDELETE ALL\fR +.II mv + sed 2q Filename > tmp ; mv tmp Filename +.sp \n(pDu +\fBDELETE NEXT Number FOR Condition\fR +\fBDELETE FILE Fieldname\fR + > Filename +.sp \n(pDu +\fBDISPLAY Range FOR Bedingung Field OFF\fR + row 'Range && Dedingung' < Filename | column Fieldnames +.sp \n(pDu +\fBDISPLAY STRUCTURE\fR +.II for + for I in * do sed 2q; done +.sp \n(pDu +\fBDISPLAY MEMORY\fR +.II df +.II du + df ; du +.sp \n(pDu +\fBDISPLAY FILES ON Disk LIKE Datatype\fR +.II ls + ls *Datatype ; ls Disk/*Datatype +.sp \n(pDu +\fB) Program\fR + Program +.sp \n(pDu +\fBCASE Condition\fR +.II case + Condition) [shell case statement] +.sp \n(pDu +\fBOTHERWISE\fR + *) [shell case statement] +.sp \n(pDu +\fBDO WHILE Condition CR ENDDO\fR +.II while + while Condition CR do done +.sp \n(pDu +\fBEDIT\fR + vi, ve, update, ex, ed, me, other editors and commands +.sp \n(pDu +\fBECT\fR +\fBENDDO\fR + do [in shell while, until, for statements] +.sp \n(pDu +\fBERASE\fR + clear +.sp \n(pDu +\fBFIND Text\fR + /Text (in vi, ve, update) +.sp \n(pDu +\fBINDEX ON Fieldname TO Filename\fR +.II index + index Filename Fieldname ... +.sp \n(pDu +\fBUSE Filename INDEX Keyfield\fR +.II search + search Filename Keyfield +.sp \n(pDu +\fBINPUT Text To Variable\fR + Variable=Text +.sp \n(pDu +\fBINSERT\fR + sed ${NUM}q Filename; echo $RECORD; tail +$NUM Filename +.sp \n(pDu +\fBJOIN TO Filename FOR Condition FIELDS Fieldnames\fR +.II jointable + jointable Filename1 Filename2 + row 'Condition' < Filename2 | jointable Filename1 - + column Fieldnames < Filename2 | jointable Filename1 - +.sp \n(pDu +\fBColumn,Row SAY Comment GET Variable PICTURE\fR +.II tput + tput Column ; tput Row ; echo Comment ; READ Variable +.sp \n(pDu +\fBGO\fR +\fBGOTO\fR +\fBGO RECORD n\fR + ve nG +.sp \n(pDu +\fBGO Fieldname\fR + ve / +.sp \n(pDu +\fBGO TOP\fR + ve H +.sp \n(pDu +\fBGO BOTTOM\fR + ve L +.sp \n(pDu +\fBIF Condition Statement2 ELSE Statement2 ENDIF\fR + if Condition then Statement1 else Statement2 fi +.sp \n(pDu +\fBLOCATE FOR Condition\fR + row 'Condition' < Filename +.sp \n(pDu +\fBLOOP\fR + while, until, for [shell statements] +.sp \n(pDu +\fBMODIFY STRUCTURE\fR + column New Fieldnames < Filename +.sp \n(pDu +\fBNOTE\fR +\fBREMARK\fR + : Old Comment + # New Comment +.sp \n(pDu +\fBMOVE Old-Filename TO New-Filename\fR + mv Old-Filename New-Filename +.sp \n(pDu +\fBREPLACE Range Fieldname WITH Expression FOR Condition\fR +.II compute + compute 'Fieldname = Expression' < Filename + compute 'Range && Condition {Fieldname = Expression}' < Filename +.sp \n(pDu +\fBREPORT Form FOR Condition TO PRINT\fR +.II report + report Form < Filename + row 'Condition' < Filename | report Form | print +.sp \n(pDu +\fBPACK\fR +.II pack + compress, uncompress [\*(UX] +.sp \n(pDu +\fBQUIT\fR + \fB\fR +.sp \n(pDu +\fBREAD\fR +.II read + read Variable +.sp \n(pDu +\fBRECALL Condition\fR + row 'Condition' < Filename +.sp \n(pDu +\fBRELEASE Variable\fR + Variable= +.sp \n(pDu +\fBRESET\fR +\fBRESTORE FROM Filename\fR +\fBRETURN\fR +.II return + return [shell] +.sp \n(pDu +\fBSAVE TO Filename\fR +\fBSELECT\fR + row, column, etc. +.sp \n(pDu +\fBSET\fR +.II set + set [shell] +.sp \n(pDu +\fBSKIP\fR +\fBSORT ON Fieldname TO Filename\fR + sorttable Fieldname > Filename +.sp \n(pDu +\fBSTORE Expression TO Variable\fR + Variable=`Expression` +.sp \n(pDu +\fBSUM Fieldname TO Variable FOR Condition\fR + Variable=`row 'Condition' < Filename | column Fieldname` +.sp \n(pDu +\fBTOTAL ON Keyfield TO Filename FIELDS Fieldnames FOR Condition\fR +.II subtotal + row 'Condition' | subtotal Keyfield Fieldnames > Filename +.sp \n(pDu +\fBUPDATE FROM Filename ON Keyfield\fR + update, vi, ve, replace, append, delete, etc. +.sp \n(pDu +\fBUSE Filename\fR + cmd < Filename +.sp \n(pDu +\fBWAIT\fR + wait [shell] +.sp \n(pDu +\fB@ Row,Column SAY expression\fR + cursor Row Column; echo EXPRESSION + ve screen and validation file +.fi +.PP +All data-base systems have some method for +importing text files. +Sometimes you'll be able to +use the header information that comes with +.B /rdb +tables, +and sometimes you'll have to specify this information +to the target system anyway. diff --git a/man/dbdict.man b/man/dbdict.man new file mode 100644 index 0000000..2a6ae45 --- /dev/null +++ b/man/dbdict.man @@ -0,0 +1,46 @@ +.TH dbdict "" "" "/rdb Command" +.PC "Print a data-base dictionary" +\fBdbdict < \fItable\fR +.PP +.II "data dictionary" +.II "column^position" +.II "column^number" +The command +.B dbdict +prints a table of two columns: +The first gives the names of each field in +.IR table , +and the second gives the field numbers. +.SH Example +For example, we might have a table named +.I journal +that looks like this: +.DM +Date Account Debit Credit Description +------ ------- ----- ------ ---------------------------------- +861222 101 25000 cash from loan +861222 211.1 25000 loan number #378-14 Bank Amerigold +861223 150.1 10000 test equipment from Zarkoff +861223 101 5000 cash payment +861223 211.2 5000 note payable to Zarkoff Equipment +861224 130 30000 inventory - parts from CCPSC +861224 201.1 15000 accounts payable to CCPSC +861224 101 15000 cash payment to CCPSC for parts +.DE +.PP +The command +.DM + dbdict < journal +.DE +.PP +produces the following: +.DM +field name +----- ---- +1 Date +2 Account +3 Debit +4 Credit +5 Description +.DE +.SO dbdict diff --git a/man/delete.man b/man/delete.man new file mode 100644 index 0000000..7bbc4df --- /dev/null +++ b/man/delete.man @@ -0,0 +1,96 @@ +.TH delete "" "" "/rdb Command" +.PC "Blank record and update index file" +\fBdelete [-m[bhirs]] \fItable keycolumn ... \fB< \fIkeytable\fR +.PP +.II row^delete +.II "delete row" +The command +.B delete +writes a blank record on the row in +.I table +whose key matches the row in the +.IR keytable . +It also deletes the offset row in the appropriate fast-access +offset table, or, in the case of the +.B hash +method, replaces the deleted offset with \-1. +.PP +Options +.B \-m[bhirs] +lets you select a type of fast-accessing to locate the row to delete. +For information on the type of fast accessing that each option invokes, +see the manual entry for +.BR index . +.SH Example +Consider table +.BR inventory , +which appears as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +The +.B record +index looks like this: +.DM + Offset +-------- + 76 + 113 + 147 + 177 + 207 + 245 + 283 +.DE +.PP +The following command deletes the first record +.BR Item 1: +.DM + echo 1 | delete -mr inventory Item +.DE +.PP +.B inventory +now appears as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +.B inventory.r +now appears as follows: +.DM + Offset +-------- + 113 + 147 + 177 + 207 + 245 + 283 +.DE +.PP +Note that we have blanked out the record and removed the +offset. +.SH "See Also" +.B +append, +index, +replace +.R +.SO delete diff --git a/man/difference.man b/man/difference.man new file mode 100644 index 0000000..4fc89ab --- /dev/null +++ b/man/difference.man @@ -0,0 +1,88 @@ +.TH difference "" "" "/rdb Command" +.PC "Output table of rows that are in only one table" +\fBdifference \fItable1 table2\fR +.PP +.II table^difference +.II table^subtract +.II "subtract tables" +.II sort +.II uniq +The command +.B difference +uses the COHERENT commands +.B sort +and +.B uniq +to subtract logically +.I table2 +from +.IR table1 . +Its output consists of the rows that are in +.I table1 +but not in +.IR table2 . +The two input tables must have the same columns +(\fIunion compatible\fR in the technical data-base literature), +and the rows must be exactly identical \(em including every space, tab, +and nonprinting character. +If two rows look the same to you but not to +.BR difference , +try the +.B see +command to discover any blank spaces and nonprinting characters. +.SH Example +In this example, consider the table +.BR journal , +which appears as follows: +.DM +Date Amount Account Ref Description +------- ------- ------- ------- -------------------------------- +820107 14.00 meal v meal with jones +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate +820114 93.00 car c car registration dmv +820119 81.72 vitamin c sundown vitamins +820121 20.83 meal v meal with scott +820121 2500.00 keogh c keogh payment +820125 99.00 dues v dues to uni-ops unix conference +.DE +.PP +And consider the table +.BR carexpense , +which appears as follows: +.DM +Date Amount Account Ref Description +------- ------- ------- ------- -------------------------------- +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate +820114 93.00 car c car registration dmv +.DE +.PP +The command +.DM + difference journal carexpense +.DE +.PP +logically ``subtracts'' +.B carexpense +from +.BR journal , +to produce the following output: +.DM +Date Amount Account Ref Description +------- ------- ------- ------- -------------------------------- +820107 14.00 meal v meal with jones +820119 81.72 vitamin c sundown vitamins +820121 20.83 meal v meal with scott +820121 2500.00 keogh c keogh payment +820125 99.00 dues v dues to uni-ops unix conference +.DE +.SH "See Also" +COHERENT Lexicon: +.B +diff, +diff3, +sort, +uniq +.R +.SO difference diff --git a/man/display.man b/man/display.man new file mode 100644 index 0000000..bdc718e --- /dev/null +++ b/man/display.man @@ -0,0 +1,35 @@ +.TH display "" "" "/rdb Command" +.PC "Write table or list file to standard output" +\fBdisplay < \fItableorlist\fR +.PP +.II "file^copy" +.II "file^write" +.II "copy file" +.II "write file" +.II "standard input" +.II "standard output" +The command +.B display +copies +.I tableorlist +from the standard input to the standard output. +This is not a very useful program, but is a template for the +C interface programs. +If you have its source code, +you have an example of how to process tables and lists +and can modify it for your own programs. +.PP +.II cat +.B display +is slower than the COHERENT command +.B cat +because it does more processing. +.B display +knows about table and list formats, and reads and writes +one row at a time. +.SH "See Also" +COHERENT Lexicon: +.B +cat, +more +.R diff --git a/man/domain.man b/man/domain.man new file mode 100644 index 0000000..a6026ab --- /dev/null +++ b/man/domain.man @@ -0,0 +1,150 @@ +.TH domain "" "" "/rdb Command" +.PC "Display invalid values in a column" +\fBdomain \fIdomaintable \fB[\fIstring ...\fB] [< \fIone-column-table\fB]\fR +.PP +.II "table^validate" +.II "validate table" +.II "domain table" +The command +.B domain +displays all strings or all rows in +.I one-column-table +that do not match any entry in the +.IR domaintable . +.PP +.B domain +is useful for validating that each item in a column +is legitimate. +First, build a file that consists of a list of all legal values. +In relational theory, these values are called the +.I domain +of the column. +(You can also use the command +.B validate +to validate numbers and short lists of valid strings.) +.PP +To check the domain of a column in a multicolumn table, simply use the +command +.B column +to project the column you want. +.PP +The +.I domaintable +must be sorted because it be searched by the command +.B search +using the binary fast-access method (\fB\-mb\fR). +.B domain +is a shell script, so you can edit it to change its search method, +.SH Example +You might have an order file for cars like this: +.DM +Qty Model Colors Options +------- ------- ------- ------- +1 sedan black 3 +1 sedan green 1 +1 sedan farble 4 +3 sedan red 5 +2 convert white 1 +1 sedan purple 2 +1 sedan yellow 4 +3 convert blue 2 +.DE +.PP +You would also need a sorted file of possible car colors: +.DM +Colors +------ +black +blue +carmel +green +purple +red +silver +white +.DE +.PP +Now we can validate the +.B Color +column and see all unacceptable colors: +.DM + column Colors < orders | domain colors +.DE +.PP +This produces the following output: +.DM +Colors +------ +farble +yellow +.DE +.PP +.B domain +complains about +.B farble +and +.B yellow +because neither color is in our domain list. +.PP +You can invoke +.B orders +in the text editor and edit it, +or add those colors to the approved list. +In +.B vi +use the +.B /pattern +command to find all of the +.I patterns +in the orders file. +Another approach is to type something like this: +.DM + :g/farble/s//purple/g +.DE +.PP +This +.B vi +command will find all instances of +.B farble +and change them to +.BR purple . +.PP +You can also validate strings written on the command line. +For example, the command +.DM + domain colors red blue purple farble yellow green +.DE +.PP +also prints the output: +.DM +farble +yellow +.DE +.PP +These command-line stings should be quoted if they have +any special characters in them. +They are sought by the command +.BR grep . +If they contain a dollar sign `$', +it should be protected with three backslashes: +this expression is scanned both by the shell and by +.BR grep , +and it takes three backslashes to get one +backslash through to +.BR grep . +.SH "See Also" +.B +column, +search, +validate, +ve +.R +.br +COHERENT Lexicon: +.B +egrep, +grep, +sort, +uniq +.R +.SO domain diff --git a/man/enter.man b/man/enter.man new file mode 100644 index 0000000..df0c903 --- /dev/null +++ b/man/enter.man @@ -0,0 +1,257 @@ +.TH enter "" "" "/rdb Command" +.PC "Add rows to a table or list file without an editor" +\fBenter [\-\fIlimit\fB] \fItableorlist\fR +.PP +.II "row^append" +.II "table^add row" +.II "table^append row" +.II "append row" +.II "add row" +The command +.B enter +is a simple way to append one or more rows to the end of +.IR tableorlist . +It displays each column name and waits for you to type each item. +.PP +.B enter +is a substitute for the form-editor +.BR ve . +It is easier to use, but very limited. +.PP +Note that this is one of the few +.B /rdb +commands that does not use the less-than symbol `<' +to input a table, +but requires the name of a table to which it is to append data. +.PP +If you make an error in entering a record, you can return to the last +line by typing a caret `^'; +.B enter +then prompts you with the previous line. +If you return two lines, keep typing carets +until you are prompted for the line you wish to reenter. +.SH Options +The option \fB\-\fIlimit\fR +lets you limit the number of rows to be entered. +For example, \fB\-1\fR prompts for one row and then exits. +Thus, it can be used in a shell script. +.SH Rules +To set up and enter a file in list format instead of table format, there +are several rules and suggestions. +.PP +First, let's discuss terminology. +A list is like a table that is turned sideways. +For example, a wide table looks like this: +.DM +Number Name Company Street City State ZIP +Phone +------ ---- ------- ------ ---- ----- --- +----- +1 Ronald McDonald McDonald's 123 Mac Attack +Memphis TENN 30000 (111) 222-3333 +.DE +.PP +Note that the row is so long that it wrapped around to the next line. +Also, the column names and column data do not line up. +.IR Names , +.IR Companies , +and +.I "Street Addresses" +are usually too long to fit nicely into such a table. +Therefore, we need a list format like this: +.DM +Number 1 +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.DE +.PP +These two formats (table and list) are made interchangeable by using two +programs: +.B listtotable +and +.BR tabletolist . +You can enter and keep a file in list format. +When you want to use several commands, +you can convert it to table format and pipe it into the regular +commands for faster execution. +For example, the command +.DM + listtotable < maillist | column Name Phone | justify +.DE +.PP +produces the following output: +.DM +Name Phone +--------------- -------------- +Ronald McDonald (111) 222-3333 +.DE +.PP +It is best, therefore, to remember that +.B Name +and +.B Phone +are column names. +The information that follows them in the list +is the first row of a table (that can be created by the command +.BR listtotable ). +.IP "\fB1.\fR" +First use an editor to create the list file. +Type each column name followed by one, and only one, tab. +Also enter the information for the first row (record). +.IP "\fB2.\fR" +Be sure to put a newline (blank line) +as the first line of the file. +.IP "\fB3.\fR" +Be sure to put a blank line at the end of each row (record). +In the previous example, it should appear after the row +.BR Phone . +This tells the program that it has reached the end of the row. +.sp \n(pDu +You can then save the file and use the +.B enter +program. +Type: +.DM +enter maillist +.sp +Name _ +.DE +.sp \n(pDu +.B enter +responds with the first column name and +a tab, and waits for you to enter whatever you wish for +.BR Name . +The underscore `_' shows where the cursor +of your terminal will be waiting for you to input data. +.IP "\fB4.\fR" +Type in your data for the item. +Use the return key to end the information for this item. +If you type more than 80 characters or so, your terminal will wrap around. +You can continue to type and wrap around until +you reach your editor's line limit or press \fB\fR. +Then +.B enter +gives you the name of the next column head, +a tab, and waits for your entry. +.IP "\fB5.\fR" +When +.B enter +has given you the first +column name of a new record and is waiting for a response, +exit from it by typing \fB\fR. +.B enter +then returns you to the COHERENT shell. +.sp \n(pDu +For list-formatted files, it is better to use the +.B enter +command than an editor. +If you do your entry with a text editor, +you must include all of the column names, +in the same order and spelled correctly. +.SH Example +To add a line to file +.BR maillist , +type the following: +.DM + enter maillist +.DE +.PP +The editing session may go as follows: +.DM +Number 2 +Name Chiquita Banana +Company Standard Brands +Street ^ +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 +.sp +Name \fB\fP +.DE +.PP +Note we made an error entering +.B Company +and did not realize it until we were at +.B Street. +We typed a caret `^' that reprompted us for +.BR Company , +which we then reentered. +.B enter +then went on to prompt us for +.B Company +again. +.PP +The entered file now appears as follows: +.DM +Number 1 +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.sp +Number 2 +Name Chiquita Banana +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 +.DE +.PP +Note that the last row (actually eight lines and the bottom +newline) is the one we entered. +Just press \fB\fR if you have no data to enter after a prompt. +.PP +Now for an example of \fBenter\fRing a table. +If you want to add a line to +.BR inventory , +type: +.DM +enter inventory +.sp +Item# Amount Cost Value Description +------- ------- ------- ------- --------------- +.DE +.PP +Then start typing your rows. +Remember to insert tabs between columns: +.DM +8 123 5.98 0 widget +9 29 15.50 0 another widget +\fB\fP +.DE +.PP +The inventory file now looks like this: +.DM +Item# Amount Cost Value Description +------- ------- ------- ------- --------------- +1 3 5.00 0 rubber gloves +2 100 0.50 0 test tubes +3 5 8.00 0 clamps +4 23 1.98 0 plates +5 99 2.45 0 cleaning cloth +6 89 14.75 0 bunsen burners +7 5 175 0 scales +8 123 5.98 0 widget +9 29 15.50 0 another widget +.DE +.PP +Note that the last two lines are the ones we entered. +.SH "See Also" +.B +listtotable, +tabletolist, +ve +.R diff --git a/man/explode.man b/man/explode.man new file mode 100644 index 0000000..30c37bf --- /dev/null +++ b/man/explode.man @@ -0,0 +1,76 @@ +.TH explode "" "" "/rdb Command" +.PC "Produce table of subparts and their count for a part" +\fBexplode \fIPart Amount \fB[ \fItable \fB]\fR +.PP +.II "table^explode part" +.II "explode part into subparts" +The command +.B explode +takes a part number or name and looks it up in a part table +to find all of its subparts. +It then finds the subparts of those subparts, and so forth, until +it can seek no farther. +.B explode +also multiplies the number of parts times the subpart +counts at each interaction, so the final subpart table +lists all of the subparts of the original part and the +total number of subparts needed to make the part. +.PP +You can use +.B explode +to generate a list of all items needed to make a product. +The cost of these subparts, times the count, +gives the value that can be totaled to arrive at the total +cost of the product. +.PP +.B explode +assumes that the +.BR Part , +.BR Subpart , +and +.B Count +columns +are the first three columns of the table, and that the third column +is actually named +.BR Count . +.SH Example +If you had a +.B parttable, +like this: +.DM +Part Subpart Count +---- ------- ------ +10001 10010 3 +10001 10020 4 +10010 10100 2 +10020 10100 5 +.DE +.PP +and you wanted to find the subpart list and count for +two part 10001s, you would type: +.DM + explode 10001 2 +.DE +.PP +The result would be: +.DM +Part Amount +---- ------ +10001 2 +10010 6 +10020 8 +10100 52 +.DE +.PP +Note that both 10010 and 10020 have 10100 as a subpart, but +.B explode +uses +.B subtotal +to combine the 10100 subparts into one row. +The table is also sorted. +.SH "See Also" +.B +bom, +subtotal +.R +.SO explode diff --git a/man/fd.man b/man/fd.man new file mode 100644 index 0000000..819ae61 --- /dev/null +++ b/man/fd.man @@ -0,0 +1,129 @@ +.TH fd "" "" "/rdb Command" +.PC "Test for functional dependency of columns" +\fBfd \fIdeterminecolumn dependcolumn \fB< \fItable\fR +.PP +.II "column^functional dependency" +.II "functional dependency^test" +.II "test functional dependency" +The command +.B fd +reads +.I table +and tests whether its +.I determinecolumn +determines +.IR dependcolumn . +A column in a table functionally depends upon another column +when there is only one value in the second column for each value in +the first. +This helps you to determine if a table is normalized. +(See the section on +.BR Normalization , +below, for details on what this means.) +.SH Example +First let's look at our inventory table: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +Note that the first column, +.B Item, +is the key column for the table. +It functionally determines all of the other columns. +Each value in column +.B Item +is unique, so each value determines, or is the key, +to its row and all of the column values on it. +.PP +However, column +.B Cost +does not functionally depend upon +.B Amount +because there are two `5' values in +.B Amount +and each has a different value for +.B Cost +associated with it (on the same row or line). +The common sense (technically called the ``semantics'') of the +table is that knowing the number of items we have in inventory +does not tell us how much they cost. +.PP +Now let's see if +.B fd +can find the dependencies. +The command +.DM + fd Item Cost < inventory +.DE +.PP +returns the message +.BR true . +This is correct, because +.B Item +is the key column. +However, the command +.DM + fd Amount Cost < inventory +.DE +.PP +returns the message +.BR false . +This, too, is correct. +.SH Normalization +A key column should aways functionally determine all +of the other columns in the table. +But nonkey columns should not depend upon each other. +.PP +If there are too few data in the table, a +pseudo-dependency may be discovered. +Be sure your table has lots of data before you test it. +.PP +To normalize your tables, you must check for functional +dependencies. +When functional dependency is found with non\-key columns, +the columns should be projected and uniqued to form a second table. +Then the functionally dependent column, but not the +determining column, should be removed from the first table. +For example: +.DM + column column1 column2 < table | uniq > newtable + column `sed 1q table | sed s/column2//` < table > tmp + mv tmp table +.DE +.PP +In the second table, the column that determines the other column +is the key for that table. +If you need to recreate the original first table, you can +join the two new tables by using the command +.BR jointable , +as follows: +.DM + jointable -j column1 table newtable > oldtable +.DE +.PP +Normalization, or simplification, is an important process. +Normalized files are smaller and are easier to maintain. +.SH "See Also" +.B +column, +jointable, +paste.rdb +.R +.br +COHERENT Lexicon: +.B +cut, +false, +mv, +paste, +sed, +true +.SO fd diff --git a/man/filesize.man b/man/filesize.man new file mode 100644 index 0000000..a83acc9 --- /dev/null +++ b/man/filesize.man @@ -0,0 +1,64 @@ +.TH filesize "" "" "/rdb Command" +.PC "Return the number of characters in a file" +\fBfilesize \fIfile ...\fR +.PP +.II "table^size" +.II "size of table" +.II file^size +The command +.B filesize +displays the number of bytes in each +.IR file . +.II wc +.II stat() +It is much faster than the COHERENT command +.B wc +because it uses the system call +.B stat() +to get the number from the i-node table, +rather than counting the bytes in the file. +.PP +If you ask +.B filesize +to size only one file, +it sends one number to the +standard output. +This number can be assigned to a shell variable. +With more than one +.IR file , +it produces a table of sizes. +.SH Example +The following command returns the size of file +.BR filesize.1 , +which holds the ``raw'' form of what you are reading: +.DM + filesize filesize.1 +.DE +.PP +This displays one value on the standard output, e.g.: +.DM + 532 +.DE +.PP +The following command asks for the size of files +.B ascii.1 +and +.BR filesize.1 : +.DM + filesize ascii.1 filesize.1 +.DE +.PP +This form of the command returns a tabular output, e.g.: +.DM + Offset Filename +-------- -------- + 464 ascii.1 + 532 filesize.1 +.DE +.SH "See Also" +COHERENT Lexicon: +.B +ls, +stat(), +wc +.R diff --git a/man/fillform.man b/man/fillform.man new file mode 100644 index 0000000..582ae5f --- /dev/null +++ b/man/fillform.man @@ -0,0 +1,62 @@ +.TH fillform "" "" "/act/gl Command" +.PC "Fill a tax from with adjusted trial balance data" +\fBfillform \fIform\fR +.PP +.II "tax form^fill" +.II "fill tax form" +The command +.B fillform +is part of the accounting/general-ledger system included with +.BR /rdb . +It fills in the tax form +.IR form , +which must be one of +.B 1120 +(corporate), +.B 1120S +(S corporation), +.B C +(schedule C), or partnership tax form. +It writes its output into a file with the name +.IR form . +.SH Example +The following example fills in the S corporation form: +.DM + fillform 1120S +.DE +.PP +When you post-process the output with the command +.DM + sed 10q 1120S +.DE +.PP +you see output that resembles the following: +.DM +Taxline Amount Description +------- ------ -------------------------------------- +101a 13500 Sales +101b 0 Returns and Allowances +101c 13500 Net Sales +102 0 Cost of Goods Sold +102-A1 20000 Beginning Inventory +102-A2 3601 Misc Merchandise +102-A3 2100 Salaries and Wages +102-A4 0 Other Costs +.DE +.SH "See Also" +.B +calculate +.R +.br +COHERENT Lexicon: +.B +sed +.R +.SO fillform +.SH Notes +Please note the tax tables and forms included with +.B /rdb +are from 1987. +These are examples, and no claim is made for their accuracy in computing +this year's taxes. +.IR "Caveat utilitor" . diff --git a/man/fixtotable.man b/man/fixtotable.man new file mode 100644 index 0000000..5cacbbe --- /dev/null +++ b/man/fixtotable.man @@ -0,0 +1,68 @@ +.TH fixtotable "" "" "/rdb Command" +.PC "Converts fixed length format to /rdb table format" +\fBfixtotable \fIcolumn\fB\|[=\|\fIn1\fB,\fIn2\fB] ... < \fIfixtdb\fR +.PP +.II "table^convert format" +.II "table^fixed length" +.II "convert table format" +The command +.B fixtotable +converts a data base from fixed-length format to +variable length \fB/rdb\fR format. +It does so +by selecting \fIn2\fR bytes of data from table \fIfixtdb\fR, +beginning at the \fIn1\fR position for each \fIcolumn\fR specified. +It inserts tabs between columns and calls the +.B /rdb +command +.B compress +to remove leading and trailing blanks. +It generates +header records from the column names. +Column names that do not specify start position (\fIn1\fR) and +field width (\fIn2\fR) are considered empty columns, and appear +as such in the output. +Blank records are removed. +.SH Example +For example, let's look at a file named \fBjournal\fR: +.DM +1 10 20 30 +----+----|----+----|----+----| +820102101 25000 +820102211.1 25000 +820103150.1 10000 +820103101 5000 +820103211.2 5000 +820104130 30000 +.DE +.PP +The first six bytes are the date field; +the next six bytes, starting at position 7 are the account number field; +the next seven bytes, starting at position 13 are the credit field; +and the last seven bytes, starting at position 20 are the debit field. +.PP +The following command yanks +the account number and the credit and debit fields from +.BR journal , +makes a new table with a new field for the description: +.DM +fixtotable Account=7,6 Debit=13,7 Credit=20,7 Description < journal +.DE +.PP +This produces the following table: +.DM +Account Debit Credit Description +------- ----- ------ ----------- +101 25000 +211.1 25000 +150.1 10000 +101 5000 +211.2 5000 +130 30000 +.DE +.SH "See Also" +.B +compress, +tabletofix +.R +.SO fixtotable diff --git a/man/foot.man b/man/foot.man new file mode 100644 index 0000000..b6381cf --- /dev/null +++ b/man/foot.man @@ -0,0 +1,63 @@ +.TH foot "" "" "/act/gl Command" +.PC "Foot or subtotal Debits and Credit of ledger" +.B foot +.PP +.II "account^subtotal" +.II "table^subtotal" +.II "subtotal table" +The command +.B foot +is part of the accounting/general-ledger system included with +.BR /rdb . +It subtotals the accounts in table +.BR ledger . +.SH Example +The command +.B foot +produces a table that resembles the following: +.DM +Account Date Debit Credit Ref Short Description +------- ------ ----- ------ ------- ------- ------------------ +1010 851231 1758 cash +1010 851231 5137 cash +------- ------ ----- ------ ------- ------- ------------------ +1010 5137 1758 +.DE +.DM +3010 1601 deposit sales +------- ------ ----- ------ ------- ------- ------------------ +3010 0 1601 +.DE +.DM +3080 850125 250 c1459 royalty book fees +------- ------ ----- ------ ------- ------- ------------------ +3080 0 250 +.DE +.DM +3100 850118 3286 c101 income sales +------- ------ ----- ------ ------- ------- ------------------ +3100 0 3286 +.DE +.DM +4020 1601 check merch +------- ------ ----- ------ ------- ------- ------------------ +4020 1601 0 +.DE +.DM +4370 850101 2 r parking parking at clients +------- ------ ----- ------ ------- ------- ------------------ +4370 2 0 +.DE +.DM +4380 850103 15 c115 travel shuttle bus +4380 850104 13 v travel meal +4380 850105 13 c116 travel meal +4380 850106 114 r travel hotel +------- ------ ----- ------ ------- ------- ------------------ +4380 155 0 +.DE +.SH "See Also" +.B +act +.R +.SO foot diff --git a/man/getjournal.man b/man/getjournal.man new file mode 100644 index 0000000..8cb1a5d --- /dev/null +++ b/man/getjournal.man @@ -0,0 +1,35 @@ +.TH getjournal "" "" "/act/gl Command" +.PC "Copy journals for sales, purchasing, pay systems" +\fBgetjournal \fIjournalname\fR +.PP +.II "journal^manipulate" +.II "manipulate journals" +The command +.B getjournal +is part of the accounting/general-ledger system included with +.BR /rdb . +It copies, totals, and adjusts journals from other parts of the +business system. +These subsidiary journals are created automatically by the +operations of sales, purchasing, and other departments. +It also gets the inventory value for the table +.BR journaladjust . +.SH Example +The command +.DM + getjournal journaldeposit +.DE +.PP +reads the table +.B journaldeposit +and prints on the standard output a table that resembles the following: +.DM +Date Amount Account Ref Description +------- ------- ------- ------- -------------------------- +891231 13500.00 sales deposit from sales/deposit +.DE +.SH "See Also" +.B +act, +consolidate +.R diff --git a/man/gregorian.man b/man/gregorian.man new file mode 100644 index 0000000..35e852b --- /dev/null +++ b/man/gregorian.man @@ -0,0 +1,171 @@ +.TH gregorian "" "" "/rdb Command" +.PC "Convert column of dates for arithmetic and format change" +\fBgregorian [ \-ceu ] [ \fIColumn ... \fB] < \fItableorlist\fR +.PP +.II "date^Gregorian" +.II "Gregorian date" +.II "date^convert" +The command +.B gregorian +converts Julian dates in one or more +.BR Column s +to Gregorian date. +When used with the command +.BR julian , +you can do the following: +.IP \fB1.\fR 0.3i +Add days to a date. +.IP \fB2.\fR +Subtract days from a date. +.IP \fB3.\fR +Subtract two dates to find the number of days between them. +.IP \fB4.\fR +Change the format of a date. +.PP +A Gregorian date is a string that presents the date of the +current year, month, and day; for example ``911231''. +A Julian date, however, is the number of days since the +beginning of the Julian calendar (January 1, 4713 BC). +This system is used by astronomers, computer programmers, and other +people who wish to store dates in a form that can be +easily translated from one system of dating to another, without having +to worry about leap years, dates of the births of religious figures, etc. +Because Julian dates are integers, +you can subtract one from another to find the number of days between +two dates. +.PP +To perform arithmetic on dates, first use the command +.B julian +to convert the dates from Gregorian form to Julian. +Then, use the command +.B compute +to add or subtract the dates (or add a constant to a date). +Finally, use +.B gregorian +to convert the sum or the difference back to Gregorian form. +.SH Options +.B gregorian +recognizes the following options: +.IP \fB\-c\fR 0.3i +Output +\fIcomputer\fR +date format, i.e., \fIYYMMDD\fR. +.B /rdb +uses this format by default for its Gregorian dates +because it can be sorted and selected in correct numerical sequence. +.IP \fB\-e\fR +Output +.I European +date format, i.e., \fIDD/MM/YY\fR. +.IP \fB\-u\fR +Output +.I U.S. +date format, i.e., \fIMM/DD/YY\fR. +.SH Example +The first example shows how to add dates. +Suppose we have a file called +.BR journal , +as follows: +.DM +Date Account Debit Credit Description +------ ------- ----- ------ ---------------------------------- +861222 101 25000 cash from loan +861222 211.1 25000 loan number #378-14 Bank Amerigold +861223 150.1 10000 test equipment from Zarkoff +861223 101 5000 cash payment +861223 211.2 5000 note payable to Zarkoff Equipment +861224 130 30000 inventory - parts from CCPSC +861224 201.1 15000 accounts payable to CCPSC +861224 101 15000 cash payment to CCPSC for parts +.DE +.PP +Now, suppose that we want to add 45 days to each of the dates. +First, see what +.B julian +does. +Typing +.DM + julian Date < journal +.DE +.PP +prints the following on the standard output: +.DM +Date Account Debit Credit Description +------ ------- ----- ------ ---------------------------------- +1752827 101 25000 cash from loan +1752827 211.1 25000 loan number #378-14 Bank Amerigold +1752828 150.1 10000 test equipment from Zarkoff +1752828 101 5000 cash payment +1752828 211.2 5000 note payable to Zarkoff Equipment +1752829 130 30000 inventory - parts from CCPSC +1752829 201.1 15000 accounts payable to CCPSC +1752829 101 15000 cash payment to CCPSC for parts +.DE +.PP +The +.B Date +column now holds a Julian date. +Note we gave no option because computer format is the default. +.PP +The next command adds 45 days to each entry in the +.B Date +column: +.DM +julian Date < journal | compute 'Date += 45' | gregorian Date +.DE +.PP +This command prints the following on the standard output: +.DM +Date Account Debit Credit Description +------ ------- ----- ------ ---------------------------------- +870205 101 25000 cash from loan +870205 211.1 25000 loan number #378-14 Bank Amerigold +870206 150.1 10000 test equipment from Zarkoff +870206 101 5000 cash payment +870206 211.2 5000 note payable to Zarkoff Equipment +870207 130 30000 inventory - parts from CCPSC +870207 201.1 15000 accounts payable to CCPSC +870207 101 15000 cash payment to CCPSC for parts +.DE +.PP +As you can see, every date is now 45 days later than they originally were. +.PP +The next example converts one format of Gregorian date to another. +To convert the +.B Date +column in our example table from computer format to U.S. format, +type the following: +.DM + julian Date < journal | gregorian -u Date | justify Date +.DE +.PP +The result is as follows: +.DM +Date Account Debit Credit Description +-------- ------- ----- ------ ---------------------------------- +12/22/86 101 25000 cash from loan +12/22/86 211.1 25000 loan number #378-14 Bank Amerigold +12/23/86 150.1 10000 test equipment from Zarkoff +12/23/86 101 5000 cash payment +12/23/86 211.2 5000 note payable`to Zarkoff Equipment +12/24/86 130 30000 inventory - parts from CCPSC +12/24/86 201.1 15000 accounts payable to CCPSC +12/24/86 101 15000 cash payment to CCPSC for parts +.DE +.PP +Because the U.S. format is two characters wider than the +computer format, we included the command +.B justify +in our command line to line things up. +.SH "See Also" +.B +compute, +computedate, +julian +.R +.br +COHERENT Lexicon: +.B +awk, +date +.R diff --git a/man/hashkey.man b/man/hashkey.man new file mode 100644 index 0000000..2fbff52 --- /dev/null +++ b/man/hashkey.man @@ -0,0 +1,44 @@ +.TH hashkey "" "" "/rdb Command" +.PC "Return the hash offset for key strings" +\fBhashkey \fIrows keystring ...\fR +.PP +.II "indexing^hash key" +.II "hash-key indexing" +The command +.B hashkey +adds the ASCII value of each character in the +.IR keystring s +(except spaces or tabs), and returns the modulo of +.IR rows . +This is for computing the hash offset into hash-index table for +the hash fast-access method. +It is used by the command +.B append +to update the hash-index table. +.SH Example +Here we convert several characters. +The command +.DM + hashkey 11 ABCD +.DE +.PP +returns \fB2\fR. +(That is, ASCII values 65 plus 66 plus 67 plus 68 yields 266; which, when +divided by 11 yields a remainder of 2). +The command +.DM + hashkey 29 abcdefg +.DE +.PP +returns \fB4\fR. +.SH "See Also" +.B +append +ascii, +chr +.R +.br +COHERENT Lexicon: +.B +ASCII +.R diff --git a/man/headline.man b/man/headline.man new file mode 100644 index 0000000..4e16f85 --- /dev/null +++ b/man/headline.man @@ -0,0 +1,37 @@ +.TH "head line" "" "" "/rdb Definition" +.PC +.II "/rdb header" +.PP +The +.I "head line" +is the first line in an +.B /rdb +table. +It names each column in the table. +Each entry in the head line must be separated by a tab character. +.SH Example +The table +.B inventory +appears as follows: +.DM +Item Amount Cost Value Description New +---- ------ ---- ----- -------------- ------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +The first line +.DM +Item Amount Cost Value Description New +.DE +.PP +is this table's head line. +.SH "See Also" +.B +dash line, listtotable, tabletolist +.R diff --git a/man/headoff.man b/man/headoff.man new file mode 100644 index 0000000..d6dfe92 --- /dev/null +++ b/man/headoff.man @@ -0,0 +1,58 @@ +.TH headoff "" "" "/rdb Command" +.PC "Remove an /rdb head from both table and list files" +\fBheadoff < \fItableorlist\fR +.PP +.II table^behead +.II "behead table" +.II "remove header" +.II "head line^remove" +.II "dash line^remove" +The command +.B headoff +``beheads'' +.IR tableorlist ; +that is, it strips off its header. +Tables have two-line column heads; list files have only +one line that consists of a single newline character. +The COHERENT command +.B "tail +3" +does the same thing for a table; and +.B "tail +2" +for a list. +.PP +The command +.B union +uses +.B headoff +to assemble files. +Only the first file keeps its head. +.SH Example +To behead the table +.BR inventory , +type: +.DM + headoff < inventory +.DE +.PP +This writes something like the following on the standard output: +.DM + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.SH "See Also" +.B +headon, +insertdash, +union +.R +.br +COHERENT Lexicon: +.B +head, +tail +.R diff --git a/man/headon.man b/man/headon.man new file mode 100644 index 0000000..1f94a72 --- /dev/null +++ b/man/headon.man @@ -0,0 +1,69 @@ +.TH headon "" "" "/rdb Command" +.PC "Add an /rdb header to a table" +\fBheadon < \fItable\fR +.PP +.II "table^add head" +.II "add head to table" +The command +.B headon +appends an +.B /rdb +header onto +.IR table . +Tables have a two-line column head. +When you have a table without an +.B /rdb +header, perhaps created by another program, you can add a head to it. +You can also do this in a pipe and send the new table to other +.B /rdb +commands for processing. +.SH Example +If you want to treat the file +.B /etc/passwd +as a table for +.B /rdb +command processing, use +.BR headon , +as follows: +.DM + tr ':' ' ' < /etc/passwd | headon | justify +.DE +.PP +Note the use of the COHERENT command +.B tr +to translate a colon `:' into a tab character; +and the use of the +.B /rdb +command +.B justify +to align the columns. +This produces something like the following: +.DM +1 2 3 4 5 6 +------ ------------- --- --- --------------- ------------- +root dVAYxSsJy10Fg 0 3 Super User / +daemon 1 12 background / +bin rVOhF49RpZXEY 2 2 binary programs /bin +uucp 5 1 UUCP /usr/lib/uucp +lp 71 2 Line Printer /usr/spool/lp +guest 100 100 Guest User /usr/guest +rod 0 0 Rod Manis /usr/rod +.DE +.PP +Be sure the first line of the table has no blank fields or +.B headon +counts the wrong number of fields. +.SH "See Also" +.B +headoff, +insertdash, +justify, +rmblank +.R +.br +COHERENT Lexicon: +.B +passwd, +tr +.R +.SO headon diff --git a/man/helpme.man b/man/helpme.man new file mode 100644 index 0000000..18746b2 --- /dev/null +++ b/man/helpme.man @@ -0,0 +1,23 @@ +.TH helpme "" "" "/rdb Command" +.PC "List the help commands available" +.B helpme +.PP +The command +.B helpme +lists all other +.B /rdb +help commands. +.SH Example +Typing +.B helpme +prints the following on the standard output: +.DM +Command Description +------------------ ------------------------------------------ +"menu" a menu with some commands +"rdb" a list of the available commands +"commands" description and syntax of all the commands +"whatis command" description and syntax of command +"whatwill feature" info on commands with that feature +"man command" the manual page for that command +.DE diff --git a/man/howmany.man b/man/howmany.man new file mode 100644 index 0000000..02331d8 --- /dev/null +++ b/man/howmany.man @@ -0,0 +1,17 @@ +.TH howmany "" "" "/rdb Command" +.PC "Display the number of commands in a directory" +.B howmany +.PP +.II "command^list" +.II "list commands" +The command +.B howmany +displays the number of the commands it finds in directory +.BR $RDB/bin . +.SH "See Also" +.B +rdb, +whatis, +whatwill +.R +.SO howmany diff --git a/man/index.man b/man/index.man new file mode 100644 index 0000000..ef8c38c --- /dev/null +++ b/man/index.man @@ -0,0 +1,202 @@ +.TH index "" "" "/rdb Command" +.PC "Set up table for search" +\fBindex [\-m[bhirs]] [\-x] [-hsl [2> \fIlocation\fB]] \fItableorlist \fB[\fIkeycol ...\fB] [ < \fIkeytable\fB ]\fR +.PP +.II "table^index" +The command +.B index +sets up a table for the program +.BR search . +The indexing it performs increases the speed with which +.B search +can find a row in a table or list file. +This is very important for large files. +.PP +Exactly what +.B index +does depends upon the method of indexing you choose. +For some methods, it builds another table, called a +.IR "secondary index file" , +that the specified method requires. +It is possible to index +one file by all five methods. +Some methods can index all of a table's columns. +.PP +Please note that these fast-access methods are static. +If the table or list +is changed \(em by a text editor, for example \(em it may have to +be reindexed. +Applications should manipulate the table +or list files and their secondary index to update them together. +Forms packages should dynamically maintain these files. +.PP +There is a trick to allow you to index any or all of the columns in a file. +.II ln +Simply use the COHERENT command +.B ln +to link the file to as many different names as you have columns to index. +Then index the table through each of its link aliases. +For details, see the COHERENT Lexicon's entry for +.BR ln . +.SH Options +The following options control the method of indexing that +.B index +produces: +.IP \fB\-m\fR +Index using the default method, which is sequential. +See +.BR \-ms , +below. +.IP \fB\-mb\fR +.II "index^binary" +Binary indexing. +.B index +simply sorts the file on the specified key column. +No secondary file is created. +Because a file can only be sorted on one column, +only one column can be indexed using this method. +The command +.B sorttable +gives the same result as this option. +.IP \fB\-mh\fR +.II "index^hash table" +Hash indexing. +.B index +builds a secondary-index file, named +\fBtableorlist\fB.h\fR, +hashes all of the key-column's values, +and writes them and their offsets to the secondary-index file. +Please note that because the COHERENT system places a limit of 14 +characters on a file name, the name of +.I tableorlist +must not exceed 12 characters, or +.B index +will not be able to name correctly the secondary-index file. +.IP \fB\-mi\fR +.II "index^inverted" +Inverted or indexed indexing. +.B index +builds a secondary-index file of keys and offsets, then sorts on the keys. +The secondary file is named +\fItableorlist\fB.i\fR. +The trick to index more than one column, previously discussed, +alsos work for the inverted method. +.sp \n(pDu +If you plan on using the incomplete-match feature of +search (which works only with the inverted method), you +must specify the \fB-x\fR flag, so the secondary index is sorted properly. +.IP \fB\-mr\fR +.II "index^record number" +Record-number indexing. +.B index +builds a secondary file named +\fItableorlist\fB.r\fR, +which is one fixed-length column of row offsets. +.IP \fB\-ms\fR +.II "index^sequential" +.II "index^linear" +Sequential or linear indexing. +.B index +does nothing in this case, because +.B search +simply looks through the whole file. +.PP +The following options control details of +.BR index 's +output: +.IP \fB\-h\fR +No head line on the output table. +.IP \fB\-l\fR +Write to the standard error the starting and stopping location of each row, +and the offset entry in the secondary-index file. +.II replace +.II lock +.II unlock +These numbers are used by the commands +.BR replace , +.BR lock , +and +.BR unlock . +.II seek +The command +.B seek +uses this option to find the locations of rows. +The locations are sent to standard-error device, which under the +Bourne and Korn shells is file descriptor 2; +thus, you can redirect them into a file by using the shell operator \fB2>\fR. +You can store these data until they are needed by your shell programs. +.sp \n(pDu +This option produces four integers. +If there is no secondary-index file, the start location is zero. +.IP \fB\-s\fR +Only one row is found. +This speeds up some of the methods +because they do not have to continue searching +after they find the first match. +The sequential method will, on average, run twice as fast; +whereas the hash, binary, and inverted methods will run slightly faster. +The record-access method will not change, as it only finds one match anyway. +.SH "Methods and Suffixes" +The following gives suffixes that +.B index +appends to names of tables, in order to name its secondary-index files. +.DS +.ta 1.25iC 2.5iC +\fIMethod Suffix Example\fR +\fB\-b\fR None ... +\fB\-h\fR .h inventory.h +\fB\-i\fR .i inventory.i +\fB\-r\fR .r inventory.r +\fB\-s\fR None ... +.DE +.SH Example +Let's look at the first ten lines of an accounts chart, named +.BR chart : +.DM +Account Name +------- ---------------------- +100 Assets +101 Cash +111 Accounts Receivable +111.1 Allowance for Bad Debt +115 Notes Receivable +120 Deposits +130 Parts Inventory +150 Equipment +.DE +.PP +Access to the data in +.B chart +can be sped up by indexing it and using the command +.B search +to find the row you want. +To index +.B chart +using the binary (sorted) search method, type: +.DM + index -mb chart Account +.DE +.PP +.B index +quietly returns when it finishes. +You are now ready to use +.B search +to find an +.B Account +number in the +.B chart . +.SH "See Also" +.B +append, +delete, +lock, +replace, +search, +unlock, +update +.R +.br +COHERENT Lexicon: +.B +ln +.R diff --git a/man/insertdash.man b/man/insertdash.man new file mode 100644 index 0000000..ce024fe --- /dev/null +++ b/man/insertdash.man @@ -0,0 +1,51 @@ +.TH insertdash "" "" "/rdb Command" +.PC "Insert dash line as second line in table" +\fBinsertdash < \fItable-without-dashline\fR +.PP +.II "table^insert dash line" +.II "dash line^insert" +The command +.B insertdash +writes a line of dashes (hyphens) as the second line of the table. +It makes the dash line +by copying the head line and turning every character in the +first into a hyphen except the tabs. +.PP +This command is useful for files created by the system or non-COHERENT +programs that do not have the dash line. +However, you will also need to convert field separators into tabs. +.II sed +.II tr +You can do this with the COHERENT commands +.B tr +or +.BR sed . +.SH Example +.B insertdash +assumes a table that resembles the following: +.DM +Item Amount Cost Value Description + 1 3 50 150 rubber gloves +.DE +.PP +After running +.B insertdash +on it, this table looks like this: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves +.DE +.SH "See Also" +.B +dashline, +headoff, +headon +.R +.br +COHERENT Lexicon: +.B +sed, +tr +.R +.SO insertdash diff --git a/man/intersect.man b/man/intersect.man new file mode 100644 index 0000000..4032abd --- /dev/null +++ b/man/intersect.man @@ -0,0 +1,79 @@ +.TH intersect "" "" "/rdb Command" +.PC "Write table of rows that are in both input tables" +\fBintersect \fItable1 table2\fR +.PP +.II " +.II sort +.II uniq +.II "table^intersect" +.II "table^logical AND" +.II "intersection of tables" +The command +.B intersect +is a shell script that uses the COHERENT commands +.B sort +and +.B uniq +to perform a logical AND on two tables. +It produces a new table that consists only of the rows that +are in both of the input tables. +The two input tables must have the same number of columns +(called \fIunion compatible\fR), and +rows must match exactly to be considered the same. +.SH Example +Consider the table +.BR journal , +which appears as follows: +.DM +Date Amount Account Ref Description +------- ------- ------- ------- -------------------------------- +820107 14.00 meal v meal with jones +820114 81.80 insur c car insurance allstate +820119 81.72 vitamin c sundown vitamins +820121 20.83 meal v meal with scott +820121 2500.00 keogh c keogh payment +820125 99.00 dues v dues to uni-ops unix conference +.DE +.PP +And consider the table +.BR carexpense , +which appears as follows: +.DM +Date Amount Account Ref Description +------- ------- ------- ------- -------------------------------- +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate +820114 93.00 car c car registration dmv +.DE +.PP +To see if any rows in +.B journal +are also in +.BR carexpense , +type the command: +.DM + intersect journal carexpense +.DE +.PP +In this example, you would see the following on the standard output: +.DM +Date Amount Account Ref Description +------- ------- ------- ------- -------------------------------- +820114 81.80 insur c car insurance allstate +.DE +.PP +As you can see, the insurance expense was already posted to the +general journal. +.SH "See Also" +.B +difference, +jointable, +union +.R +.br +COHERENT Lexicon: +.B +sort, +uniq +.R +.SO intersect diff --git a/man/invoice.man b/man/invoice.man new file mode 100644 index 0000000..d2d2f27 --- /dev/null +++ b/man/invoice.man @@ -0,0 +1,54 @@ +.TH invoice "" "" "/act/sales Command" +.PC "Print invoice for a sale order" +.B invoice +.PP +.II "print invoice" +The command +.B invoice +is part of the accounting/sales system that is included with +.BR /rdb . +It prompts for the number of a sales order and then prints an invoice +for that order. +.SH Example +The following walks you through an example session with +.BR invoice : +.DM +Please enter order number (or Return to exit): \fB1\fP + Invoice + ------- +Makeapile, Inc., 123 Bigbucks Blvd., Dallas, TX 12345, 1-800-SOF-WARE +.DE +.DM +Order Number 1 +Date Ordered 850723 +Date Shipped 0 +.DE +.DM +Mailing Address for Customer Number 2 +Customer Number 2 +Mr. Thomas Boomer President +.DE +.DM +Ye Olde Thermonuclear Bombe Shoppe +54321 Blooy Road +Livermore, California USA +.DE +.DM +Order Number Code Backord Qty Price Total Name +------- ------- ------- ------- ------- ------- ------- ---- +1 1 rdb 10 1 1500 1500.00 /rdb +.DE +.DM + Gross 1500.00 + Tax 75.00 + Total 1575.00 +.DE +.DM +Please enter order number (or Return to exit): +.DE +.SH "See Also" +.B +act, +cstate +.R +.SO invoice diff --git a/man/jointable.man b/man/jointable.man new file mode 100644 index 0000000..cf97142 --- /dev/null +++ b/man/jointable.man @@ -0,0 +1,405 @@ +.TH jointable "" "" "/rdb Command" +.PC "Join two tables into one where keys match" +\fBjointable [\-a[1|2]] [\-j[1|2] \fIcolumn\fB]... \-n \-] \fItable1 \fB[\fItable2\fB]\fR +.PP +.II "table^join two t." +.II "join tables" +The command +.B jointable +joins +.I table1 +to +.I table2 +on +.IR column , +which is common to both tables (called the \fIkey column\fR). +If no +.I column +is named, +.B jointable +assumes that the first column in each table is the key column. +.PP +Each table must be sorted on the key column. +.B jointable +writes a new table that has one row for the rows in the tables +whose values in the key column match. +The columns of the new table are all the columns in +.I table1 +followed by all the columns in +.IR table2 \(em +with the sole exception that the key column is not repeated. +.PP +This is called a +.IR "natural join" . +An +.I equi-join +repeats the key column. +.PP +The COHERENT system's command +.B join +will also join two tables in the manner described above, but +it does not know about +.B /rdb +head lines. +.SH "String vs. Numeric Sort" +The commonest problem seen with +.B jointable +is when users attempt to join tables that have been sorted +.I numerically +on the key columns (that is, the columns +that the tables are going to be joined on). +They use +.B sorttable +with the +.B \-n +option (for numeric) to sort a table, then call +.B jointable. +.BR jointable , +however, expects to see a +.I "string sort" +fails to join the tables correctly. +.PP +The following compares a string sort with a numeric sort: +.DS +.ta 0.5iC 2.0iC + \fIString Numeric\fR + 1 1 + 10 2 + 2 10 + 20 20 +.DE +.PP +Can you see why? +Numeric is the way we count and is obviously correct. +But string sort looks at the first character, then the second. +A space sorts ahead of `0', so `1' comes before ``10''. +More importantly, `1' comes before `2' +regardless of the characters to the right. +That is why ``10'' comes before ``2'' followed by a space. +This problem shows up as empty output from a +.BR jointable , +when you know some of the rows should be joined. +.PP +Right-justification makes numeric and string sorts the same. +The leading spaces help the string sort to sort numbers correctly. +.PP +To make it easy for users to avoid this problem, +.B jointable +has the option +.BR \-n , +for symmetry with the COHERENT command +.BR sort . +.PP +If you want to join two tables through a numeric key column, +you must either call the +.B sorttable +without its +.B \-n +numeric sort option, so that +it defaults to string sort; +or if you use the +.B \-n +option for +.BR sorttable , +be sure to use it also with +.BR jointable . +.SH Options +.B jointable +recognizes the following options: +.IP \fB\-a\fIn\fR +All or one of the files (1 or 2) is output, +whether it matches or not. +In addition to the normal output, +.B jointable +produces a line for each unpairable line in file +.IR n , +where +.I n +is 1 or 2. +For example, the command +.DM + jointable -a1 ledger chart +.DE +.sp \n(pDu +forces +.B jointable +to print every row in table +.BR ledger , +regardless of whether any value in +.BR ledger 's +.B Account +column matches any value in +.BR chart 's +.B Account +column. +.IP "\fB\-j\fIn column\fR +Join on the +.I column +column in table number +.I n. +If +.I n +is missing, use the +.I column +column in each table. +For example, the command +.DM + jointable -j1 Account -j2 account ledger chart +.DE +.sp \n(pDu +joins tables +.B ledger +and +.B chart +using the +.B Account +column in +.B ledger +and the +.B account +column in +.BR chart . +If no columns are specified with this option, +.B jointable +joins the tables by the first column in each. +Thus, this option lets you join on any column defined +in either file, even when they have different names and locations. +.IP \fB-n\fR +Numeric join. +This means that table1 and table2 were sorted with the numeric option +.BR \-n . +Remember that if you use +.B \-n +with the command +.BR sorttable , +you must also use +it with +.BR jointable . +.IP \fB\-\fR +Read the standard input in place of a table. +For example, the command +.DM + sorttable < journal | jointable - chart +.DE +.sp \n(pDu +means to sort table +.BR journal , +then join it with table +.B chart +so that +.B journal +is on the left of the output table and +.B chart +is on the right. +.sp \n(pDu +Note that this option is used only in place of +.IR table1 . +If +.I table2 +is absent from the command line, +.B jointable +automatically reads the standard input for that table's data. +.PP +Note that most of these options are about +the same as for the COHERENT command +.BR join . +.SH Example +Consider table +.BR chart , +as follows: +.DM +Account Name +------- ---- +100 Assets +101 Cash +111 Accounts Receivable +111.1 Allowance for Bad Debt +115 Notes Receivable +120 Deposits +130 Parts Inventory + ... +.DE +.PP +and table +.BR ledger , +as follows: +.DM +Account Date Debit Credit +------- ---- ----- ------ +101 820102 25000 +101 820103 5000 +101 820104 15000 +130 820104 30000 +150.1 820103 10000 +201.1 820104 15000 +211.1 820102 25000 +211.2 820103 5000 +.DE +.PP +Note that each is sorted in string fashion on its first column. +To join them, use the command: +.DM + jointable ledger chart +.DE +.PP +This joins the tables on the first column in each, and writes the +following to the standard output: +.DM +Account Date Debit Credit Name +------- ---- ----- ------ ---- +101 820102 25000 Cash +101 820103 5000 Cash +101 820104 15000 Cash +130 820104 30000 Parts Inventory +150.1 820103 10000 Test equipment +211.1 820102 25000 Notes Payable - BA +211.2 820103 5000 Notes Payable - Z Equip +.DE +.PP +As you can see, column +.B Name +has been added to the columns of the +.B ledger +table, and that column +.B Account +is not repeated. +Also note that +.B jointable +wrote only the rows whose values in column +.B Account +match. +If a table has several rows with repeated key values (e.g., the +three +.BR 101 s +in the +.B Account +column), the joined table's rows are also repeated (see the three +.BR Cash s +in column +.BR Name ). +.PP +If you have a table named +.B journal +that is sorted on column +.BR Account , +as follows +.DM +Date Account Debit Credit +---- ------- ----- ------ +820104 101 15000 +820103 101 5000 +820102 101 25000 +820104 130 30000 +820103 150.1 10000 +820104 201.1 15000 +820102 211.1 25000 +820103 211.2 5000 +.DE +.PP +you could join it with table +.B chart +by typing this command: +.DM + jointable -j Account journal chart +.DE +.PP +The following gives +another way for the same result (because +.BR chart 's +join column defaults to column 1): +.DM + jointable -j1 Account journal chart +.DE +.PP +Both commands produce the following output: +.DM +Date Account Debit Credit Name +------ ------- ----- ------ ---- +820104 101 15000 Cash +820103 101 5000 Cash +820102 101 25000 Cash +820104 130 30000 Parts Inventory +820103 150.1 10000 Test equipment +820102 211.1 25000 Notes Payable - Bank of Amerigold +820103 211.2 5000 Notes Payable - Z Equipment +.DE +.PP +The +.B \-a +option lets you see all of one table whether or not there is a match. +For example, the command +.DM + jointable -a1 ledger chart +.DE +.PP +writes the following: +.DM +Account Date Debit Credit Name +------- ---- ----- ------ ---- +101 820102 15000 Cash +101 820103 5000 Cash +101 820104 25000 Cash +130 820104 30000 Parts Inventory +150.1 820103 10000 Test equipment +201.1 820104 15000 +211.1 820102 25000 Notes Payable - Bank of Amerigold +211.2 820103 5000 Notes Payable - Z Equipment +.DE +.PP +Note that column +.BR Account 's +value +.B 201.1 +in the ledger shows up here, even though it is not in +.BR chart . +This shows us that +.B 201.1 +is an error. +All account numbers should be in the chart of accounts. +We should either add +.B 201.1 +to the chart of accounts, or find +the correct number in the chart of accounts and replace +.B 201.1 +in the +.B journal +with the correct account number. +.PP +If you pipe a file into a +.BR jointable , +you can control which table it will be (first or second) by using the +dash option. +For example: +.DM + sorttable Account < journal | jointable -j Account - chart +.DE +.PP +This produces the following output: +.DM +Date Account Debit Credit Name +---- ------- ----- ------ ---- +820103 101 5000 Cash +820104 101 15000 Cash +820102 101 25000 Cash +820104 130 30000 Parts Inventory +820103 150.1 10000 Test equipment +820102 211.1 25000 Notes Payable - Bank of Amerigold +820103 211.2 5000 Notes Payable - Z Equipment +.DE +.PP +Note the +.B \- +before +.BR chart . +It says that the standard input file +will be file 1 or on the left side of the table. +.SH "See Also" +.B +column, +sorttable +.R +.br +COHERENT Lexicon: +.B +join +.R diff --git a/man/julian.man b/man/julian.man new file mode 100644 index 0000000..3f42d3e --- /dev/null +++ b/man/julian.man @@ -0,0 +1,19 @@ +.TH julian "" "" "/rdb Command" +.PC "Convert column of dates for arithmetic and format change +\fBjulian [ -ceu ] [ \fIColumn ... \fB] < \fItableorlist\fR +.PP +.II "date^convert" +.II "Julian dates" +.II "date^Julian" +The command +.B julian +converts Gregorian dates in one or more columns to Julian format. +For details on the differences between Gregorian and Julian dates, and +how to use these commands in scripts, see the manual entry for the command +.BR gregorian . +.SH "See Also" +.B +computedate, +gregorian, +todaydate +.R diff --git a/man/justify.man b/man/justify.man new file mode 100644 index 0000000..48f231c --- /dev/null +++ b/man/justify.man @@ -0,0 +1,139 @@ +.TH justify "" "" "/rdb Command" +.PC "Left or right justify the columns of a table" +\fBjustify [-elrct'\fIc\fB'] [ \fIcolumn ... \fB] < \fItable\fR +.PP +.II "table^justify" +.II "column^justify" +.II "justify columns" +The command +.B justify +right-justifies the columns that contain only numbers, and +left-justifies the columns that contain any nonnumeric characters. +It also lines up floating-point numbers on their decimal points. +.PP +.B justify +improves the appearance of a table. +This makes it easier for you to read and edit. +It also makes a column of variable-length strings into one +length, by padding each string with spaces on the right. +Note that the extra spaces inserted to improve a table's appearance +could easily double or triple the size of the table. +This increased size not only takes up disk space, but slows processing of +.IR table . +You must decide whether appearance or size is more important. +.SH Options +.B justify +recognizes the following options: +.IP \fB\-e\fR +Expand the column to the next tab stop. +Ordinarily, a column is only as wide as the widest data or column name. +This improves the appear, but increases the size of the file. +.PP +The following options +override the default justifications (left-justify character strings, +right-justify numbers): +.IP \fB\-l\fR +(This is the letter ``el'', not the number one.) +Left-justify all named columns. +.IP \fB\-c\fR +Center all named columns. +.IP \fB\-r\fR +Right justify all named columns. +.PP +These options are sticky, meaning that if you follow an +.BR l , +.BR c , +or +.R r +option with several column names, each is treated according to +the option. +For example, +.B "\-l Description Account" +left justifies both +.B Account +and +.BR Description , +regardless of the data each contains. +.PP +.IP "\fB\-t\(aa\fIc\fB\(aa\fR" +Use the character +.I c +as the column separator instead of tab. +.I c +can be any ASCII character. +Note, however, that once you process a table with this option, +you can no longer use +.B /rdb +commands on it, because all +.B /rdb +commands require tabs as column separators. +See the command +.B trim +for a discussion of how you can protect special characters from the shell. +.SH Example +One use of +.B justify +is to align columns. +Variable-length character columns (like names and +descriptions) destroy the alignment of columns. +For example, notice how the variable-length names cause the tabs to +break at different points in the following table: +.DM +Name Credit +---- ------- +Anderson 23.49 +Ho 145.98 +Johnson 1.15 +.DE +.PP +One solution is to move the variable length column to the last column: +.DM + column Credit Name < credit +.DE +.PP +The table now appears as follows: +.DM +Credit Name +------- --------------- +23.49 Anderson +145.98 Ho +1.15 Johnson +.DE +.PP +Another solution is to use +.B justify +to right-pad the +.B Name +column, and (while we're at it) align the floating point number in the +.BR Credit : +.DM +.ta 1.5i +Name Credit +-------- ------ +Anderson 23.49 +Ho 145.98 +Johnson 1.15 +.DE +.PP +Remember that the file is now larger. +All of those extra blank spaces can double or triple the size of your file. +.PP +It is easier to enter the initial data into a table without +the padding characters. +Then you can use +.B justify +to pretty up the table. +.PP +When using the command +.B listtotable +to convert a list-format file to a table, +you may wish to use +.B justify +to make the resulting table readable. +.SH "See Also" +.B +column, +compresss, +listtotable, +trim +.R diff --git a/man/label.man b/man/label.man new file mode 100644 index 0000000..263dd36 --- /dev/null +++ b/man/label.man @@ -0,0 +1,63 @@ +.TH label "" "" "/rdb Command" +.PC "Print mailing labels from a mailing list" +\fBlabel < \fIlist\fR +.PP +.II "print mailing labels" +The command +.B label +prints mailing labels from a mailing list, in list format. +This command is obsolete because the command +.B report +can make labels more easily; but it is +is retained as an example of shell programming. +.PP +.B label +is a shell script so you can change it easily +to handle different lists and labels. +.SH Example +If you had a file called +.B maillist +that looked like this: +.DM +Number 1 +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.sp \n(pDu +Number 2 +Name Chiquita Banana +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 +.DE +.PP +The command +.DM + label < maillist +.DE +.PP +generates mailing labels that look like this: +.DM +Ronald McDonald +McDonald's +123 Mac Attack +Memphis, TENN 30000 +.sp 2 +Chiquita Banana +United Brands +Uno Avenido de la Reforma +San Jose, Costa Rica 123456789 +.DE +.SH "See Also" +.B +letter, +report +.R +.SO label diff --git a/man/length.man b/man/length.man new file mode 100644 index 0000000..9afefc4 --- /dev/null +++ b/man/length.man @@ -0,0 +1,54 @@ +.TH length "" "" "/rdb Command" +.PC "Return the length of its argument or input file" +\fBlength [\fIstring\fB] [< \fIfile\fB]\fR +.PP +.II "length of string" +.II "string^length" +The command +.B length +outputs the number of characters in its first argument. +If there +is no argument, it reads the standard input. +.PP +Because +.B length +counts only its first argument, you must put quotation marks +around all of the words on the command line to make them one argument. +.PP +.II wc +.B length +uses +.B "wc -c" +to do the counting. +.SH Example +Here we find the length of a string and a file. +.DM + length aA1 +.DE +.PP +This returns \fB3\fR. +.PP +The next command +.DM + length 'word1 word2' +.DE +.PP +returns \fB11\fR \(em that is, the length of +.B word1 +plus +.B word2 +plus the space between them. +.PP +The final example measures the number of characters in file +.BR length.1 : +.DM + length < length.1 +.DE +.PP +Note that you need must quote strings if they have spaces or tabs in them. +.SH "See Also" +COHERENT Lexicon: +.B +wc +.R +.SO length diff --git a/man/letter.man b/man/letter.man new file mode 100644 index 0000000..e77aa1f --- /dev/null +++ b/man/letter.man @@ -0,0 +1,150 @@ +.TH letter "" "" "/rdb Command" +.PC "Print form letters from a mailing list" +\fBletter \fIletter.1 ... \fB< \fImaillist\fR +.PP +.II "print letters" +.II "letters, print" +The command +.B letter +prints form letters from a standard letter and a mailing list. +It is obsolete, because the command +.B report +is so much more powerful and easier to use; but it is retained for +compatibility and as an example of shell programming. +.PP +.B letter +is a shell script, so you can be easily changed it +to handle different lists and letters. +To modify it, copy +.B letter +from +.B $RDB/bin/letter +to your local directory, then use your text editor to edit it. +Then you can modify it with a text editor +to handle any special features of your mailing list and letters. +.SH Example +If you have a file called +.B maillist +that looks like this: +.DM +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.sp \n(pDu +Name Chiquita Banana +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 +.DE +.PP +Assume, too, that the two parts of your letter are in two files. +.B letter.1 +contains: +.DM +We have a new product that we want to sell to everyone at +.DE +.PP +And +.B letter.2 +contains: +.DM +Please have everyone send us an order. We will be very grateful. +.sp \n(pDu +Sincerely, +.sp \n(pDu +Mr. Marketing +Makeapile, Inc. +.DE +.PP +To assemble the letter files and the mailing list into one set of +mail-merged letters, type the following command: +.DM + letter letter.1 letter.2 < maillist +.DE +.PP +This produces: +.DM +Ronald McDonald +McDonald's +123 Mac Attack +Memphis, TENN 30000 +.sp +Dear Ronald McDonald: +.sp \n(pDu +We have a new product that we want to sell to everyone at +McDonald's. Please have everyone send us an order. We will be +very grateful. +.sp +Sincerely, +.sp \n(pDu +Mr. Marketing +Makeapile, Inc. +.sp \n(pDu +\fI< ... spaces to bottom of letter ... > +.DE +.DM +Chiquita Banana +United Brands +Uno Avenido de la Reforma +San Jose, Costa Rica 123456789 +.sp +Dear Chiquita Banana: +.sp \n(pDu +We have a new product that we want to sell to everyone at United +Brands. Please have everyone send us an order. We will be very +grateful. +.sp \n(pDu +Sincerely, +Mr. Marketing +Makeapile, Inc. +.sp \n(pDu +\fI< ... spaces to bottom of letter ... > +.DE +.PP +The following sample letter program prints out the mailing name +and address, and the salutation \fBDear \fIName\fR. +It then \fBcat\fRs +.BR letter.1 , +echos the company name, and finally cats +.BR letter.2 . +.B letter +writes its output to temporary file +.BR TMP , +then +.BR nroff 's +the +.B TMP +file to the standard output. +.B nroff +reformats the file +(left and right justified) for each company name's length. +.PP +By studying the +.B letter +shell script and the COHERENT shell's documentation +.B (sh +or +.BR ksh) , +you can assemble fancy, complex form letters. +.SH "See Also" +.B +label, +report +.R +.br +COHERENT Lexicon: +.B +cat, +ksh, +nroff, +troff, +sh +.R +.SO letter diff --git a/man/like.man b/man/like.man new file mode 100644 index 0000000..b2a94a9 --- /dev/null +++ b/man/like.man @@ -0,0 +1,25 @@ +.TH like "" "" "/rdb Command" +.PC "Find names that sound like another name" +\fBlike \fIsimilar-name file \fB[ \fIname-column \fB]\fR +.PP +.II soundex +.II "name, convert to soundex" +.II "convert name to soundex" +.II "Knuth, Donald" +The command +.B like +converts a name to Knuth's soundex code and looks it up in the +.B .x +secondary-index file of a name. +To work, you must have first run the command +.BR soundex , +to create the soundex file. +.SH Example +See +.B soundex +for examples. +.SH "See Also" +.B +soundex +.R +.SO like diff --git a/man/listtosh.man b/man/listtosh.man new file mode 100644 index 0000000..b8f5ca0 --- /dev/null +++ b/man/listtosh.man @@ -0,0 +1,66 @@ +.TH listtosh "" "" "/rdb Command" +.PC "Convert list format to shell variable" +\fBlisttosh < \fIlist\fR +.PP +.II "list^convert to shell" +.II "convert list to shell" +The command +.B listtosh +converts a list-format file (\fIcolumn\fB\fIdata\fR) +to a shell variable format +(\fBVARIABLE='\fIvalue\fB'\fR). +You can use this to move a record into a shell variable. +.SH Example +Consider file +.BR maillist : +.DM +Number 1 +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Menphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.sp \n(pDu +Number 2 +Name Chiquita Banana +Company United Brands +Street Uno Avenito De La Revolution +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 +.DE +.PP +The command +.DM + eval `row 'Number == 2' < maillist | listtosh` +.DE +.PP +transforms the mailing list into the following format: +.DM +Number='2' +Name='Chiquita Banana' +Company='United Brands' +Street='Uno Avenito De La Revolution' +City='San Jose' +State='Costa Rica' +ZIP='123456789' +Phone='1234' +.DE +.PP +Once this is done, test the transformation by typing: +.DM + echo "$Name's phone number is $Phone." +.DE +.PP +This writes the following on the standard output: +.DM + Chiquita Banana's phone number is 1234. +.DE +.SH "See Also" +.B +tabletolist +.R +.SO listtosh diff --git a/man/listtotable.man b/man/listtotable.man new file mode 100644 index 0000000..01be02a --- /dev/null +++ b/man/listtotable.man @@ -0,0 +1,120 @@ +.TH listtotable "" "" "/rdb Command" +.PC "Convert from list to table format" +\fBlisttotable [ \-el ] < \fIlistfile\fR +.PP +.II "list^convert to table" +.II "table^from list" +.II "convert list to table" +.B listtotable +converts a file in list format to table format. +Some +.B /rdb +programs must see files in table format, +so the conversion is necessary. +.PP +In addition, table-formatted files are processed faster than +list-formatted files. +Any time you send a list-formatted file through a pipe of two +or more programs, even if they can handle list format, convert +them to table format so that the programs +in the pipe will run two or three times faster on average. +.PP +The reason that table-formatted files run so much faster +is that a list file is often two or three times larger +than a table because of the repeated column names. +Therefore, when handling list-formatted files, the programs +have to process two or three times as many characters. +.SH Options +.B listtotable +recognizes the following options: +.IP \fB\-e\fR 0.3i +Expand each column to next tab stop. +Ordinarily, table columns heads are only as wide as list column heads. +.IP \fB\-l\fR +No head line in the list file. +This option makes it possible to convert and sometimes +use the older list-formatted files. +.sp \n(pDu +Earlier versions of +.B /rdb +did not require the leading newline in the list-formatted file; however, +this requirement was added by later editions of +.B /rdb +to make it easy for its commands to distinguish a list file from a table. +.SH "List and Table-Format Rules" +List format looks like this: +.DM +Number 1 +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.DE +.DM +Number 2 +Name Chiquita Banana +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 + ... +.DE +.PP +Remember there are three rules for constructing a list-format file: +.IP \fB1.\fR 0.3i +The +first character in the file is a newline. +Be sure that there are no +unseen spaces, tabs, or nonprinting characters on the first +line, or things will get quite messed up. +.IP \fB2.\fR +Each line consists of two columns separated by a tab. +The first column is gives the column's +name, and the second column gives its values. +.IP \fB3.\fR +A newline character follows each +.I row +(record). +.PP +And there are three rules for contructing a table, as follows: +.IP \fB1.\fR 0.3i +One tab between each column in a row, and a newline at the end. +.IP \fB2.\fR +Name of the column at the top of the column, in the first line of the file. +.IP \fB3.\fR +A dash line as the second line in the file. +.SH Example +The command +.DM + listtotable < maillist +.DE +.PP +converts list file +.B maillist +into table format. +The result appears as follows: +.DM +Number Name Company Street City State ZIP Phone +------- ------- ------- ------- ------- ------- ------- ------- +1 Ronald McDonald McDonald's 123 Mac Attack Memphis +TENN 30000 (111) 222-3333 +2 Chiquita Banana United Brands Uno Avenito De La +Revolution San Jose Costa Rica 123456789 1234 +.DE +.PP +When you look at this messy table, +you can see the need for list format. +The information in each column is often too wide for the screen and +wraps around to the next line. +.SH "See Also" +.B +tabletolist +.R +.SH Notes +If you use this command with a table instead of a list file, you +will see a mess. diff --git a/man/lock.man b/man/lock.man new file mode 100644 index 0000000..7a2a54b --- /dev/null +++ b/man/lock.man @@ -0,0 +1,95 @@ +.TH lock "" "" "/rdb Command" +.PC "Lock a record or field of a file" +\fBlock \fIfilename processid from to indexfrom indexto\fR +.PP +.II "record locking" +The command +.B lock +locks +.I filename +by writing a row into the lock file \fB/tmp/L\fIfilename\fR, +which contains one line for each locked record or field. +Its command line contains the following arguments: +.IP \fIprocessid\fR +The process identifier of the program that locks +.IR filename . +.IP \fIfrom\fR +The beginning point of the bytes to be locked. +.IP \fIto\fR +The end point of the bytes to be locked. +.IP \fIindexbegin\fR +The beginning point of the index entry for the data being locked. +.IP \fIindexend\fR +The end point of the index entry for the data being locked. +.PP +When a process attempts to lock a string of bytes that is already locked, +.B lock +returns an error condition. +You can test for this by using the COHERENT command +.B test +to test the shell variable \fB$?\fR. +.SH Example +Let's use +.B lock +on table +.BR inventory , +which appears as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +First, use +.B seek +to locate the record and return the offset and size, as follows: +.DM + LOCATION=`echo 5 | seek -mb inventory Item` + echo $LOCATION + 207 245 0 8 +.DE +.PP +This means that the record is offset 207 bytes into file +.BR inventory , +and that it is 38 bytes long, ending at byte 245. +The 0 and 8 mean that there is no secondary index file. +.PP +Now we can call +.B lock +to lock the record: +.DM + lock inventory $$ $LOCATION +.DE +.PP +Note that we use the shell variable +.B $LOCATION +to supply the arguments +.I from +and +.IR to . +We also use the built-in shell variable \fB$$\fR to +write the process identifier on the command line. +.PP +The lock +.B /tmp/Linventory +has the following contents: +.DM + 207 245 0 8 +.DE +.SH "See Also" +.B +seek, +unlock +.R +.br +COHERENT Lexicon: +.B +test +.R +.SO lock diff --git a/man/lowercase.man b/man/lowercase.man new file mode 100644 index 0000000..2265266 --- /dev/null +++ b/man/lowercase.man @@ -0,0 +1,56 @@ +.TH lowercase "" "" "/rdb Command" +.PC "Transform text to lower case" +\fBlowercase [\fIstring ...\fB] [< \fIfile\fB]\fR +.PP +.II "string^convert to lower case" +.II "convert string to lower case" +The command +.B lowercase +converts to lower case every letter in +.I string +or +.IR file . +If there are no arguments, it reads and converts the standard input. +It uses +.BR tr , +the COHERENT character-translation command, to do the converting. +.SH Example +The command: +.DM + lowercase BIG WORDS +.DE +.PP +prints +.DM + big words +.DE +.PP +on the standard output. +.PP +If the file +.B CAPITALS +contains the string +.DM + THESE ARE ALL CAPS. +.DE +.PP +then the command +.DM + lowercase < CAPITALS +.DE +.PP +prints on the standard output: +.DM + these are all caps. +.DE +.SH "See Also" +.B +cap, +uppercase +.R +.br +COHERENT Lexicon: +.B +tr +.R +.SO lowercase diff --git a/man/maximum.man b/man/maximum.man new file mode 100644 index 0000000..66d2f98 --- /dev/null +++ b/man/maximum.man @@ -0,0 +1,27 @@ +.TH maximum "" "" "/rdb Command" +.PC "Display the maximum value in a column" +\fBmaximum [ \-l ] [ \fIcolumn ... \fB] < \fItable\fR +.PP +.II column^maximum value" +The command +.B maximum +displays the maximum value of each +.IR column . +If no +.I column +appears on the command line, +.B maximum +returns the maximum value in every column in +.IR table . +.PP +The \fB\-l\fR +option writes the table to the standard output, as well as the +minimum value for each named column. +.SH Example +For an example of this command, see the entry for +.BR total . +.SH "See Also" +.B +mean, +minimum +.R diff --git a/man/mean.man b/man/mean.man new file mode 100644 index 0000000..ba1ea70 --- /dev/null +++ b/man/mean.man @@ -0,0 +1,37 @@ +.TH mean "" "" "/rdb Command" +.PC "Display the mean of a column" +\fBmean [ \-l ] [ \fIcolumn ... \fB] < \fItable\fR +.PP +.II "column^mean value" +.B mean +displays the mean value of each +.IR column . +If no +.I column +appears on the command line, +.B mean +returns the mean value for each column in +.IR table . +.PP +.B mean +assign the value of zero to a blank line. +This changes the value of the mean. +If that is not what you want, use the command +.B rmblanks +or +.B "compress \-b" +to remove blank lines. +.B mean +also treats blank values as zero +.PP +The option \fB\-l\fR +prints the contents of the table, as well as the mean value for each +named column. +.SH Example +For an example of this command, see the entry for +.BR total . +.SH "See Also" +.B +maximum, +minimum +.R diff --git a/man/menu.man b/man/menu.man new file mode 100644 index 0000000..31cad01 --- /dev/null +++ b/man/menu.man @@ -0,0 +1,276 @@ +.TH menu "" "" "/rdb Command" +.PC "Root menu with some COHERENT commands" +.B menu +.PP +.II "command^menu" +The command +.B menu +displays a table of commands that you can execute by +simply typing a number or a name. +Menus are useful for inexperienced users. +Experienced users can set up a menu very easily. +.PP +.B menu +is a shell script that simply echos the +.B menu +table of choices, then waits for the user to type a number or name. +Then +.B menu +uses a shell \fBcase\fR statement +to execute a command or series of commands for each +choice. +To set up your own menus, simply copy the +.B menu +shell command from the +.B $RDB/bin +into your directory, then edit it. +You should change its name to +.B Menu +or +.BR Menu.something , +to keep from confusing +.BR /rdb . +.PP +There is a sample local +.B menu, +named +.B Menu +in directory +.BR $RDB/demo . +.SH Example +The following gives an example menu: +.B1 + COHERENT MENU +Number Name For +------- ------- ---------------------------------------------------- + 0 exit leave menu or return to higher menu + 1 Menu goto another local menu (if any) + 2 sh get COHERENT shell + 3 vi edit a file + 4 mail read mail + 5 send send mail to someone + 6 cal see your calendar + 7 who see who is on the system + 8 ls list the files in this directory + 9 cat display a file on the screen + 10 rdb display rdb commands +.sp \n(pDu +Enter a number or name for the action you wish or DEL to exit: +.B2 +.PP +.B menu +waits for you to type your choice of number or name. +Numbers are quicker to type, but names are more easily remembered, and +help you learn how to do things at the shell level. +.SH "Setting Up Your Own Menus" +You can automate your operations with menus. +If you have naive users who might have trouble learning or +remembering the COHERENT or +.B /rdb +commands, you can set up menus for them. +Also, when you have an operation in which a few commands +are repeatedly executed, you can put them into a menu for simple choice. +.PP +You can put your menus in the directories in which they +will be used or in your +.B $HOME/bin +or other +.B bin +directory. +.PP +To start, go to the directory of your choice and copy the +.B $RDB +.B menu +to your directory: +.DM + cp $RDB/bin/menu Menu +.DE +.PP +or +.DM + cp $RDB/demo/Menu . +.DE +.PP +We suggest the convention of using the name +.B Menu +with the capital `M' +so it appears at the top of your list of files when you do an +.B lc +command. +.PP +You can then name submenus; for example, +\fBMenu.\fIother\fR +where +.I other +is something that suggests the kinds of commands the menu displays. +.PP +Now edit the +.B Menu +file. +As you study the code, you will find that it is very easy to +create your own directory. +.PP +Menu is in two parts. +The first part is the menu table: +.DM + cat << SCREEN +$CLEAR COHERENT MENU +Number Name For +------- ------- ------------------------- + 0 exit leave menu or return to higher menu + 1 Menu goto another local menu (if any) + 2 sh get unix shell + 3 vi edit a file + 4 mail read mail + 5 send send mail to someone + 6 cal see your calendar + 7 who see who is on the system + 8 ls list the files in this directory + 9 cat display a file on the screen + 10 rdb display rdb commands +.sp \n(pDu +Please enter a number or name for the action you wish or DEL to exit: +.sp \n(pDu +SCREEN +.DE +.PP +The command +.B cat +simply sends to the screen everything +after the command and before the line that +begins with +.BR SCREEN , +including the line that begins +.BR "Please enter a number" . +.PP +The shell replaces +.B $CLEAR +on the first line with a sequence of characters that clears the terminal. +.B $CLEAR +must have been set by the following command in the +.B menu +or in the +.B .profile +file of the user: +.DM +.II clear + CLEAR=`clear` +.DE +.PP +The second part of +.B Menu +is a large +.B case +statement that you can edit to do what you want. +Here is a sample: +.DM +.II read + read ANSWER COMMENT +.DE +.DM +.II case + case $ANSWER in +.DE +.DM + 0|exit) exit 0 ;; + 1|Menu) Menu ;; + 2|sh) sh ;; + 3|vi) + echo 'Which file or files do you wish to edit' + read ANSWER COMMENT + vi $ANSWER $COMMENT + ;; +.DE +.DM + 4|mail) mail ;; + 5|send) + echo 'Please enter login name of person to send mail to' + read ANSWER COMMENT + echo 'Type you letter, and end by typing Ctrl-d' + mail $ANSWER + ;; +.DE +.DM + 6|cal) (cd ; calendar) ;; + 7|who) who ;; + 8|ls) ls ;; + 9|cat) + echo 'Please enter the name of the file you wish to see' + read ANSWER COMMENT + cat $ANSWER + ;; +.DE +.DM + 10|rdb) menu.rdb ;; + *) echo 'Sorry, but that number or name is not recognized.' ;; + esac +.DE +.PP +The command +.B read +waits for the user to enter his choice. +The first word typed is assigned to the shell variable +.BR ANSWER . +If the user types any more words, they are assigned to variable +.BR COMMENT , +which might be used in your commands. +.PP +Next is the +.B case +statement that examines +.B $ANSWER +to decide which command to execute. +Note that the number and name +are separated by a vertical-bar character `|' to mean +.BR or : +.DM + 0|exit) exit 0 ;; +.DE +.PP +This line is selected if type either 0 or +.BR exit . +The command +.B exit +is then executed. +The double semicolons ``;;'' +are required to show the end of the commands for this +.B case +statement. +.PP +Each +.B case +statement can hold any command or series of commands that you +could type at your terminal. +.B esac +at the bottom is +.B case +spelled backwards; it marks the end of the whole +.B case +statement. +.PP +The expression +.B *) +catches any pattern that does not match earlier +patterns. +In this case, a message is displayed. +The pound sign `#' marks the beginning of a comment. +.PP +You have great power to do things as a result of +the user selecting a choice. +Of course, you need to match the name and number in your +menu table with the patterns in the +.B case +statement; +it is easy to edit the first part and forget to edit the second. +.SH "See Also" +COHERENT Lexicon: +.B +case, +cat, +clear, +echo, +esac, +ksh, +read, +sh +.R diff --git a/man/minimum.man b/man/minimum.man new file mode 100644 index 0000000..54c5239 --- /dev/null +++ b/man/minimum.man @@ -0,0 +1,33 @@ +.TH minimum "" "" "/rdb Command" +.PC "Display the minimum of each column selected" +\fBminimum [ \-l ] [ \fIcolumn ... \fB] < \fItable\fR +.PP +.II "column^minimum value" +The command +.B minimum +returns the lowest value of each +.I column +in +.IR table . +If no +column is named on the command line, +.B minimum +returns the minimum value for every column in +.IR table . +Blank lines are given the value of zero. +.PP +Option \fB\-l\fR returns the contents of +.IR table , +as well as the minimum value of each +.IR column . +.SH Example +For an example of using the command in a script, see the +.B +total +.R +.SH "See Also" +.B +maximum, +mean, +total +.R diff --git a/man/not.man b/man/not.man new file mode 100644 index 0000000..df32f03 --- /dev/null +++ b/man/not.man @@ -0,0 +1,35 @@ +.TH not "" "" "/rdb Command" +.PC "Logical not, to reverse return status of command" +\fBnot \fIcommand ...\fR +.PP +.II "return value^invert" +.II "logical NOT" +The command +.B not +converts a return status of zero to 255, and any nonzero +return status to zero. +Thus, true becomes false and vice versa. +This is used to test a command for not true or false. +.SH Example +This example shows how +.B not +reverses the return code of the COHERENT command +.BR true : +.DM + true ; echo $? +.DE +.PP +This command returns zero. +On the other hand, the command +.DM + not true ; echo $? +.DE +.PP +returns 255. +.SH "See Also" +COHERENT Lexicon: +.B +false, +true +.R +.SO not diff --git a/man/number.c.man b/man/number.c.man new file mode 100644 index 0000000..1eeb460 --- /dev/null +++ b/man/number.c.man @@ -0,0 +1,27 @@ +.TH Number "" "" "/rdb Command" +.PC "Insert a column-row number into a table or list" +\fBNumber < \fItableorlist\fR +.PP +.B Number +creates appends a new column to the beginning of a table, +or as the first field in a list. +The newly created column contains the number of each row in the table. +Unlike the related command +.BR number , +.B Number +capitalizes +the column heading \(em that is, +the name of the command sets the name of the column. +.SH "See Also" +.B +number +.R +.SH Notes +To change the name of a column, use the command +.BR rename . +To move the row-number column to a different position within its table, +use the command +.BR column . +If you want to change the name of the number column +to something else, use the command +.BR rename . diff --git a/man/number.man b/man/number.man new file mode 100644 index 0000000..861748d --- /dev/null +++ b/man/number.man @@ -0,0 +1,70 @@ +.TH number "" "" "/rdb Command" +.PC "Insert a column-row number into a table or list" +\fBNumber < \fItableorlist\fR +.PP +.II "column^append row number" +.II "row^number column" +.II "append row-number column" +.II "table^append row-number column" +The command +.B number +creates appends a new column to the beginning of a table, +or as the first field in a list. +The newly created column contains the number of each row in the table. +Unlike the related command +.BR Number , +.B number +writes the word ``number'' in lower-case letters as the header on the +column. +.SH Example +For example, consider the following table, called +.BR inventory : +.DM +Item# Amount Cost Value Description +------- ------- ------- ------- --------------- + 1 3 5.00 15.00 rubber gloves + 2 100 0.50 50.00 test tubes + 3 5 8.00 40.00 clamps + 4 23 1.98 45.54 plates + 5 99 2.45 242.55 cleaning cloth + 6 89 14.75 1312.75 bunsen burners + 7 5 175.00 875.00 scales +.DE +.PP +Typing the command +.DM + number < inventory +.DE +.PP +yields the following result: +.DM + number Item# Amount Cost Value Description +------- ------- ------- ------- ------- --------------- + 1 1 3 5.00 15.00 rubber gloves + 2 2 100 0.50 50.00 test tubes + 3 3 5 8.00 40.00 clamps + 4 4 23 1.98 45.54 plates + 5 5 99 2.45 242.55 cleaning cloth + 6 6 89 14.75 1312.75 bunsen burners + 7 7 5 175.00 875.00 scales +.DE +.PP +Note that the first column is the new number column. +.PP +.SH "See Also" +.B +Number, +addcol, +column, +jointable, +rename +.R +.SH Notes +To change the name of a column, use the command +.BR rename . +To move the row-number column to a different position within its table, +use the command +.BR column . +If you want to change the name of the number column +to something else, use the command +.BR rename . diff --git a/man/pad.man b/man/pad.man new file mode 100644 index 0000000..63bf81e --- /dev/null +++ b/man/pad.man @@ -0,0 +1,103 @@ +.TH pad "" "" "/rdb Command" +.PC "Add extra spaces at end of last column" +\fBpad [-\fInumber\fB] < \fItableorlist\fR +.PP +.II "row^pad with spaces" +.II "table^pad" +The command +.B pad +adds extra spaces to the end of the last column of each record in +.IR tableorlist . +\fInumber\fR gives the number of spaces to add to the last column. +The default is 70. +.PP +.B pad +is useful for preparing up a file for updating by the command +.BR update . +.B update +writes an edited record back into the file in the same +place it was taken out. +If you add characters while editing the record, it will not fit. +.PP +The command +.B replace +looks for trailing and leading spaces to trim, +to fit the record back. +If it does not find enough room, it appends the record to the end of the file. +Padding gives each record a fixed number of extra spaces to be +trimmed as needed. +.PP +The traditional solution is to use fixed-length records and fields. +To do this, you can use the command +.B justify +to pad the file; +but this often increase the file by two or three times. +That makes the file much larger, and increases the time needed to process it. +.B pad +lets you control how much larger each record will be. +.SH Example +To pad file +.IR maillist , +type: +.DM + pad < maillist > tmp +.DE +.PP +To +.B see +the results, pipe the results to the command \fBsee\fR: +.DM + pad < maillist | see +.DE +.PP +The following appears on the standard output: +.DM +Number^J1$ +Name^JRonald McDonald$ +Company^JMcDonald's$ +Street^J123 Mac Attack$ +City^JMenphis$ +State^JTENN$ +ZIP^J30000$ +Phone^J(111) 222-3333 $ +$ +Number^J2$ +Name^JChachita Banana$ +Company^JUnited Brands$ +Street^JUno Avenito De La Revolution$ +City^JSan Jose$ +State^JEl Salvadore$ +ZIP^J123456789$ +Phone^J1234 $ +$ +.DE +.PP +Note the dollar signs, which mark the end of the line, are +far to the right of the last column, +.IR Phone . +Those are the 70 extra spaces that have been added; 70 being the default +number of spaces. +On such a small record, that is probably far too many. +.SH Technical +Note that column +.I Phone +already had a value in it. +If a value is already in the last column, +.B pad +overwrites the spaces. +If the value is longer than the number of spaces, +.B pad +writes the value but adds no spaces. +.PP +We recommend that you have a last column named +.I Comment +or +.I Remarks +to act as a catch-all for both comments and padding. +.SH "See Also" +.B +justify, +replace, +see, +update +.R diff --git a/man/padstring.man b/man/padstring.man new file mode 100644 index 0000000..aa2111f --- /dev/null +++ b/man/padstring.man @@ -0,0 +1,116 @@ +.TH padstring "" "" "/rdb Command" +.PC "Return string with blanks to fill a field" +\fBpadstring [-]\fIlength string\fR +.PP +.II "string^pad with spaces" +The command +.B padstring +adds blanks to the end of +.I string +to make it the requested +.IR length . +This command useful for screen and report writing, +where you want to line up everything. +.PP +The option +.I -length +means to left-justify the string in the field, that is, +write the padding blanks to the end of +.IR string . +The option +.IR length , +without the hyphen, +right justifies the string \(em that is, it adds the spaces to the +beginning of +.IR string . +.SH Example +The first example +.DM + padstring 10 string +.DE +.PP +displays the string: +.DM + string +.DE +.PP +Note that the string is right-justified; that is, the spaces were added to +the left of +.BR string . +.PP +Now we can use this in a +.B report +form file to make fixed-length fields. +An ordinary form has the files to the right of a field, moving all around. +For example, the command +.DM + report form < mailtable +.DE +.PP +writes the output: +.DM +Name : Ronald McDonald Company: McDonald's +Street: 123 Mac Attack +City : Menphis State : TENN ZIP: 30000 +Phone : (111) 222-3333 +.sp \n(pDu +Name : Chiquita Banana Company: United Brands +Street: Uno Avenito De La Revolution +City : San Jose State : Costa Rica ZIP: 123456789 +Phone : 1234 +.DE +.PP +This result is rather messy. +However, if we edit the file +.B form +to include the +.B padstring +command, then we will get a fixed form, as follows: +.DM +Name : '!> Company: +Street: +City : '!> State : ZIP: +Phone : +.DE +.PP +Now, running +.B report +on this file yields: +.DM +Name : Ronald McDonald Company: McDonald's +Street: 123 Mac Attack +City : Menphis State : TENN ZIP: 30000 +Phone : (111) 222-3333 +.sp \n(pDu +Name : Chiquita Banana Company: United Brands +Street: Uno Avenito De La Revolution +City : San Jose State : Costa Rica ZIP: 123456789 +Phone : 1234 +.DE +.PP +Note that +.B State +has not been done yet, and therefore +.B ZIP +moves back and forth; +but +.B Company +and +.B State +stay lined up correctly. +.PP +Note that if +.B padstring +is too long a name, use the COHERENT link command +.B ln +to link it to a shorter name. +.SH "See Also" +.B +report, +screen +.R +.br +COHERENT Lexicon: +.B +ln +.R diff --git a/man/paste.rdb.man b/man/paste.rdb.man new file mode 100644 index 0000000..6fb9d9f --- /dev/null +++ b/man/paste.rdb.man @@ -0,0 +1,137 @@ +.TH paste.rdb "" "" "/rdb Command" +.PC "Paste together two or more tables" +\fBpaste.rdb \fItable1 table2 ...\fR +.PP +.II "table^paste" +.II "paste tables" +The command +.B paste.rdb +displays +.IR table1 , +.IR table2 , +and so on, side by side. +This is almost identical to the COHERENT command +.BR paste . +.PP +The +.B /rdb +version of +.B paste.rdb +uses the COHERENT +command +.B pr +to list the tables side by side: +.DM + pr -m -t -s table1 table2 [...] +.DE +.PP +.B paste.rdb +differs from the command +.B jointable +in that +.B jointable +looks for matching values in key columns and writes only the +rows in which it finds a match. +.BR paste.rdb +does not care what it is pasting together. +You can get some interesting garbage with +.BR paste.rdb . +.PP +You can also do a kind of visual +.B diff +in which you can see an old and new file lined up side by side. +You could do this with two list files, but be careful, because +the output will generally not be acceptable by other +.B /rdb +programs. +.SH Example +The following commands break the table +.BR inventory : +.DM + column Item Value < inventory > tmp1 + column Cost Amount Description < inventory > tmp2 +.DE +.PP +This yields table +.BR tmp1 +.DM +Item Value +----- ----- + 1 150 + 2 500 + 3 400 + 4 437 + 5 2376 + 6 13083 + 7 875 +.DE +.PP +.BR tmp2 : +.DM +Cost Amount Description +---- ------ -------------- + 50 3 rubber gloves + 5 100 test tubes + 80 5 clamps + 19 23 plates + 24 99 cleaning cloth + 147 89 bunsen burners + 175 5 scales +.DE +.PP +Now, you can use +.B paste.rdb +to put them back together: +.DM + paste.rdb tmp1 tmp2 +.DE +.PP +This writes the following onto the standard output: +.DM +Item Value Cost Amount Description +----- ----- ---- ------ -------------- +1 150 50 3 rubber gloves +2 500 5 100 test tubes +3 400 80 5 clamps +4 437 19 23 plates +5 2376 24 99 cleaning cloth +6 13083 147 89 bunsen burners +7 875 175 5 scales +.DE +.PP +This is not an efficient way use +.BR column ; +one command could do the job, instead of two. +.BR paste.rdb , +however, it is a good way to put tables together if +.B jointable +is inappropriate. +If you had wanted to use +.B jointable +here, you should +.B project +column +.B Item +into both tables. +.SH "See Also" +.B +column, +jointable +.R +.br +COHERENT Lexicon: +.B +diff, +paste, +pr +.R +.SH Notes +This command is named +.B paste.rdb +rather than +.BR paste , +as in other implementations of +.BR /rdb , +to avoid clashing with the COHERENT command +.BR paste . +.SO paste diff --git a/man/path.man b/man/path.man new file mode 100644 index 0000000..4892985 --- /dev/null +++ b/man/path.man @@ -0,0 +1,80 @@ +.TH path "" "" "/rdb Command" +.PC "Find the full path of a command" +\fBpath [ \fIcommand ... \fB]\fR +.PP +.II "command^find path" +The command +.B path +finds the directory path to a command, if it is in one of the +directories in your shell's +.B PATH +environment variable. +.PP +A path means two things in COHERENT: +.IP \fB1.\fR 0.3i +It means the list of nested directories within which a file is located. +For example, file +.B FOO +may have the path of +.BR /usr/local/src/ ; +this means that FOO +is kept in directory +.BR src , +which in turn is in directory +.BR local , +which in turn is in directory +.BR usr , +which in turn is in directory +.BR / . +.IP \fB2.\fR +It means a list of directories that the shell searches +to find an executable program that matches the command typed in. +Each user has his path stored in the environmental variable +.BR PATH ; +to see your path, type the command: +.DM + echo $PATH +.DE +.PP +.B path +searches the directories named in your +.B PATH +to find the path of +.IR command . +If +.B path +cannot find +.I command +or if +.I command +is not executable, it returns nothing. +.SH Example +If you type: +.DM + path column +.DE +.PP +If this command returns +.DM + /usr/rdb/bin +.DE +.PP +then +.B column +lives in directory: +.BR /usr/rdb/bin . +.PP +Please note that for +.B path +to work, you must insert into your +.B PATH +the directory that holds the +.B /rdb +commands. +.SH "See Also" +COHERENT Lexicon: +.B +PATH, +\&.profile +.R +.SO path diff --git a/man/precision.man b/man/precision.man new file mode 100644 index 0000000..f3e2390 --- /dev/null +++ b/man/precision.man @@ -0,0 +1,29 @@ +.TH precision "" "" "/rdb Command" +.PC "Display the precision of a column" +\fBprecision [ \-l ] [ \fIcolumn ...\fB ] < \fItable\fR +.PP +.II "column^find precision" +The command +.B precision +returns the maximum number of digits to the right of the +decimal point in each +.IR column . +If no +.I column +is named on the command line, +.B precision +returns information on every numeric column in +.IR table . +.PP +The option +.B \-l +tells +.B precision +to return the entire +.IR table , +in addition to the precision of each +.IR column . +.SH Example +For example of this command, see the entry for +.BR total . + diff --git a/man/project.man b/man/project.man new file mode 100644 index 0000000..906e215 --- /dev/null +++ b/man/project.man @@ -0,0 +1,12 @@ +.TH project "" "" "/rdb Command" +.PC "Write selected projects (same as column)" +\fBproject [ \fIcolumn ...\fB ] < \fItableorlist\fR +.PP +The command +.B project +is another name for the command +.BR column . +.SH "See Also" +.B +column +.R diff --git a/man/prompt.man b/man/prompt.man new file mode 100644 index 0000000..f09f04d --- /dev/null +++ b/man/prompt.man @@ -0,0 +1,60 @@ +.TH prompt "" "" "/rdb Command" +.PC "Echo a string on the standard output" +\fBprompt \fIstring ...\fR +.PP +.II "string^prompt" +.II echo +The command +.B prompt +echoes a string onto the standard output. +It resembles the COHERENT command +.BR echo , +but it does not write a newline character after the echoed string. +Thus, you can use +.B prompt +to write a prompt onto the screen. +.SH Example +The command +.DM + prompt "Please enter your name: " +.DE +.PP +writes the following on the standard output: +.DM + Please enter your name: _ +.DE +.PP +The underscore character marks where this command leaves the cursor. +.SH "See Also" +.B +chr +.R +.br +COHERENT Lexicon: +.B +echo +.R +.SH Notes +.B prompt +looks for an environmental variable called +.BR ECHONOCR . +If the variable is missing or not equal to +.BR \-n , +then +.B prompt +calles the COHERENT command +.B echo +with its \fB\-n\fR option, which drops the newline. +If +.B ECHONOCR +is set to anything other than +.BR \-n , +then +.B prompt +calls +.BR \ec +option, which is used by some UNIX systems. +When using +.BR /rdb +under the COHERENT system, it is best just to not set +.BR ECHONOCR . diff --git a/man/rdb.man b/man/rdb.man new file mode 100644 index 0000000..8e8a8ea --- /dev/null +++ b/man/rdb.man @@ -0,0 +1,20 @@ +.TH rdb "" "" "/rdb Command" +.PC "List all /rdb programs in directory $RDB/bin" +.B rdb +.PP +.II "command^list all" +.II "list all commands" +The command +.B rdb +prints on the standard output all commands in directory +.BR $RDB/bin . +This command is a shell script, which you should edit if its output +is not to your liking. +.SH "See Also" +.B +act, +helpme, +whatis, +whatwill +.R +.SO rdb diff --git a/man/rdb.tmac b/man/rdb.tmac new file mode 100644 index 0000000..cde9107 --- /dev/null +++ b/man/rdb.tmac @@ -0,0 +1,27 @@ +.\" Macros used by the MWC /rdb man pages that groff's man package lacks. +.de DM +.sp 0.5 +.nf +.in +4n +.. +.de DS +.DM +.. +.de DE +.in -4n +.fi +.sp 0.5 +.. +.de PC +.\" short description, already shown by .TH +.. +.de II +.. +.de SO +.. +.de B1 +.DM +.. +.de B2 +.DE +.. diff --git a/man/record.man b/man/record.man new file mode 100644 index 0000000..5591986 --- /dev/null +++ b/man/record.man @@ -0,0 +1,72 @@ +.TH record "" "" "/rdb Command" +.PC "Find and output a record from a table" +\fBrecord \fInumber \fB< \fItable\fR +.PP +.II "row^find r. by number" +.II "find record by number" +The command +.B record +searches through +.I table +for the record with the serial number +.IR number . +If it finds the record, it prints it and the table's header on the +standard output. +.SH Example +To retrieve the third record from table +.BR inventory , +type: +.DM + record 3 < inventory +.DE +.PP +You will see something like the following: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 3 5 80 400 clamps +.DE +.PP +The following shell script uses +.B record +and +.B read +to find specific values in table +.BR inventory . +The command +.B read +uses the head line of +.B inventory +to assign values to each column name. +.DM +: describe - gives the description for an item in inventory +USAGE='usage: describe Item < table' +.sp \n(pDu +record $1 < inventory | +( read HEAD + read DASH + read $HEAD + echo "Item $Item is $Description" +) +.DE +.PP +When you type +.DM + describe 1 < inventory +.DE +.PP +you see output in the following format: +.DM + Item 1 is rubber gloves +.DE +.SH "See Also" +.B +row, +search +.R +.br +COHERENT Lexicon: +.B +read +.R +.SO record diff --git a/man/rename.man b/man/rename.man new file mode 100644 index 0000000..754fb42 --- /dev/null +++ b/man/rename.man @@ -0,0 +1,118 @@ +.TH rename "" "" "/rdb Command" +.PC "Rename a column" +\fBrename [ \fIoldcolumn newcolumn ...\fB ] < \fItableorlist\fR +.PP +.II column^rename +The command +.B rename +changes the name of +.I oldcolumn +to +.I newcolumn +within +.IR tableorlist . +You can name on the command line +as many \fIoldcolumn/newcolumn\fR pairs as you wish. +.SH Example +Consider the table +.BR inventory : +.DM +Item# Amount Cost Value Description +----- ------ ----- ------- -------------- + 1 3 5.00 15.00 rubber gloves + 2 100 0.50 50.00 test tubes + 3 5 8.00 40.00 clamps + 4 23 1.98 45.54 plates + 5 99 2.45 242.55 cleaning cloth + 6 89 14.75 1312.75 bunsen burners + 7 5 175.00 875.00 scales +.DE +.PP +The command +.DM + rename Amount Qty Description Name < inventory +.DE +.PP +renames +.B Amount +to +.BR Qty , +and +.B Description +to +.BR Name . +The table now appears as follows: +.DM +Item# Qty Cost Value Name +----- --- ------ ------- -------------- + 1 3 5.00 15.00 rubber gloves + 2 100 0.50 50.00 test tubes + 3 5 8.00 40.00 clamps + 4 23 1.98 45.54 plates + 5 99 2.45 242.55 cleaning cloth + 6 89 14.75 1312.75 bunsen burners + 7 5 175.00 875.00 scales +.DE +.PP +If you have a list-formatted file, you can also rename the column names. +For example, consider the list +.BR maillist : +.DM +Number 1 +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.DE +.DM +Number 2 +Name Chiquita Banana +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 +.DE +.PP +The command +.DM + rename Number Num ZIP Zip < maillist +.DE +.PP +changes +.B Number +to +.BR Num , +and +.B ZIP +to +.BR Zip . +The list now appears as follows: +.DM +Num 1 +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Memphis +State TENN +Zip 30000 +Phone (111) 222-3333 +.DE +.DM +Num 2 +Name Chiquita Banana +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +Zip 123456789 +Phone 1234 +.DE +.SH "See Also" +.B +column +.R diff --git a/man/replace.man b/man/replace.man new file mode 100644 index 0000000..52e1b6b --- /dev/null +++ b/man/replace.man @@ -0,0 +1,154 @@ +.TH replace "" "" "/rdb Command" +.PC "Insert a record into a file at specified location" +\fBreplace [\-a] [\-m[bhirs] \fItableorlist from to \fB[\fIxfrom xto\fB] < \fIintableorlist\fR +.PP +.II row^replace +The command +.B replace +replaces the row at offsets +.I from +through +.I to +in +.I tableorlist +with the contents of +.IR intableorlist . +.B replace +will not insert a record larger than +.I to +minus +.IR from . +It adds spaces to the +.I last +column of records within +.I tableorlist +that are smaller than the input record. +.PP +.I xfrom +and +.I xto +give the beginning and end of the offsets of the index entry within +.IR tableorlist 's +associated fast-access table, should there be one. +.PP +To obtain painlessly the offsets of the record and its index entry, use +the command +.BR seek . +This is demonstrated in the following example. +.PP +The command +.B update +remembers the location and size of the record it takes out of a file +and passes it to +.B replace +so that only a correctly sized file goes back into that file. +.SH Options +.B replace +recognizes the following options: +.IP \fB\-a\fR +Do not append the record in +.I intableorlist +to the end of +.IR tableorlist . +The default to append if the record is too big +to fit back into the location from which it was taken. +This option allows you to prevent appending. +If you want a binary file to stay +sorted, you will either have to resort it or not allow appending. +.IP \fB\-m[bhirs]\fR +Fast access methods. +See the manual page for +.B index +for a description of what each of these options means. +.SH Example +If table +.B recordtable +contains one record, you use can insert that record into table +.B bigfile +with the following command: +.DM + replace bigfile 1421 1487 0 8 < recordtable +.DE +.PP +Note that the contents of +.B recordtable +replace rows 1421 through 1487 within +.BR bigfile . +.PP +For the second example, consider again table +.BR inventory , +as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +First, let's use the command +.B seek +to extract a record, and save its offset in the shell variable +.BR LOCATION : +.DM + LOCATION=`echo 5 | seek -mr -o tmpfile inventory Item` +.DE +.PP +.B $LOCATION +is initialized to the following +.DM + 207 245 54 62 +.DE +.PP +and +.B tmpfile +contains the following: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 5 99 24 2376 cleaning cloth +.DE +.PP +Here we use the record fast-access method. +.PP +Now let's +change 99 to 199 within +.BR tmpfile , +so we can see a change: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 5 199 24 2376 cleaning cloth +.DE +.PP +Now we can replace the edited record in +.BR inventory : +.DM + replace -mr inventory $LOCATION < tmpfile +.DE +.PP +.B inventory +now appears as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 199 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +You can see that row 5 has been updated, because it now has +199 in it. +.SH "See Also" +.B +seek, +update +.R diff --git a/man/report.man b/man/report.man new file mode 100644 index 0000000..c1f690c --- /dev/null +++ b/man/report.man @@ -0,0 +1,187 @@ +.TH report "" "" "/rdb Command" +.PC "Write reports using a form and a table" +\fBreport \fIform \fB< \fItableorlist\fR +.PP +The command +.B report +produces reports, form letters, mailing labels, invoices, +and so forth, for each row in +.IR tableorlist , +following the template within file +.IR form . +.PP +You must use a text editor to fill out +.I form +to determine how the form will appear. +Insert column names from +.I tableorlist +between angle brackets (< >) +where you want the data to appear within the report. +.PP +You can even call shell commands from within a form, +pass them data from +.IR tableorlist , +print their output within your formatted form. +This is a powerful extension to COHERENT shell programming. +.PP +.B report +mostly replaces the older commands +.B label +and +.BR letter . +These commands are still part of +.BR /rdb , +in part for compatibility with older versions, and in part as examples +of shell programming. +.SH "Form Syntax" +The following describes the format of a form: +.IP "\fIregular text\fR" +.B form +prints any characters in your form that do not match the symbols +shown below. +.IP "\fI\fR" +Angle brackets indicate that data from +.I "column-name" +in the current row of +.I tableorlist +should be inserted in place of the column name. +.IP "\fI\fR" +Angle brackets and exclamation points indicate that the shell is to execute +.IR command . +Any output from +.I command +appears at this point in the report. +.IP "\fI!>\fR" +This is an insertion within a command. +The data from column +.I col +in the current row of +.I tableorlist +are passed to +.IR cmd . +This passes the contents of +.I col +as an argument to +.IR command . +The output of +.I command +then replaces this entry within the form. +.PP +If +.B report +does not recognize a column name, it prints +.I "" +as if it were normal text. +This allows you to use the angle brackets as literal characters within your +form, if you wish. +It also helps you debug your form: +if the data from the table are not replacing +.IR "" , +then +.I "column-name" +does not exactly match the column name in the table. +Check for spelling errors, upper- and lower-case discrepancies, +embedded blanks, and nonprinting characters. +.SH Example +Consider table +.BR maillist , +as follows: +.DM +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.DE +.DM +Name Chiquita Banana +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 +.DE +.PP +You could use an editor to develop a form like the following: +.DM + + + +< City >, +.DE +.DM +Hi < Name >: +.sp \n(pDu +The date and time is +.sp \n(pDu +We are also sending this letter to: +.sp \n(pDu +"' | justify !> +Bye !> +.DE +.PP +Note that we have used all three types of inserts: +column name, command, and command insert. +Now with these two files ready, you only need type +.DM + report form < maillist +.DE +.PP +to see the following output: +.B1 +Ronald McDonald +McDonald's +123 Mac Attack +Memphis, TENN 30000 +.sp +Hi Ronald McDonald: +.sp \n(pDu +The date and time is Wed Aug 19 01:58:19 PST 1983 +.sp \n(pDu +We are also sending this letter to: +.sp +Name City +--------------- ------- +Chiquita Banana San Jose +.sp \n(pDu +Bye Ronald McDonald +.B2 +.PP +.B1 +Chiquita Banana +United Brands +Uno Avenido de la Reforma +San Jose, Costa Rica 123456789 +.sp \n(pDu +Hi Chiquita Banana: +.sp \n(pDu +The date and time is Wed Aug 19 01:58:40 PST 1983 +.sp \n(pDu +We are also sending this letter to: +.sp \n(pDu +Name City +--------------- ------- +Ronald McDonald Memphis +.sp \n(pDu +Bye Chiquita Banana +.B2 +.PP +Some users have noted that the shell escape places an +extra line in the output. +If you don't want the extra line say something like: +.DM + +.DE +.SH "See Also" +.B +label, +letter +.R +.br +COHERENT Lexicon: +.B +echo +.R diff --git a/man/reportwriter.man b/man/reportwriter.man new file mode 100644 index 0000000..8c4a22d --- /dev/null +++ b/man/reportwriter.man @@ -0,0 +1,47 @@ +.TH reportwriter "" "" "/rdb Command" +.PC "Sample program to write standard reports" +.B reportwriter +.PP +The command +.B reportwriter +is a sample shell program that writes a sample standard report. +It uses the command +.B splittable +to divide a table into page-sized tables. +It shows several tricks for using COHERENT shell programming and +.B /rdb +tools to put together reports. +.SH Example +Here is what +.B reportwriter +produces (with the middle of each page +replaced with an ellipsis to keep the listing short): +.B1 + Prices of Computers that ran UNIX(TM) in 1983 + From Urban Software of New York City + Report Date: 9 Feb 1985 +.sp +Price Company City +----- ------- ---- + Advanced Micro Devices Santa Clara + Alcyon Corporation San Diego + American Telephone & Telegraph + BASIS Microcomputer GmbH D-4400 Muenster + Corvus Systems San Jose + David Computers Inc. Kitchener + Digital Computers Ltd. Tokyo 102 + Heurikon Corp. Madison + ... + Western Digital Irvine + Western Electric Corp. + 4.2 Venturcom/IBM PC Cambridge + ... + 13.9 Victory Computer Systems, Inc. San Jose +.sp + Page 1 +.B2 +.SH "See Also" +.B +splittable +.R +.SO reportwriter diff --git a/man/rmblank.man b/man/rmblank.man new file mode 100644 index 0000000..89ec261 --- /dev/null +++ b/man/rmblank.man @@ -0,0 +1,111 @@ +.TH rmblank "" "" "/rdb Command" +.PC "Remove blank rows from a table" +\fBrmblank < \fItable\fR +.PP +.II "row^delete blank" +.II "blank row^delete" +.II delete +.II replace +The command +.B rmblank +deletes from +.I table +all input rows that consist of only white space (spaces and tabs). +The related programs +.B delete +and +.B replace +blank out records. +They can be used in transaction processing where the files +are too large to process and several users are updating them. +At a quiet time, the files can be compacted and reindexed. +.PP +Be sure to reindex any file that is processed by either +.B rmblank +or +.B compress. +.SH Example +First we have table +.BR inventory , +as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +Then we delete record 4 +.DM + echo 4 | delete inventory Item +.DE +.PP +and +.B inventory +now appears as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +The command +.B see +shows the contents of +.B inventory +actually to be as follows: +.DM +Item^IAmount^ICost^IValue^IDescription $ +----^I------^I----^I-----^I--------------$ + 1^I 3^I 50^I 150^Irubber gloves$ + 2^I 100^I 5^I 500^Itest tubes$ + 3^I 5^I 80^I 400^Iclamps$ + ^I ^I ^I ^I $ + 5^I 99^I 24^I 2376^Icleaning cloth$ + 6^I 89^I 147^I13083^Ibunsen burners$ + 7^I 5^I 175^I 875^Iscales$ +.DE +.PP +Now the command +.DM + rmblank < inventory +.DE +.PP +removes lines that consist only of blanks. +After we run this command, +.B inventory +appears as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +Remember to reindex a file after you run +.B rmblank +on it. +.SH "See Also" +.B +compress, +delete, +index, +replace, +rmblank +.R +.SO rmblank diff --git a/man/rmcore.man b/man/rmcore.man new file mode 100644 index 0000000..b95b895 --- /dev/null +++ b/man/rmcore.man @@ -0,0 +1,28 @@ +.TH rmcore "" "" "/rdb Command" +.PC "Remove all core files" +.B rmcore +.PP +.II core^remove" +.II "remove core files" +The command +.B rmcore +uses the COHERENT system's command +.B find +to search for files named +.B core +in the current directory and all subordinate directories. +As its name implies, it removes all such files that it finds. +.PP +Run this command first when you begin to fill up your disk. +.B rmcore +starts at the directory you are in and goes down the file tree. +If you are superuser, you can issue this +command at the root directory `/' and clean the whole system. +.SH "See Also" +COHERENT Lexicon: +.B +core, +find, +rm +.R +.SO rmcore diff --git a/man/row.man b/man/row.man new file mode 100644 index 0000000..cdb6950 --- /dev/null +++ b/man/row.man @@ -0,0 +1,189 @@ +.TH row "" "" "/rdb Command" +.PC "Make a new table where rows match logical condition" +\fBrow '\fIcolumn condition column-or-value ..\fB' < \fItable\fR +.PP +.II "table^split" +.II "table^create new" +.II "row^select via expression" +The command +.B row +creates a new table from +.IR table . +The new table +consists of only those rows that meet +.IR condition . +.PP +.B row +uses a script written in the +.B awk +language, which gives you great power for specifying the logical condition. +.PP +Several conditional expressions can be combined with the +logical AND (&&) and OR (||) operators. +Because the logical conditions are represented by special characters, +they must be enclosed by apostrophes, to protect them from being +interpreted by the shell. +If you have special characters within a column name, +enclose the column name in quotation marks. +.SH Example +First a simple example. +Consider table +.B inventory : +.DM +Item Onhand Cost Value Description +---- ------ ---- ----- ----------- +1 3 5 15 rubber gloves +2 100 1 100 test tubes +3 5 8 40 clamps +4 23 2 51 plates +5 99 2 97 cleaning cloth +6 89 18 1602 bunsen burners +7 5 175 875 scales +.DE +.PP +To make a table of all of the parts that cost more that $5, type: +.DM + row 'Cost > 5' < inventory +.DE +.PP +The new table appears as follows: +.DM +Item Onhand Cost Value Description +---- ------ ---- ----- ----------- +3 5 8 40 clamps +6 89 18 1602 bunsen burners +7 5 175 875 scales +.DE +.PP +Now a more complex example. +If you want a table of all the burners +and of all of the items of which we have fewer than ten in stock, type: +.DM + row 'Description ~ /burner/ || Onhand < 10' < inventory +.DE +.PP +The new table appears as follows: +.DM +Item Onhand Cost Value Description +---- ------ ---- ----- ----------- +1 3 5 15 rubber gloves +3 5 8 40 clamps +6 89 18 1602 bunsen burners +7 5 175 875 scales +.DE +.PP +The string +.B "~ /burner/" +searches for the string ``burner'' anywhere in the column. +The double pipe (||) means logical OR. +.SH "Column Names" +The commands +.BR row , +.BR compute , +and +.B validate +recognize column names as upper- and lower-case letters, +digits, and the underscore `_'. +The regular expression is [A-Za-z0-9_]. +If you have any other characters in your column names, put +apostrophes or quotation marks around them. +.PP +.B row +compares the recognized strings with the table's head line. +Only if it finds a match does it convert the string +to the \fB$1, $2...\fR column positions that +.B awk +needs. +.SH "awk's Reserved Words" +.B awk +is the pattern-scanning language that does all of the work for the +commands +.BR row , +.BR compute , +and +.BR validate . +.PP +.B awk +recognizes the following words as having special significance. +If you name a columns with any of these reserved words +and then try to use them in a query, the +.B /rdb +commands that are built around +.B awk +turn them into column positions and +.B awk +does not see them. +It is best to avoid these names as column heads or simply +capitalize the first letter of each of your column names. +.PP +This list also indicates the many functions you have with +.BR awk . +However, do not rely on the following descriptions. +The tutorial section of +your COHERENT manual contains a summary of the +.B awk +language. +.PP +Please note that this list of +.B awk +functions and reserved words comes from UNIX System V. +Other implementations of +.B awk +may not contain all of these functions. +.DM +.ta 1.5i +\fIReserved Word Description\fR +.tc . +\fBBEGIN\fR Pattern that matches before first input record +\fBEND\fR Pattern that matches after last input record +\fBbreak\fR Exit the nearest \fBfor\fR or \fBwhile\fR loop +\fBcontinue\fR Go to next iteration of \fBfor\fR or \fBwhile\fR loop +\fBelse\fR Used in \fBif then else\fR expression +\fBexit\fR Leave program entirely, as if at end of input +\fBexp\fR Raise number to a power +\fBfor\fR \fBfor (\fIexpression\fB ; \fIcondition\fB; \fIexpression\fB)\fR +\fBgetline\fR Get next input line +\fBif\fR \fBif (\fIcondition\fB) \fIstatement \fB[else \fIstatement\fB]\fR +\fBin\fR \fBfor (\fIvariable \fBin \fIarray\fB) \fIstatement\fR +\fBindex\fR \fBindex ( \fIstring1\fB, \fIstring2 \fB)\fR +\fBint\fR Truncate argument to integer +\fBlength\fR Return current line length, or length of argument +\fBlog\fR Return log (to base 2) of argument +\fBnext\fR Skip to next record and reexecute all commands +\fBprint\fR Output variables +\fBprintf\fR \fBprintf ("\fIformat\fB", \fIvariable\fB, ... )\fR +\fBsplit\fR \fBsplit (\fIstring\fB, \fIarrayname\fB, \fIseparator\fB)\fR +\fBsprintf\fR \fBsprintf ("\fIformat\fB", \fIvariable, ... \fB)\fR +\fBsqrt\fR Return square root of argument +\fBsubstr\fR \fBsubstr ( \fIstring\fB, \fIstart\fB, \fInumber \fB)\fR +\fBwhile\fR \fBwhile ( \fIcondition\fB ) \fIstatement\fR +.tc +.DE +.PP +Although +.B row +is built around the +.B awk +language, +.B row +has the advantage of knowing about the names of the columns. +Therefore, you can use column names instead +of column positional numbers. +To grasp the full power of +.B row +and +.BR compute , +read the COHERENT manual's tutorial on +.BR awk . +.SH "See Also" +.B +compute, +row, +select, +validate +.R +.br +COHERENT Tutorials: +.B +Introduction to the awk Language +.R diff --git a/man/sale.man b/man/sale.man new file mode 100644 index 0000000..a914e4a --- /dev/null +++ b/man/sale.man @@ -0,0 +1,115 @@ +.TH sale "" "" "/act/sales Command" +.PC "Enter sale order, and item, update customer" +.B sale +.PP +.II "sales order" +The command +.B sale +is part of the accounting/sales system included with +.BR /rdb . +It enters a sales order into table +.B saleorder +and items into table +.BR saleitem . +It also permits an order-taker to add a new customer to +the customer-information table +.BR customer , +or to update information on an existing customer. +.SH Example +.B sale +is interactive, to allow you to confirm each action. +First, it asks for customer number. +If you give it a number, it looks the number up in table +.BR customer . +If it finds a customer with that number, it displays information +on that customer for you to confirm. +If you do not have the customer's number, you can search with +the slash string option or enter the question mark to enter +.BR customer . +There, you can search for the customer or add a new customer. +.PP +The following gives an example order-entry session: +.DM +MakeaPile, Inc. saleorder +Enter Customer Number (? for new, /string search, Return to exit) \fB1\fP +Vendor Number 1 +Mr. Luke Skywalker CEO +.sp \n(pDu +Rebel Enterprises +123 Lea Street +Space Port City, Far Side 123456789 Tatooy +.DE +.DM +Code Qty +------- ------- +\fBrdb 1\fP +Order Number Code Backord Qty Price Total Name +------- ------- ------- ------- ------- ------- ------- ------- +1 1 rdb 10 1 1500 1500.00 /rdb +Number Cust Date Shipped Gross Tax Total +------ ---- ------- ------- ------- ------- ------- +1 2 850723 0 1500.00 75.00 1575.00 +.DE +.DM +Is this correct? (y, n) \fBy\fP +.DE +.PP +After you have confirmed the order, table +.B sale +modifies table +.BR salesorder , +as follows: +.DM +Number Cust Date Shipped Gross Tax Total +------ ---- ------- ------- ------- ------- ------- +1 2 850723 860525 1500.00 75.00 1575.00 +2 2 850724 860525 3000.00 150.00 3150.00 +3 3 850902 860525 22500.00 1125.00 23625.00 +4 3 850902 0 36000.00 1800.00 37800.00 +5 3 850902 0 21000.00 1050.00 22050.00 +.DE +.PP +It also modifies table +.BR salesitem , +as follows: +.DM +Order Number Code Backord Qty Price Total Name +------- ------- ------- ------- ------- ------- ------- ---- +1 1 rdb 10 1 1500 1500.00 /rdb +2 1 rdb 10 2 1500 3000.00 /rdb +3 1 rdb 10 5 1500 7500.00 /rdb +3 2 act 10 10 1500 15000.00 /act +4 1 rdb 10 5 1500 7500.00 /rdb +4 2 rdb 10 19 1500 28500.00 /rdb +5 1 rdb 10 5 1500 7500.00 /rdb +5 2 act 10 9 1500 13500.00 /act +.DE +.PP +Once you enter the correct customer number, +.B sale +gives you a table of items to enter. +Type in +.B Code +and +.BR Qty , +and the program looks up the other information in the inventory. +Verify that the information is correct and make sure +that you have enough in inventory to satisfy the order. +Continue entering items until you are through. +Pressing \fB\fR indicates that you are finished; +.B sale +then calculates the totals for the order, including tax. +.PP +This program uses the commands +.B cursor +and +.B termput +to maneuver about the screen. +.SH "See Also" +.B +cursor, +invoice, +postar, +termput +.R +.SO sale diff --git a/man/schema.man b/man/schema.man new file mode 100644 index 0000000..b42bcf0 --- /dev/null +++ b/man/schema.man @@ -0,0 +1,58 @@ +.TH schema "" "" "/rdb Command" +.PC "Print a table's schema" +\fBschema \fItableorlist ...\fR +.PP +.II "table^print schema" +.II "data base^print schema" +The command +.B schema +prints the schema, or a data-base dictionary, for each +.IR tableorlist . +.PP +Other data-base systems require that you define a schema +prior to creating a table. +In +.BR /rdb , +you can derive a schema from an existing table or tables. +.SH Example +The following command combines the schemas for tables +.B inventory +and +.BR maillist . +The example pipes the concatenated schema through the command +.B justify +to ``pretty up'' the output: +.DM + schema inventory maillist | justify +.DE +.PP +This produces the following: +.DM +Table Field Name +--------- ----- ----------- +inventory 1 Item +inventory 2 Amount +inventory 3 Cost +inventory 4 Value +inventory 5 Description +maillist 1 Number +maillist 2 Name +maillist 3 Company +maillist 4 Street +maillist 5 City +maillist 6 State +maillist 7 ZIP +maillist 8 Phone +.DE +.PP +Because +.B schema +is a shell script, you can modify it to give widths of columns, +data types, and so on. +.SH "See Also" +.B +datatype, +precision, +width +.R +.SO schema diff --git a/man/screen.man b/man/screen.man new file mode 100644 index 0000000..6bf1a03 --- /dev/null +++ b/man/screen.man @@ -0,0 +1,190 @@ +.TH screen "" "" "/rdb Command" +.PC "Convert a form into a screen-input shell program" +\fBscreen < \fIform\fB > \fIshellprogram\fR +.PP +.II "screen^make input form" +The command +.B screen +reads +.I form +and writes a COHERENT shell program that paints the screen and reads +a user's input. +Because the output of +.B screen +is a shell script, you can edit it to validate data, +read data and write it onto the screen, and so on. +.I form +must be in the same format as described in the manual entry for the command +.BR report . +.PP +.B screen +writes a simple shell script. +The script works ``as is'' to read data +typed onto the screen and to write a standard table. +It uses the +.B /rdb +command +.B cursor +to move to each input field on the screen. +.PP +A script output by +.B screen +can be used in many ways. +You can use it as a form, to enter data into a new table. +You can edit the script to have it append its output into an +existing table, +to pass its data via a pipe to another command, or to read +data from a table and display them on the screen. +Commands can be edited into the screen-form script. +You can add calls to the +.B /rdb +command +.BR validate , +to confirm that the user has added data that are at least sensible. +You can even embed the script within another shell script, to perform +screen entry for it. +.SH "Setup Steps" +To create a screen-entry form, do the following: +.IP \fB1.\fR +Use your text editor to write the screen-form file. +.IP \fB2.\fR +Run +.B screen +to read the form file. +Redirect its output into the file in which you wish to store the form's +executable script. +.IP \fB3.\fR +Edit the shell script to meet your requirements, +such as adding validation, record locking, and so forth. +.IP \fB4.\fR +Run the executable shell script to input data and write a new table. +.SH Example +A simple form looks like this: +.DM + Mail List Entry Form + +.sp \n(pDu +Name : Company: +Street: +City : State : ZIP: +Phone : +.DE +.PP +Everything is written to the screen except what is between angle brackets +``<>'', which are variables to be input. +Exclamation marks within angle brackets (e.g., \fB\fR) +mark commands to execute when the program is running. +.PP +When +.B screen +reads the above form and compiles it, it writes the following shell script: +.DM +: paint crt screen +exec 1>/dev/tty +cat <&2 +echo "Name Company Street City State ZIP Phone" +echo "---- ------- ------ ---- ----- --- -----" +: append row +echo "$Name $Company $Street $City $State $ZIP $Phone" +.DE +.PP +This shell progam is in three parts, each introduced by a comment: +.IP "\fBpaint crt screen\fR" +This section clears the screen, then paints the form onto it. +.IP "\fBread user input\fR" +This section +moves the cursor to each input field, then waits for +the user to enter a string of characters. +Each field is linked to a column; that is, what the user types in that field +will be written into the column to which the field is linked. +.IP "\fBoutput table head\fR" +The final section writes the new table in standard +.B /rdb +format; that is, a column header followed by the row of data entered by the +user. +.PP +To let you use the screen program in a pipe, +the generated commands send the interactive output to the screen through the +standard output temporarily tied to +.BR /dev/tty , +and to the output table through the standard-error device. +To accomodate COHERENT's implementation of +.BR exec , +you must tie the standard error to the standard output +when you run the script screen has created. +For example +.DM + mail.s > mail.t 2>&1 +.DE +.PP +The following shows the form compiled above, with example data: +.B1 + Mail List Entry Form + Wed Sep 4 14:09:30 EDT 1987 +.sp +Name : Hi Ho Company: +Street: 1st St. +City : LA State : CA ZIP: 90024 +Phone : 213-555-1212 +.B2 +.PP +The above input creates the following table: +.PP +.DM +Name Company Street City State ZIP Phone +---- ------- ------ ---- ----- --- ----- +Hi Ho HH Inc. 1st St. LA CA 90024 213-555-1212 +.DE +.PP +You can redirect output into a file or pipe. +Here, it was directed into table +.BR mail.t . +You can use this table as its stands alone, +or you can append it to another table by by piping it to the +.B append +command. +For example: +.DM + mail.s 2>&1 | append mail.t +.DE +.SH "See Also" +.B +cursor, +lock, +unlock, +validate +.R +.br +COHERENT Lexicon: +.B +cat, +echo, +exec, +ksh, +read, +sh +.R diff --git a/man/search.man b/man/search.man new file mode 100644 index 0000000..14de8b1 --- /dev/null +++ b/man/search.man @@ -0,0 +1,426 @@ +.TH search "" "" "/rdb Command" +.PC "Search a table" +\fBsearch [-m[bhirs]] [\-hsnx +x] [\-l [2> \fIlocation\fB]] \fItableorlist \fB[\fIkeycolumn ...\fB] [ < \fIkeytable\fB ]\fR +.PP +.II "row^find quickly" +The command +.B search +finds a row quickly. +It searches +.I tableorlist +for each row in which the value +.I keycolumn +matches one or keys you supply. +The key or keys can either be entered interactively, +or read from +.IR keytable . +You must use the +.B /rdb +command +.B index +to index +.I tableorlist +before you can use +.B search +to search it. +.PP +By default, a key's value must match exactly the string within +.IR keycolumn . +The options +.B \-x +and +.B +x +modify this behavior; they are described below. +.PP +You can use +.B search +in any of several different ways. +Because it reads the key values from the standard input, +you can input the keys by typing them interactively at the terminal, +by diverting the contents of +.I keytable +into it, +or by piping the output of a command into it. +Each method is demonstrated in the examples given below. +Therefore, it is easy to use +.B search +on a command line or in a shell script. +For example, the command +.B domain +is a shell script that uses +.B search +to speed its searches. +If you use a pipe to pass data to +.BR search , +think of a stream of keys going into it +and a stream of matching rows coming out. +.PP +.B search +can use a number of different methods of searching. +There are advantages and disadvantages to each; +different sizes and structures of tables are best suited to different +methods of searching. +If you are not sure which method to use, you can try them all +on your +.IR tableorlist . +Use the COHERENT command +.B time +to find how long each method takes, then pick the one that +runs the fastest. +Another +.B /rdb +command, +.BR timesearch , +runs each form of +.B search +on the sample +.BR unixtable , +which is kept in directory +.BR $RDB/demo . +Because +.B timesearch +is a shell script, you can modify it to time the different methods +for your own file. +.PP +The behavior of +.B search +depends upon which method of searching you choose. +With some methods, it first searches +another table (called the ``secondary index file'') +built by the command +.BR index . +One method, sequential, searches every row in +.IR tableorlist . +.PP +If you change +.IR tableorlist , +such as when you add a new row or delete an old one, you must reindex it. +If you develop application programs, you can have them update +secondary index files to handle dynamic file-processing. +.SH "Methods of Searching" +The following describes the different methods of searching a table. +Each method is introduced by the command-line option with which +you can invoke it: +.IP \fB\-m\fR +.II "search^sequential +Perform a sequential search. +This is the default method. +.IP \fB\-mb\fR +.II search^binary +Binary search. +This first looks at the middle record of the file. +If the key column's value is greater than that of the key that +.B search +is seeking, then +.B search +look at the record one quarter of the way through the file. +.B search +continues to bifurcate the table until it finds the row, or finds +that the row is not there. +.sp \n(pDu +The method of searching +requires that you first sort the table on the key column. +.sp \n(pDu +A binary search takes log(2)\fIn\fR accesses: +.DS +.ta 1.0iC 2.5iC + \fIAccesses No. of Records\fR + 10 1 thousand + 20 1 million + 30 1 billion + 40 1 trillion +.DE +.sp \n(pDu +Binary searching is a good method for searching tables +that must remain sorted on the key column. +.IP \fB\-mh\fR +.II search^hash +Hash searching. +A hash search computes an address from the key and +looks into a secondary file at that address. +If the key matches, +it retrieves the address of the row you want in the main file and then +retrieves it. +If the address +.B search +hashes to is empty (i.e., has a value of zero), +then the key does not match and the row is not in the file. +.sp \n(pDu +Occasionally, another key is there that happens to hash +to the same address; +this is called a +.IR collision . +When a collision occurs, +.B search +will keep looking at +subsequent entries in the secondary table until +it either finds a match or finds an empty or deleted (\-1) entry. +.sp \n(pDu +A hash search usually takes two disk accesses when the +hash table is half full. +For this reason, +.B /rdb +sets up hash tables that are twice as large as the number +of records being hashed. +As the data table grows, it must be reindexed to keep the hash speed up. +.sp \n(pDu +Hashing is a good method for tables that are static or change only rarely. +.IP \fB\-mi\fR +.II search^inverted +.II search^indexed +Inverted or indexed searching. +An inverted search uses a secondary file that must have been created by +.BR index . +This index table has two columns: +the key column from the data table, sorted to allow binary searching; +and the offset of each key-column value within the data table. +When +.B search +finds the key in the index table's key column, it then uses value from +the address column to retrieve the row from the data table +in a single disk read. +The inverted method takes a little longer than binary, +but sorting the index file is much faster than sorting the data table. +.IP \fB\-mr\fR +.II search^record +Record number searching. +A record search simply retrieves the row requested. +For example, if you enter `5', you will get the fifth row in the data table. +.sp \n(pDu +To retrieve a record by its row number, +.B search +reads a secondary file that +that contains the offset for each record in the data table. +The record method is a faster way to retrieve a record; +however, you must know the number of the record you want, and you must +either keep the data table in that order, or reindex it. +You can use the command +.B replace +to move a row to the of its table and update the secondary +index to point to the row's new location. +.IP \fB\-ms\fR +.II search^sequential +.II search^linear +Sequential or linear searching. +A sequential search examines all records in +the data table in the order in which they appear. +It is not a fast-access method, although it may +still be faster than that used by the command +.BR row . +It is provided as a benchmark for timing other methods. +If your file is not big enough to +make the other access methods worthwhile, just use sequential +.B search +or +.B row +and save yourself the overhead of building and maintaining an index. +.sp \n(pDu +A sequential search can search multiple key columns, and and can search +any column. +.II grep +The sequential method is better than the COHERENT command +.B grep +because it looks at one column, whereas +.B grep +scans the entire row for a pattern match. +.SH "Other Options" +.B search +also recognizes the following command-line options: +.IP \fB\-h\fR +Do not print the table's head line. +By default, +.B search +prints the head line of the table it searches as well as all rows it finds. +The \fB\-h\fR option suppresses the printing of the head line +and prints only the rows you are seeking. +.IP \fB\-l\fR +Output the location of the record in the data table +and the location of the index record in the index table. +.II replace +.II lock +.II unlock +These locations is in the form +.IR from to xfrom xto" , +and can be piped to the commands +.BR replace , +.BR lock , +and +.BR unlock . +.B search +writes these data to the standard-error device, +to separate them from the table it writes to the standard output. +.sp \n(pDu +Because +.B search +is a shell program, it demonstrates how you can do this in your own programs. +Turning on this option also turns on the +.B \-s +option because +.B /rdb +allows you to lock or replace one record at a time. +Therefore, all keys in your file must be unique, or +some will never be found. +.IP \fB\-s\fR +Search for only one row. +This speeds the search because +.B search +quits when it finds the first match and continue searching the file. +.IP \fB\-x\fR +Partial match and case insensitive, like the Berkeley command \fBlook\fR. +.IP \fB+x\fR +Exact match, including blank characters. +The default matching ignores blank characters. +.SH Examples +Assume you have a file called +.BR inventory , +which appears as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- +1 3 50 150 rubber gloves +2 100 5 500 test tubes +3 5 80 400 clamps +4 23 19 437 plates +5 99 24 2376 cleaning cloth +6 89 147 13083 bunsen burners +7 5 175 875 scales +.DE +.PP +If you had indexed the column +.BR Description , +then you have several ways to find rows: +.IP \fB1.\fR 0.3i +Interactively type key values. +.B search +immediately prints the rows it finds. +For example, type: +.DM +search -mh inventory Description +.DE +.sp \n(pDu +.B search +responds with the head line of the inventory table. +Now you can type names, in this example +.BR scales ; +if +.B search +finds a match, it returns the row that contains the key. +.IP \fB2.\fR +Divert a table or list file to +.BR search . +You can create a table with a column of key values; for example: +.DM +Description +----------- +scales +clamps +.DE +.sp \n(pDu +The following command line diverts this table into +.B search +.DM +search -mh inventory Description < description +.DE +.sp \n(pDu +and produces the following output: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- +7 5 175 875 scales +3 5 80 400 clamps +.DE +.IP \fB3.\fR +Input keys through a COHERENT pipe. +For example, the command +.DM +column Description < sometable | search -mh inventory | column Value +.DE +.sp \n(pDu +returns the following: +.DM +Value +----- + 875 + 400 +.DE +.sp \n(pDu +If you wish to justify a table like this, just pipe the output of +.B search +to the command +.BR justify . +.sp \n(pDu +This lets you use the COHERENT command +.B echo +to pipe a key value: +.DM +echo scales | search -mh inventory Description +.DE +.sp \n(pDu +which returns the following to the standard output: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- +7 5 175 875 scales +.DE +.PP +In shell programming, you can capture the output of a program +or a pipe by putting it within two grave (`) symbols. +In a shell script, you can set an environmental variable like this: +.DM +RECORD=`echo key | search -mh -h bigtable column` +.DE +.PP +The command +.B "echo $RECORD" +displays the data diverted into this variable; for example: +.DM + 7 5 175 875 scales +.DE +.PP +Now the +.B RECORD +environmental variable is equal to the matching row in the table. +Note the +.B \-h +is important to keep the head line from being written into +.BR RECORD . +.SH "Search vs. Select" +.B search +is important for application programs written in +shell scripts, the C programming language, or other code. +It can be used interactively, but the command +.B row +is much more powerful because it can seek combinations of logical conditions, +regular expressions, and conditions such as +.I "greater than," +whereas +.B search +needs a key (either exact or partial). +But for pulling a row out of a big table, +.B search +can speed things up enough to be preferred. +In summary, +.B search +is for high-speed access with keys, whereas +.B row +should be used to retrieve rows that match complex logical conditions +and regular expressions. +.SH "See Also" +.B +append, +column, +delete, +index, +replace, +row, +seek, +update +.R +.br +COHERENT Lexicon: +.B +egrep, +grep, +look, +time +.R diff --git a/man/searchtree.man b/man/searchtree.man new file mode 100644 index 0000000..6186a56 --- /dev/null +++ b/man/searchtree.man @@ -0,0 +1,165 @@ +.TH searchtree "" "" "/rdb Command" +.PC "Seek a string node in tree table" +\fBsearchtree [-m[bhirs]] \fItable root goal column1 column2\fR +.PP +.II "directory^search" +.II "tree^search" +.II PROLOG +The command +.B searchtree +searches +.I table +to see if string +.I root +in +.I column1 +is associated with +.I goal +in +.IR column2 . +It returns +TRUE to the shell if it find that +.I root +is associated with +.IR goal , +or FALSE if it is not. +.PP +.I goal +may be within +.IR table , +in which case the COHERENT command +.B grep +can also find it; +.BR searchtree , +however, will also see if +.I goal +be reached by a path from +.IR root . +.PP +The options +.B \-m[bhirs] +name the fast-access method to use. +The default is sequential (\fB\-ms\fR). +See the entry for the command +.B search +for a full description of each method of searching. +.PP +.B searchtree +is a breadth-first search that uses the +.B search +fast-access methods to find all of the children at each level of a tree. +This is the kind of search often done in artificial intelligence programs. +However, +.B searchtree +stores its data in a temporary file rather than in memory, +so that it will work on very large data bases. +.PP +Because +.B searchtree +is a shell script, you can modify it to serve many kinds of searches. +For example, you can add a message like +.I yes/no +or +.IR true/false . +You can also add shell code to print out a path. +You might want to use heuristics to guide the search at each +level, and side effects to update data or print messages along the way. +.PP +This kind of searching is also used by the PROLOG language. +.BR searchtree , +however, differs from PROLOG in two significant respects. +First, PROLOG must have its data base in memory, which +limits the size of the database it can handle. +Second, PROLOG uses depth-first searching, which requires it to backtrack. +.SH Example +Consider the tree represented by table +.BR isa , +as follows: +.DM +Name Isa +---- --- +barbara human +bill human +evan human +rod human +human primate +primate mammal +cat mammal +dog mammal +mammal animal +bird animal +fish animal +animal lifeform +plant lifeform +.DE +.PP +Remember that all trees, in fact all graphs, can be listed +as tables when the nodes (vertices) +that are connected by a line (arcs) that is on each row. +A directional graph is handled by having one column be +.B From +and another be +.BR To . +.PP +The following command uses +.B searchtree +to examine table +.B isa +to see if entity +.B rod +in column +.B Name +is of type +.B lifeform +as given in column +.BR Isa : +.DM + searchtree isa rod lifeform Name Isa ; echo $? +.DE +.PP +The command +.B "echo $?" +prints the value returned by +.BR searchtree . +In this case, you see `0' (or FALSE), because +.B rod +is not a +.BR lifeform ; +rather it (or he) is a +.BR human . +.PP +Because +.B searchtree +returns only a status code, you can use it as a test condition. +Your shell program could have a statement like this: +.DM + prompt "What is your name: " + read NAME + if searchtree isa $NAME lifeform Name Isa + then + echo "So, I am talking to a lifeform." + else + echo "So, I am talking to another machine." + fi +.DE +.PP +The following gives an sample exchange with this script; text in \fBbold\fR +gives what the user types: +.DM + What is your name: \fBrod\fP + So, I am talking to a lifeform. +.DE +.PP +The result of the search of the tree was tested by the +.B if +statement, and used to decide which message to print. +.SH "See Also" +.B +search +.R +.SH Notes +.II COHware +If you wish to experiment with PROLOG, volume 2 of +.B COHware +includes the source code to a PROLOG interpreter. +.SO searchtree diff --git a/man/see.man b/man/see.man new file mode 100644 index 0000000..0cb15bf --- /dev/null +++ b/man/see.man @@ -0,0 +1,68 @@ +.TH see "" "" "/rdb Command" +.PC "Display nonprinting as well as printing characters" +\fBsee < \fItableorlist\fR +.PP +.II "file^display contents" +The command +.B see +displays the contents of +.IR tableorlist . +Unlike the COHERENT command +.BR cat , +.B see +represents all nonprinting characters by converting +them to the form +\fB^\fIsome-printing-character\fR. +It is essentially the same as the Berkeley UNIX commands +.B see +or +.BR "cat -v" . +.PP +.B see +is very useful for seeing if you have placed tabs into your tables or lists. +Tabs print out as +.B ^I +because they are also +obtained by typing \fB\fR. +.B see +is also helpful for finding nonprinting characters. +.SH Example +To see the if the tabs are placed correctly in table +.BR badtable , +type: +.DM + see < badtable +.DE +.PP +You'll see something like: +.DM +Date^IAccount^IDebit^ICredit^IDescription$ +------^I-------^I-------^I-------^I--------------------------$ +820102^I101^I25000^I^Icash from loan$ +820102^I211.1^I^I25000^Iloan number #378-14 Bank Amerigold$ +820103^I150.1 10000^I^Itest equipment from Zarkoff$ +820103^I101^I^I5000^Icash payment$ +820103^I211.2^I^I5000^Inote payable to Zarkoff Equipment$ +820104^I130^I30000^I^Iinventory - parts from CCPSC$ +820104^I201.1^I^I15000^Iaccounts payable to CCPSC$ +820104^I101^I^I15000^Icash payment to CCPSC for parts$ +.DE +.SH "See Also" +COHERENT Lexicon: +.B +ASCII +.R +.SH Notes +.B see +assumes ASCII characters with a value between 0 and 127. +.B see +adds 128 to negative characters and to characters with values greater +than 127. +It then adds the value of ASCII `A' to characters with a value less +than that of a blank character (ASCII 32) +so they map to the upper-case characters. +.PP +For example, NUL +is printed as \fB^@\fR, a tab character as \fB^I\fR, and a form-feed character +as \fB^L\fR. +.SO see diff --git a/man/seek.man b/man/seek.man new file mode 100644 index 0000000..cc9a1a2 --- /dev/null +++ b/man/seek.man @@ -0,0 +1,124 @@ +.TH seek "" "" "/rdb Command" +.PC "Return the beginning and ending offset of a row" +\fBseek [-m[bihrs]] [-o \fIoutfile\fB] \fItableorlist \fB[\fIkeycol...\fB] < \fIfile\fR +.PP +.II "row^find" +.II "row^offset" +.II "find row" +The command +.B seek +returns the offsets of the beginning and end of a record in +.IR tableorlist . +It uses the command +.B search +with its location option +.BR \-l . +.PP +The offset and size integers returned can be used by the +.B replace +to write a new record. +They are also used by the commands +.B lock +and +.B unlock +to set and remove record locks. +.SH Options +.B seek +recognizes the following options: +.IP \fB-m[bhirs]\fR +Fast access methods. +See the manual entry for the command +.B search +for a full description of various search options and how you can invoke each. +.IP \fB-o\fR +Write the found record into +.BR outfile . +.SH Example +Let's use +.B seek +on the table +.BR inventory , +which is as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +The following command invokes +.B seek +to find row 5: +.DM + echo 5 | seek -mb inventory Item +.DE +.PP +This writes the following to the standard output: +.DM + 207 245 0 8 +.DE +.PP +This means that the record is 207 bytes into the file and that +it is 38 bytes long, ending at byte 245. +.PP +The next example +extracts a row from +.BR inventory +blanks it out, then writes the row back into +.B inventory +on top of the original row. +First, extract the row: +.DM + LOCATION=`echo 5 | seek -mb -o tmp inventory Item` +.DE +.PP +The location of the selected record is stored in environmental variable +.BR LOCATION . +The option +.B "\-o tmp" +writes the record with key equal to 5 into table +.BR tmp , +which appears as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 5 99 24 2376 cleaning cloth +.DE +.PP +The next command blanks the contents of +.BR tmp , +then writes the blanked row back into +.B inventory +at the location stored earlier at +.BR LOCATION : +.DM + blank < tmp | replace inventory $LOCATION +.DE +.PP +.B inventory +now appears as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates +.sp \n(pDu + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.SH "See Also" +.B +index, +lock, +replace, +search, +unlock +.R +.SO seek diff --git a/man/select.man b/man/select.man new file mode 100644 index 0000000..a441b89 --- /dev/null +++ b/man/select.man @@ -0,0 +1,31 @@ +.TH select "" "" "/rdb Command" +.PC "Output selected rows" +\fBselect '\fIcolumn condition column-or-value\fB' < \fItable\fR +.PP +.II "row^select by expression" +The command +.B select +writes a new file that consists of the rows of +.I table +that match the expression +.I condition +in +.IR column . +.PP +.B select +is a link to the command +.BR row . +It is included for those users who prefer the name +.B select +(which is a relational theory name) to +.BR row . +.\"However, it conflicts with the Korn shell which also has a +.\".B select +.\"command. +.\"We strongly recommend you use +.\".B row +.\"instead. +.SH "See Also" +.B +row +.R diff --git a/man/sorttable.man b/man/sorttable.man new file mode 100644 index 0000000..ff59e3d --- /dev/null +++ b/man/sorttable.man @@ -0,0 +1,172 @@ +.TH sorttable "" "" "/rdb Command" +.PC "Sort a table by one or more columns" +\fBsorttable [\fIsort options\fB] [\fIcolumnname ...\fB] < \fItableorlist\fR +.PP +.II "table^sort" +The command +.B sorttable +uses the COHERENT command +.B sort +to sort +.IR tableorlist . +Unlike +.BR sort , +it knows about table head lines and column names. +.PP +With +.B sorttable , +you use the column names rather than column numbers, as in +.BR sort . +.B sorttable +also tells +.B sort +to use tabs as field separators, instead of white space +(i.e., blanks and tabs). +.PP +.B sorttable +completely sorts by the first column you name. +If there are any duplicate values in that column, it goes +to the second column and sort on those values. +It continues across the named columns until it either finds +a column that consists of unique values, or it runs out of columns. +For example, if you sort a ledger on columns +.B Account +and +.BR Date , +.B sorttable +sorts the entire table on +column +.BR Account , +and sorts by +.B Date +within each account. +.PP +If no columns are named, +.B sorttable +sorts +.I tableorlist +by all of its columns from left to right. +.SH Options +.B sorttable +passes all the options you give it to +.BR sort . +See the entry for +.B sort +in the COHERENT manual's Lexicon for details on this command's options. +.SH Example +Consider table +.BR inventory , +which is structured as follows: +.DM +Item# Amount Cost Value Description +------- ------- ------- ------- --------------- + 1 3 5.00 15.00 rubber gloves + 2 100 0.50 50.00 test tubes + 3 5 8.00 40.00 clamps + 4 23 1.98 45.54 plates + 5 99 2.45 242.55 cleaning cloth + 6 89 14.75 1312.75 bunsen burners + 7 5 175.00 875.00 scales +.DE +.PP +To see +.B inventory +sorted with the highest +.B Cost +items on top, type: +.DM + sorttable -n -r Cost < inventory +.DE +.PP +This produces: +.DM +Item# Amount Cost Value Description +------- ------- ------- ------- --------------- + 7 5 175.00 875.00 scales + 6 89 14.75 1312.75 bunsen burners + 3 5 8.00 40.00 clamps + 1 3 5.00 15.00 rubber gloves + 5 99 2.45 242.55 cleaning cloth + 4 23 1.98 45.54 plates + 2 100 0.50 50.00 test tubes +.DE +.PP +Note that +.B Cost +is in reverse order (the +.B \-r +option) and that it was treated as a number (\fB\-n\fR option) +instead of a character string. +.PP +If you use +.B sorttable +frequently, you should carefully study the +COHERENT Lexicon entry for +.BR sort . +For example, you can control which columns +.B sorttable +sort on. +If you name several columns, +.B sort +sees each column's numbers proceeded by a `+'. +The `+' means ``start sorting on this column'', the hyphen `-' means +``stop on the next column''. +To see this, use the +.B \-D +(debug) option for +.B sorttable. +(By the way, it is because +.B sort +has a +.B \-d +option that we use a capital +.B \-D +for the debug option for the +.B /rdb +commands). +For example, typing +.DM + sorttable -D Cost Description < inventory +.DE +.PP +yields: +.DM +Item Onhand Cost Value Description +------- ------- ------- ------- --------------- +sorttable: command executed: sort -t' ' +2 -3 +4 -5 +2 100 1 100 test tubes +4 23 2 51 plates +5 99 3 297 cleaning cloth +1 3 5 15 rubber gloves +3 5 8 40 clamps +6 89 18 1602 bunsen burners +7 5 175 875 scales +.DE +.PP +The line beginning with +.B sort +is the line passed to COHERENT. +Note the +.BR +1 , +.BR \-2 , +and so on. +That tells +.B sort +to sort on columns 2 and 4 (where the first column is 0). +Thus, +.B sorttable +correctly turns the column names you type into a +.B sort +command by looking up the column names in the table head line and +converting them to column numbers for the COHERENT command +.BR sort . +.PP +People are often confused by +.B sort +because it does not behave the way one would expect. +Study its entry in the COHERENT Lexicon. +.SH "See Also" +COHERENT Lexicon: +.B +sort +.R diff --git a/man/soundex.man b/man/soundex.man new file mode 100644 index 0000000..79582cb --- /dev/null +++ b/man/soundex.man @@ -0,0 +1,108 @@ +.TH soundex "" "" "/rdb Command" +.PC "Convert a name into soundex code" +\fBsoundex < \fIfile \fB> \fIfile\fB.x ; index -mb \fIfile.x\fB Soundex\fR +.PP +.II "file^convert to soundex" +.II "string^convert to soundex" +.II "Knuth, Donald" +The command +.B soundex +creates a secondary index to +.I file +based on Knuth's soundex code. +It is used by the +.B /rdb +command +.B like +to find a name that sounds like another name. +.PP +The output must be written into file +\fIfile\fB.x\fR; then run the command +.B index +on column +.B Soundex +within +.IB table .x +to ensure a high-speed search. +.SH Example +Consider table +.BR name , +which has many similar names in it: +.DM +Name +---- +Abraham +Abraham +Ackerman +Actor +Adams +Adams +Adams +Adams +\&... +.DE +.PP +Use +.B soundex +to create the secondary index file, as follows: +.DM + soundex < name > name.x ; index -mb name.x Soundex +.DE +.PP +The soundex'd index file appears as follows: +.DM +.ta 1.0i +Name Soundex +---- ------- +Avila A140 +Avila A140 +Avila A140 +Abraham A160 +Abraham A160 +Aissen A220 +Ajeska A220 +Augustine A223 +\&... +.DE +.PP +Now you can use the +.B like +command to find a name. +For example, the command +.DM + like Manis name +.DE +.PP +produces something like: +.DM +Name +---- +Mann +Mann +Means +Mink +Monahan +Moniz +Mooney +Mooney +Munoz +Munoz +.DE +.PP +Or the command +.DM + like Schaffer name +.DE +.PP +produces something like: +.DM +Name +---- +Schaeffer +Schaffer +Schiffrin +.DE +.SH "See Also" +.B +like +.R diff --git a/man/splittable.man b/man/splittable.man new file mode 100644 index 0000000..f491220 --- /dev/null +++ b/man/splittable.man @@ -0,0 +1,80 @@ +.TH splittable "" "" "/rdb Command" +.PC "Divide a table horizontally" +\fBsplittable [\-n] \fItable\fR +.PP +.II "table^split horizontally" +The command +.B splittable +uses the COHERENT command +.B split +to split +.I table +horizontally into several smaller tables, each with its own head line. +.I n +is the number of rows to write into each new table, +not counting the two head lines. +.PP +This command is most useful for report writing +because it can split tables into page-sized chunks. +.PP +Like +.BR split , +.B splittable +creates file names that end with \fBaa\fR, \fBab\fR, \fBac\fR, and so on. +This give 676 (26 x 26) possible output files. +.SH Example +Here is an example using a very short table, called +.BR inventory : +.DM +Item# Amount Cost Value Description +----- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +The command +.DM + splittable -4 inventory +.DE +.PP +splits +.B inventory +into two tables, one with the first four lines of +.B inventory +and the other with the last three. +Each new table also has the same head line as +.BR table . +.PP +The command +.DM + ls inventory* +.DE +.PP +shows us that the current directory now contains files: +.DM +inventory +inventoryaa +inventoryab +.DE +.PP +The two new tables are named, respectively, +.B inventoryaa +and +.BR inventoryab . +.SH "See Also" +COHERENT Lexicon: +.B +ls, +split +.R +.SH Notes +Because a COHERENT file can have no more than 14 characters in its name, +.B splittable +will not work correctly with tables whose names are longer than 12 characters. +.I "Caveat utilitor." +.SO splittable diff --git a/man/substitute.man b/man/substitute.man new file mode 100644 index 0000000..8e3688e --- /dev/null +++ b/man/substitute.man @@ -0,0 +1,65 @@ +.TH substitute "" "" "/rdb Command" +.PC "Replace old string with new string" +\fBsubstitute \fIoldstring newstring file ...\fR +.PP +.II "string^replace" +.II "replace string" +The command +.B substitute +replaces +.I oldstring +with +.I newstring +throughout each +.IR file . +Because it uses the COHERENT command +.BR sed , +be sure to protect with backslash `\e' all +.BR sed 's +special characters: +`*', `^', `$', and `\e'. +.SH Example +Again, consider table +.BR inventory , +as follows: +.DM +Item Amount Cost Value Description +----- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +To replace string +.B rubber +with +.B latex +throughout +.BR inventory , +type: +.DM + substitute 'rubber' 'latex' file +.DE +.PP +.B inventory +now appears as follows: +.DM +Item Amount Cost Value Description +----- ------ ---- ----- -------------- + 1 3 50 150 latex gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.SH "See Also" +COHERENT Lexicon: +.B +sed +.R diff --git a/man/subtotal.man b/man/subtotal.man new file mode 100644 index 0000000..38586bf --- /dev/null +++ b/man/subtotal.man @@ -0,0 +1,101 @@ +.TH subtotal "" "" "/rdb Command" +.PC "Output subtotals of columns in a table +\fBsubtotal [ \-l ] [ \fIbreak-column column ... \fB] < \fItable\fR +.PP +.II column^subtotal +.II "column^compute subtotal" +The command +.B subtotal +computes subtotals in each numeric +.I column +within +.IR table . +.PP +.B subtotal +uses +.I break-column +to mark where computation should begin and end: +as long as a value in +.I break-column +remains the same, the values in the other columns are accumulated. +When the value in the +.I break-column +changes, +.B subtotal +prints the value or values accumulated since the last break. +.PP +If no columns are specified on the command line, +.B splittable +uses the first, or leftmost, column of the table as the +break column, and computes subtotals for all other columns. +.PP +The option +.B \-l +prints whole table, not just the subtotals. +.SH Example +There are two forms of output. +One produces only the subtotals. +If you wanted to see the daily subtotals of table +.BR journal , +type: +.DM + subtotal Date Debit Credit < journal +.DE +.PP +This produces something like the following: +.DM +Date Account Debit Credit Description +------ ------- ----- ------ ---------------------------------- +891222 25000 25000 +891223 10000 10000 +891224 30000 30000 +.DE +.PP +Note that columns +.B Description +and +.B Account +are blank, because they contain non-numeric data. +.PP +The +.B \-l +prints the entire table, as well as the subtotals. +For example, the command +.DM + subtotal -l Date Debit Credit < journal +.DE +.PP +produces: +.DM +Date Account Debit Credit Description +------ ------- ----- ------ ---------------------------------- +891222 101 25000 cash from loan +891222 211.1 25000 loan number #378-14 Bank Amerigold +------ ------- ----- ------ ---------------------------------- +891222 25000 25000 +.DE +.DM +891223 150.1 10000 test equipment from Zarkoff +891223 101 5000 cash payment +891223 211.2 5000 note payable to Zarkoff Equipment +------ ------- ----- ------ ---------------------------------- +891223 10000 10000 +.DE +.DM +891224 130 30000 inventory - parts from CCPSC +891224 201.1 15000 accounts payable to CCPSC +891224 101 15000 cash payment to CCPSC for parts +------ ------- ----- ------ ---------------------------------- +891224 30000 30000 +.DE +.PP +This lets you see each row as well as the subtotals. +This could help you find out when you got out of balance. +.SH "See Also" +.B +compute, +maximum, +mean, +minimum +.R +.SO subtotal diff --git a/man/tableorlist.man b/man/tableorlist.man new file mode 100644 index 0000000..bf4c05d --- /dev/null +++ b/man/tableorlist.man @@ -0,0 +1,50 @@ +.TH tableorlist "" "" "/rdb Command" +.PC "Report whether a file has table or list format" +\fBtableorlist [\fItableorlist ...\fB] [< \fItableorlist\fB]\fR +.PP +.II "table^check if file is" +.II "list^check if file is" +The command +.B tableorlist +checks whether +.I tableorlist +is a table or a list. +If it is a list +(that is, its first character is a newline), then +.B tableorlist +prints the string ``list'' and returns status code 1 to the shell; +otherwise, it prints the string ``table'' and returns status code 0. +.SH Example +To check if +.B inventory +is a table or list, type: +.DM + tableorlist inventory +.DE +.PP +This prints +.DM + table +.DE +.PP +on the standard output. +To check what status value +.B tableorlist +returned, type: +.DM + echo $? +.DE +You'll see: +.DM + 0 +.DE +.SH "See Also" +.B +listtotable, +tabletolist +.R +.br +COHERENT Lexicon: +.B +file +.R diff --git a/man/tabletofact.man b/man/tabletofact.man new file mode 100644 index 0000000..dc92485 --- /dev/null +++ b/man/tabletofact.man @@ -0,0 +1,109 @@ +.TH tabletofact "" "" "/rdb Command" +.PC "Converts a table to PROLOG fact-file format" +\fBtabletofact \fItable ... \fB> \fIprologfactfile\fR +.PP +.II "table^convert to PROLOG facts" +.II "convert table to PROLOG facts" +.II "PROLOG^convert table to facts" +The command +.B tabletofact +converts each +.I table +into a file of facts in predicate calculus functor form, +which can be input to a PROLOG interpreter. +.SH Facts +PROLOG +facts are in the form +.IR relationship(subject,object) . +For example, the fact +.DM + parent(sally,fred) +.DE +.PP +means that ``sally is the parent of fred''. +.PP +PROLOG is a programming language whose name is a contraction of the phrase +.IR "programming in logic" . +Its input consists of +.IR facts , +.IR rules , +and +.IR questions . +If you build a data base of facts and rules, +you can ask questions and get answers. +If a question you ask does not have a +corresponding fact in the data base, +PROLOG looks through its rule file to see if other facts might +logically deduce the fact requested. +.SH Example +Suppose we have these simple table +.B parent +.DM +Parent Child +------- ------- +Sally Fred +Mike Fred +Fred Jane +.DE +.PP +the simple table +.B female +.DM +Name +---- +Jane +Sally +.DE +.PP +and the simple table +.BR male : +.DM +Name +---- +Fred +Mike +.DE +.PP +The command +.DM + tabletofact parent female male > fact +.DE +.PP +produces the following facts: +.DM +parent(Sally,Fred). +parent(Mike,Fred). +parent(Fred,Jane). +female(Jane). +female(Sally). +male(Fred). +male(Mike). +.DE +.PP +As you can see, +the relationship is set by the name of the table, and the subject and +object (if any) are set by the data in each row. +.PP +PROLOG +can now use its command +.B consult +to read this file. +If PROLOG +had rules about father, mother, grandparent, and so on, +then it could answer questions like: +.B mother(sally,fred)? +That fact is not in the data base, but can be deduced from rules like: +.DM + mother(X,Y) if parent(X,Y) and female(X) +.DE +.PP +.SH "See Also" +.B +tabletorule +.R +.SH Notes +.II COHware +If you wish to experiment with PROLOG, +volume 2 of +.B COHware +includes the source code for a PROLOG interpreter. diff --git a/man/tabletofix.man b/man/tabletofix.man new file mode 100644 index 0000000..7c8b894 --- /dev/null +++ b/man/tabletofix.man @@ -0,0 +1,82 @@ +.TH tabletofix "" "" "/rdb Command" +.PC "Convert /rdb table format to fixed-length format" +\fBtabletofix \fIcolumn\fB=\fIn\fB\|[r] ... < \fItableorlist\fR +.PP +.II "table^convert to fixed-length" +.II "record^convert to fixed-length" +The command +.B tabletofix +converts +.I tableorlist +from variable-length \fB/rdb\fR format to the +fixed-length format used by many other data-base management systems. +.PP +.B tabletofix +works by either padding or truncating each +.I column +to make it +.I n +characters wide. +If the letter \fIr\fR +is used with the width specification, +.B tabletofix +right-justifies the +.I column +in question \(em that is, +it pads the column with leading blanks, assuming any are necessary. +Otherwise, the +.I column +is left justified \(em that is, it pads the column with trailing blanks. +.PP +.B tabletofix +also throws away the head lines, as well as all tab characters and +blank records. +.SH Example +Consider table +.BR journal , +which looks like this when viewed with the command +.BR see : +.DM +Date^IAccount^IDebit^ICredit^IDescription$ +----^I-------^I ----^I -----^I-----------$ +820102^I101^I25000^I^Icash from loan$ +820102^I211.1^I^I25000^Iloan number #378-14 Bank Amerigold$ +820103^I150.1^I10000^I^Itest equipment from Zarkoff$ +820103^I101^I5000^I^Icash payment$ +820103^I211.2^I^I5000^Inote payable to Zarkoff Equipment$ +820104^I130^I30000^I^Iinventory - parts from CCPSC$ +.DE +.PP +The symbol \fB^I\fR indicate a tab, and the symbol +\fB$\fR indicates a newline. +.PP +Now, let's generate a fixed-length field data base from \fBjournal\fR. +We'll use the fields +.BR Date , +.BR Account , +.BR Credit , +and +.BR Debit . +We'll give the first two fields six bytes each; +and we'll give the last two fields seven bytes each, right justified: +.DM +tabletofix Date=6 Account=6 Debit=7r Credit=7r < journal > newjournal +.DE +.PP +This commands writes its output into file +.BR newjournal , +which appears as follows: +.DM +820102101 25000 +820102211.1 25000 +820103150.1 10000 +820103101 5000 +820103211.2 5000 +820104130 30000 +.DE +.SH "See Also" +.B +column, +fixtotable, +see +.R diff --git a/man/tabletolist.man b/man/tabletolist.man new file mode 100644 index 0000000..abcd7d7 --- /dev/null +++ b/man/tabletolist.man @@ -0,0 +1,68 @@ +.TH tabletolist "" "" "/rdb Command" +.PC "Convert a table to list format" +\fBtabletolist [ \-l ] < \fItable\fR +.PP +.II "table^convert to list" +.II "list^convert from table" +The command +.B tabletolist +converts +.I table +to list format. +.PP +Option \fB\-l\fR tells +.B tabletolist +not to write a newline at the beginning of the new list file. +This is for compatibility with the older list format and for other uses. +Note that most +.B /rdb +commands cannot read a list file if it lacks the initial newline. +.SH Example +Consider +.BR mailtable , +which has the following format: +.DM +Number Name Company Street City State ZIP Phone +------- ------- ------- ------- ------- ------- ------- ------- +1 Ronald McDonald McDonald's 123 Mac Attack Memphis +TENN 30000 (111) 222-3333 +2 Chiquita Banana United Brands Uno Avenito De La +Revolution San Jose Costa Rica 123456789 1234 +.DE +.PP +As you can see, the information in each row is often too wide for the screen; +these data would be better stored in a list-format file. +To convert +.B mailtable +to list format, type the following: +.DM + tabletolist < mailtable +.DE +.PP +This writes the following to the standard output: +.DM +\fB\fP +Number 1 +Name Ronald McDonald +Company McDonald's +Street 123 Mac Attack +City Memphis +State TENN +ZIP 30000 +Phone (111) 222-3333 +.DE +.DM +Number 2 +Name Chiquita Banana +Company United Brands +Street Uno Avenido de la Reforma +City San Jose +State Costa Rica +ZIP 123456789 +Phone 1234 + ... +.DE +.SH "See Also" +.B +listtotable +.R diff --git a/man/tabletom4.man b/man/tabletom4.man new file mode 100644 index 0000000..e86682b --- /dev/null +++ b/man/tabletom4.man @@ -0,0 +1,101 @@ +.TH tabletom4 "" "" "/rdb Command" +.PC "Convert a table to m4 define-file format" +\fBtabletom4 < \fItable \fB> \fIdefinefile\fR +.PP +.II "table^convert to m4" +.II m4 +The command +.B tabletom4 +converts a two-column table into a file of +define statements that can be input to the COHERENT macro processor +.BR m4 . +Define macros are in the form +.I define(old,new). +.PP +.B m4 +can do many things, including word-for-word substitution. +It can be used for language translation (in a stiff, inflexible way) +and for other conversions of data bases. +.PP +The +.I definefile +that +.B tabletom4 +produces is required by the +.B /rdb +command +.BR translate . +Only words that have a corresponding translation in the second +column will be put into +.IR definefile . +In this way, you can +use the file before you have all of the translations entered. +.SH Example +Consider the simple translation table +.BR ed.t : +.DM +English Deutch +------- ------- +I Ich +love liebe +you dich +widgit +.DE +.PP +The following command converts +.B ed.t +to m4 format and writes its output into file +.BR ed : +.DM + tabletom4 < ed.t > ed +.DE +.PP +.B ed +now contains the following: +.DM +define(I,Ich) +define(love,liebe) +define(you,dich) +.DE +.PP +Note that +.B widgit +was not converted because it did not have +a translation string in the second column of the table. +.B ed +can now be used by +.B m4 +or +.BR translate . +For example, if file +.B text +contains the following: +.DM + I love you. +.DE +.PP +The command +.DM + m4 ed text +.DE +.PP +writes +.DM + Ich liebe dich. +.DE +.PP +onto the standard output. +.SH "See Also" +.B +translate +.R +.br +COHERENT Tutorials: +.B +Introduction to the m4 Macro Processor +.R +.br +COHERENT Lexicon: +.B +m4 +.R diff --git a/man/tabletorule.man b/man/tabletorule.man new file mode 100644 index 0000000..2e7bd35 --- /dev/null +++ b/man/tabletorule.man @@ -0,0 +1,106 @@ +.TH tabletorule "" "" "/rdb Command" +.PC "Convert a table to PROLOG rule-file format" +\fBtabletorule \fItable ... \fB> \fIprologrulefile\fR +.PP +.II "table^convert to PROLOG rules" +.II "PROLOG^convert table to rules" +The command +.B tabletorule +converts each +.I table +into a file of rules in predicate-calculus implication form +that can be input to a PROLOG interpreter. +.PP +PROLOG rules are in the form: +.DS +.I + relationship(X,Y) :- class(X), relationship(X,Y). +.DE +.PP +For example +.DM + mother(X,Y) :- female(X), parent(X,Y). +.DE +.PP +means that ``X is the mother of Y, if X is female and X is the parent of Y.'' +.PP +PROLOG is a programming language used for artificial-intelligence applications. +Its name is a contraction of the phrase +.I "programming in logic." +Input to PROLOG consists of +.IR facts , +.IR rules , +and +.IR questions . +If you build a data base of facts and rules, you can ask questions +and get answers. +If a question you ask does not have a +corresponding fact in the data base, +PROLOG will look through its rule file to see if other facts might +logically deduce the fact requested. +.SH Example +Suppose we have the simple table +.B parent +.DM +Parent Child +------- ------- +Sally Fred +Mike Fred +Fred Jane +.DE +.PP +the simple table +.B female +.DM +Name +---- +Jane +Sally +.DE +.PP +the simple table +.B male +.DM +Name +---- +Fred +Mike +.DE +.PP +and the simple table +.BR ruletable : +.DM +True If +----------- ------------------------- +mother(X,Y) female(X), parent(X,Y) +.DE +.PP +The command +.DM + tabletorule ruletable > rule +.DE +.PP +writes the following into file +.BR rule : +.DM + mother(X,Y) :- female(X), parent(X,Y). +.DE +.PP +Now PROLOG can use its +.B consult +command to read +.BR rule . +It could then answer questions like: +.DM + mother(sally,fred)? +.DE +.SH "See Also" +.B +tabletofact +.R +.SH Notes +.II COHware +If you wish to experiment with PROLOG, +volume 2 of +.B COHware +includes the source code for a PROLOG interpreter. diff --git a/man/tabletosed.man b/man/tabletosed.man new file mode 100644 index 0000000..82df2ed --- /dev/null +++ b/man/tabletosed.man @@ -0,0 +1,99 @@ +.TH tabletosed "" "" "/rdb Command" +.PC "Convert table format to sed file format" +\fBtabletosed < \fItable \fB> \fIsedfile\fR +.PP +.II sed +.II "table^convert to sed commands" +The command +.B tabletosed +converts a two-column +.I table +into a file of +.B sed +statements that can be input to the +.B sed +stream editor. +.B sed +macros are in the form: +.DM + s/old/new/g +.DE +.PP +.B sed +can do many things, including word-for-word substitution. +.I sedfile +produced is used by the +.B /rdb +command +.BR translate . +It can be used for language translation (in a stiff, inflexible way) +and for other conversions of data bases. +.SH Example +Suppose we have a simple translation table, called +.BR ed.t " +.DM +English Deutch +------- ------- +I Ich +love liebe +you dich +widgit +.DE +.PP +The following command turns +.B ed.t +into a file of +.B sed +editing instructions, called +.BR ed.sed.1 : +.DM + tabletosed < ed.t > ed.sed.1 +.DE +.PP +.B ed.sed.1 +appears as follows: +.DM + s/I/Ich/g + s/love/liebe/g + s/you/dich/g +.DE +.PP +We can now use +.B ed.sed.1 +with +.B sed +or +.BR translate . +For example, if file +.B text +contains the sentence +.DM + I love you. +.DE +.PP +then typing +.DM + sed -f ed.sed.1 text +.DE +.PP +prints the following on the standard output: +.DM + Ich liebe dich. +.DE +.PP +You can also use this command to construct form letters. +.SH "See Also" +.B +translate +.R +.br +COHERENT Tutorials: +.B +Introduction to the sed Stream Editor +.R +.br +COHERENT Lexicon: +.B +sed +.R +.SO tabletosed diff --git a/man/tabletostruct.man b/man/tabletostruct.man new file mode 100644 index 0000000..49aa514 --- /dev/null +++ b/man/tabletostruct.man @@ -0,0 +1,159 @@ +.TH tabletostruct "" "" "/rdb Command" +.PC "Convert table to C-language struct declaration" +\fBtabletostruct \fItag name\fB < \fItable \fB> \fItable\fB.h\fR +.PP +.II struct +.II "table^convert to C struct" +.II "C language^struct" +The command +.B tabletostruct +converts a standard +.B /rdb +table into a C-language +.B struct +declaration. +.B struct +is the C language's record format. +.PP +.B tabletostruct +is useful for writing table-driven C code. +The tables used by your programs can be manipulated +by the data base and your users, or by nonprogrammer developers +(engineers, experts, managers, operators), +then converted to a +.B struct +and compiled into the C code for high-speed access. +.PP +.I tag +gives the +.B struct +tag for declaring other variables. +.I name +is the +.B struct +name to reference the structure values. +.PP +Please note once code compiled from a table, +changing that tables no longer affects the program until you recompile it. +Therefore, you gain maximum speed at the price of +recompiling the system and losing the flexibility of being +able to modify the tables during execution. +.SH Example +Here we start with a simple table, convert it to +.B struct, +make it a header file, and +compile it into a simple program that prints out each field. +To begin, consider table +.BR table , +which is structured as followed: +.DM +A B C +- - - +1 2 3 +.DE +.PP +The following commands converts +.B table +to a +.B struct +and writes it into file +.BR table.h : +.DM + tabletostruct Table table < table > table.h +.DE +.PP +.B table.h +now holds the following: +.DM +struct Table +{ +char *A; +char *B; +char *C; +} table [] = +{ "1","2","3" } +; +.DE +.PP +The following gives some C code that prints the contents of the +.B struct +we call +.BR Table : +.DM +#include "table.h" +.sp \n(pDu +main () +{ + printf ("A=%s\en", table[0].A); + printf ("B=%s\en", table[0].B); + printf ("C=%s\en", table[0].C); +} +.DE +.PP +When compiled with the command +.DM + cc -o printtable printtable.o +.DE +.PP +the newly created command +.B printtable +yields the following output: +.DM + A=1 + B=2 + C=3 +.DE +.PP +Note that the data are stored in an array of +.BR struct s, +so we must give a subscript for each row we want. +.PP +We can also put the command +.B tabletostruct +into a +.BR Makefile , +so that the COHERENT command +.B make +can automate the building of the +.B struct +and its compilation. +For example: +.DM +printtable: table.h printtable.o + cc -o printtable printtable.o +.sp \n(pDu +table.h: table + tabletostruct Table table < table > table.h +.DE +.PP +This says that the printable program depends upon +.B table.h +being up to date, and +.B table.h +depends upon table +.B table +being up to date. +If you had modified +.B table +since the last compile, +.B make +reexecutes the command +.BR tabletostruct . +.SH "See Also" +.B +listtotable, +tabletolist +.R +.br +COHERENT Tutorials: +.B +The C Language, +The make Programming Discipline +.R +.br +COHERENT Lexicon: +.B +array, +make, +struct +.R diff --git a/man/tabletotbl.man b/man/tabletotbl.man new file mode 100644 index 0000000..961510d --- /dev/null +++ b/man/tabletotbl.man @@ -0,0 +1,70 @@ +.TH tabletotbl "" "" "/rdb Command" +.PC "Convert /rdb table format to UNIX tbl/nroff format" +\fBtabletotbl < \fItablefile \fB> \fItblfile\fR +.PP +.II "table^convert to tbl" +.II tbl +The command +.B tabletotbl +converts an +.B /rdb +table to UNIX +.B tbl +format. +.B tbl +is a UNIX program that formats tables for the +.B nroff/troff +formatting and typesetting programs. +.\"See the UNIX documentation for +.\".B "tbl, nroff," +.\"and +.\".B troff. +.\".SH Example +.\"Suppose we have a journal table: +.\".sp +.\".nf +.\"$ cat journal +.\"Date Account Debit Credit Description +.\"------ ------- ----- ------ ---------------------------------- +.\"861222 101 25000 cash from loan +.\"861222 211.1 25000 loan number #378-14 Bank Amerigold +.\"861223 150.1 10000 test equipment from Zarkoff +.\"861223 101 5000 cash payment +.\"861223 211.2 5000 note payable to Zarkoff Equipment +.\"861224 130 30000 inventory - parts from CCPSC +.\"861224 201.1 15000 accounts payable to CCPSC +.\"861224 101 15000 cash payment to CCPSC for parts +.\".sp +.\"$ tabletotbl < journal +.\"\&.TS +.\"r n r r l. +.\"Date Account Debit Credit Description +.\"------ ------- ----- ------ ---------------------------------- +.\"861222 101 25000 cash from loan +.\"861222 211.1 25000 loan number #378-14 Bank Amerigold +.\"861223 150.1 10000 test equipment from Zarkoff +.\"861223 101 5000 cash payment +.\"861223 211.2 5000 note payable to Zarkoff Equipment +.\"861224 130 30000 inventory - parts from CCPSC +.\"861224 201.1 15000 accounts payable to CCPSC +.\"861224 101 15000 cash payment to CCPSC for parts +.\"\&.TE +.\".fi +.\".sp +.\"This can be sent on to +.\".B tbl +.\"and +.\".B nroff, +.\"or inserted into text that will be sent to them. +.\".sp +.\".nf +.\"$ tabletotbl < journal | tbl | nroff +.\".fi +.SH Notes +The COHERENT system's version of +.B nroff +and +.B troff +do not yet support +.BR tbl . +.SO tabletotbl diff --git a/man/tax.man b/man/tax.man new file mode 100644 index 0000000..18a90cf --- /dev/null +++ b/man/tax.man @@ -0,0 +1,87 @@ +.TH tax "" "" "/rdb Command" +.PC "Compute tax from income and tax table" +\fBtax \fIincome \fB< \fItaxtable\fR +.PP +.II "table^compute tax" +The command +.B tax +reads tax-table information from +.I taxtable +and computes taxes from it. +.I income +is the income you wish to check. +.I taxtable +must be in the three-column format shown in the following example. +.PP +.B tax +writes to the standard output +the computed tax rounded off to the nearest dollar. +.SH Example +The following table, +.BR X83 , +holds the federal income tax for single taxpayers for 1983 (Schedule X): +.DM +income tax percent +------ --- ------- +2300 0 11 +3400 121 12 +4400 251 15 +6500 566 15 +8500 866 17 +10800 1257 19 +12900 1656 21 +15000 2097 24 +18200 2865 28 +23500 4349 32 +28800 6045 36 +34100 7953 40 +41500 10913 45 +55300 17123 50 +.DE +.PP +If your taxable income is $20,000, type the following: +.DM + tax 20000 < X83 +.DE +.PP +.B taxtable +writes the following to the standard output: +.DM + 3369 +.DE +.PP +That is, according to the data in +.BR X83 , +a single person with an income of $20,000 in 1983 owed the IRS +$3,369. +.PP +You can also catch the output in a shell variable, like this: +.DM + TAX=`tax 20000 < X83` +.DE +.PP +This initializes environmental variable +.B TAX +to +.BR 3369 . +You can now use +.B TAX +to write the tax into a table. +For example: +.DM + compute "line == 35 { value = $TAX }" < form1040 +.DE +.PP +Note that this command +uses the quotation mark (") instead of the apostrophe ('), +because we want the shell to substitute the value of tax where we +have typed $TAX. +To quote something within quotation marks ("), you +must use apostrophes ('). +.SH Notes +Please note that the tax tables included with +.B /rdb +are for 1987. +Mark Williams Company makes no claim as to their accuracy, or their +usefulness in computing current taxes. +.I "Caveat utilitor!" diff --git a/man/termput.man b/man/termput.man new file mode 100644 index 0000000..8b4f4ba --- /dev/null +++ b/man/termput.man @@ -0,0 +1,108 @@ +.TH termput "" "" "/rdb Command" +.PC "Get terminal capability from /etc/termcap file" +\fBtermput \fIcapability\fR +.PP +.II termcap +.II "terminal^get capability" +The command +.B termput +reads and returns the current capabilities for your terminal, as set in file +.BR /etc/termcap . +A two-letter capability code is followed by a string of characters +that, when sent to the terminal screen, turn on or off that capability. +This assumes, of course, that your terminal is properly described in +.BR /etc/termcap . +.SH Capabilities +The capabilities are two-letter codes recognized by +.BR termcap . +Here are some examples: +.DS +\fBcl\fR Clear the terminal screen +\fBcm\fR Cursor movement +\fBso\fR Start standout mode (e.g., reverse video) +\fBse\fR End standout mode +.DE +.PP +Using most of these codes is straightforward: +just echo the appropriate code to the terminal to get the effect you want. +Cursor movement, however, is more complicated; +the cursor-movement code that +.B termput +returns must be edited to hold +the row and column to which you wish to move the cursor. +For details, +see the manual page for the command +.BR cursor . +.SH Example +You can embed +.B termput +in a shell script to +help invoke reverse video, blinking, and any other +your terminal has defined in +.BR /etc/termcap . +.PP +The first example clears the screen: +.DM + termput cl +.DE +.PP +The next example puts the screen into reverse video: +.DM + termput se +.DE +.PP +and the following turns off reverse video: +.DM + termput so +.DE +.PP +You can use +.B termput +to initialize environmental variables, which you can then invoke +in a shell script. +This is much faster than making repeated calls to +.BR termput . +To set up these shell variables, insert the following commands +your file +.BR $HOME/.profile : +.DM +.ta 2.5i +export AE=`termput ae` # end alternate character set - graphics +export AS=`termput as` # start alternate character set - graphics +.DE +.DM +.ta 2.5i +export CLEAR=`termput cl` # clear the screen +export CURSOR=`termput cm` # cursor movement - for the cursor command +export MB=`termput mb` # start blinking mode +export ME=`termput me` # turn off all attributes +export SE=`termput se` # end stand out mode +export SO=`termput so` # start stand out mode +export UE=`termput ue` # end underline mode +export US=`termput us` # start underline mode +.DE +.PP +Once this is set up, you can use these environmental variables in your +shell program. +For example, if you want a message to stand out, bracket it with +.B $SO +and +.BR $SE : +.DM + echo "${SO} LOGOFF NOW ${SE}" +.DE +.SH "See Also" +.B +clear, +cursor, +screen +.R +.br +COHERENT Lexicon: +.B +console, +stty, +termcap, +terminal-independent operations +.R +.SO termput diff --git a/man/testall.man b/man/testall.man new file mode 100644 index 0000000..dcbe158 --- /dev/null +++ b/man/testall.man @@ -0,0 +1,64 @@ +.TH testall "" "" "/rdb Command" +.PC "Test all /rdb programs in directory $RDB/demo" +\fBtestall [ > \fItestall.new \fB]\fR +.PP +.II "program^test" +The command +.B testall +runs most of the +.B /rdb +programs with the files in the +.BR $RDB/demo . +You should run +.B testall +in directory +.BR $RDB/demo , +because the test files are there. +.PP +.B testall +writes the output of each example to the standard output. +We suggest that you store the output in file +.BR $RDB/demo/testall.old . +Thereafter, you can check if the behavior of a test program has changed, +by using the COHERENT command +.B diff +to compare the output of +.B testall +with the contents of +.BR $RDB/demo/testall.old . +.B /rdb +performs this testing when the +.B /rdb +database is installed on a computer, and when code is changed. +.SH Example +First go to the demo directory, +assuming the path is +.BR /usr/rdb/demo : +.DM + cd /usr/rdb/demo + testall > testall.new + diff testall.old testall.new +.DE +.PP +The command +.B diff +finds and displays any differences between +.B testfile.old +and +.BR testfile.new . +After careful checking, if differences are correct, type: +.DM + mv testall.new testall.old +.DE +.PP +or +.DM + rm testall.new +.DE +.PP +This saves space, because these files are large. +.SH "See Also" +COHERENT Lexicon: +.B +diff +.R diff --git a/man/testsearch.man b/man/testsearch.man new file mode 100644 index 0000000..3562a3d --- /dev/null +++ b/man/testsearch.man @@ -0,0 +1,40 @@ +.TH testsearch "" "" "/rdb Command" +.PC "Test the fast-access methods" +.B testsearch +.PP +.II "search^test" +.II "index^test" +The command +.B testsearch +runs and times the +.B /rdb +commands +.B index +and +.BR search . +.PP +You must +.B cd +to directory +.B $RDB/demo +before you run +.BR testsearch , +because it uses tables +.B unixtable +and +.B unixlist +in that directory. +If you wish, you can edit +.B testsearch +to time the search of a big list or table that you are manipulating. +.SH "See Also" +.B +testall, +timesearch +.R +.br +COHERENT Lexicon: +.B +time +.R +.SO testsearch diff --git a/man/timesearch.man b/man/timesearch.man new file mode 100644 index 0000000..d8c1954 --- /dev/null +++ b/man/timesearch.man @@ -0,0 +1,66 @@ +.TH timesearch "" "" "/rdb Command" +.PC "Time fast-access methods" +\fBtimesearch [ > \fItimesearch.new\fB ]\fR +.PP +.II "program^time execution" +.II "time execution" +The command +.B timesearch +runs and times the +.B /rdb +commands +.B index +and +.BR search . +Its purpose is to find the fastest fast-access method for a given situation. +Theory often fails when working with access methods; +a sequential search by the select command may prove faster +than one of the fast-access methods when the overhead of building the +index is included in the evaluation. +.PP +.B timesearch +is in directory +.B $RDB/demo +rather than in +.B $RDB/bin +because it is written to test just the tables +.B unixtable +and +.BR unixlist . +Because it is a shell script, +you can edit it to test searching on a big table you are manipulating. +.SH Example +The following command test-runs +.BR timesearch : +.DM + cd $RDB/demo ; timesearch | more +.DE +.PP +.B timesearch +writes to the standard output the outputs of the +.B /rdb +commands +.B index +and +.BR search , +plus the output of the COHERENT command +.B time +that it uses to time the programs. +.SH "See Also" +.B +index, +search, +testall, +testsearch +.R +.br +COHERENT Lexicon: +.B +time +.R +.br +COHERENT tutorials: +.I +Introducing sh, the Bourne Shell +.R +.SO timesearch diff --git a/man/todaysdate.man b/man/todaysdate.man new file mode 100644 index 0000000..38976f3 --- /dev/null +++ b/man/todaysdate.man @@ -0,0 +1,27 @@ +.TH todaysdate "" "" "/rdb Command" +.PC "Print today's date in YYMMDD format" +.B todaysdate +.PP +.II "date^get today" +The command +.B todaysdate +displays the today's date in the form: +.IR YYMMDD . +Under +.BR /rdb , +this format is called the ``computer format.'' +It is the best format for entering data in a column +because it sorts correctly. +.SH "See Also" +.B +computedate, +gregorian, +julian +.R +.br +COHERENT Lexicon: +.B +date, +time, +timezone +.R diff --git a/man/total.man b/man/total.man new file mode 100644 index 0000000..7a21eb2 --- /dev/null +++ b/man/total.man @@ -0,0 +1,87 @@ +.TH total "" "" "/rdb Command" +.PC "Sum a column" +\fBtotal [ \-l ] [ \fBcolumn ...\fB ] < \fItable\fR +.PP +.II "column^add" +.II table^add +The command +.B total +adds up the values in each +.I column +in +.IR table . +If no +.I column +is named on the command line, +it totals every column in +.IR table . +.PP +The option +.B \-l +prints the entire table as well as the total for each +.IR column . +.PP +Several other commands behave just like +.BR total , +except that the value they produce is not a total but: +.BR datatype , +.BR maximum , +.BR minimum , +.BR mean , +.BR precision , +.BR width , +.BR length , +and so on. +The all call the same program, but decide +what to print depending upon the name used to call it. +.SH Example +There are two formats. +Without the option +.BR \-l , +.B total +just gives the head lines and the total line. +For example, to find the totals of columns +.B Debit +and +.B Credit +in table +.BR journal , +type: +.DM + total Debit Credit < journal +.DE +.PP +This returns something like: +.DM +Date Account Debit Credit Description +---- ------- ----- ------ ----------- + 65000 65000 +.DE +.PP +However, if you wish to see the whole table, as in a report, type: +.DM + total -l Debit Credit < ledger +.DE +.PP +This returns something like: +.DM +Account Date Debit Credit +------- ------- ------- ------- +101 890102 25000 +101 890103 5000 +101 890104 15000 +130 890104 30000 +150.1 890103 10000 +201.1 890104 15000 +211.1 890102 25000 +211.2 890103 5000 +------- ------- ------- ------- + 65000 65000 +.DE +.SH "See Also" +.B +maximum, +mean, +minimum, +subtotal +.R diff --git a/man/translate.man b/man/translate.man new file mode 100644 index 0000000..4306b23 --- /dev/null +++ b/man/translate.man @@ -0,0 +1,121 @@ +.TH translate "" "" "/rdb Command" +.PC "Word-for-word substitution using a translation file" +\fBtranslate \fIlanguage \fB< \fItext \fB> \fItranslatedtext\fB +.PP +.II "text^translate" +The command +.B translate +performs word-for-word replacement on +.IR text . +You can use it to perform crude translations from one language to +another. +.PP +.B translate +uses either +.BR m4 , +.BR sed , +or both. +For +.BR m4 , +.B translate +needs a define file generated by the +.B /rdb +command +.BR tabletom4 ; +the define file must be named +.IR language . +For +.BR sed , +it needs a preprocessing file and can also use a postprocessing file, +both generated by the command +.BR tabletosed ; +the preprocessing file must be named \fIlanguage\fB.sed.1\fR, and the +postprocessing file must be named \fBlanguage\fB.sed.2\fR. +See the manual pages for these +.B /rdb +commands for a description of the sort of table they require as input. +.SH Example +Suppose we have a simple translation for English to Deutsch (German), +called +.BR ed.t : +.DM +English Deutsch +------- ------- +I Ich +love liebe +you dich +widgit +.DE +.PP +The command +.DM + tabletom4 < ed.t > ed +.DE +.PP +generates the +.B m4 +definition file +.BR ed , +which contains the following: +.DM +define(I,Ich) +define(love,liebe) +define(you,dich) +.DE +.PP +Note that +.B tabletom4 +did not convert +.B widgit +because this word had no translation in the table. +.PP +Now, if file +.B text +contains the sentence +.DM +I love you. +.DE +.PP +you can use +.B translate +to translate it into German. +Typing +.DM + translate ed text +.DE +.PP +prints the following on the standard output: +.DM + Ich liebe dich. +.DE +.PP +.B translate +has also been used in a biomedical expert system. +The idea is to convert each word into its value after looking it +up in a table. +For example: +.DM +Var Value +------- ------- +age 42 +weight 80 + ... +.DE +.PP +This is something the Lisp language does frequently. +.PP +.B translate +can also be used to construct form letters. +.SH "See Also" +.B +tabletom4, +tabletosed, +word +.R +.br +COHERENT Lexicon: +.B +m4, +sed +.R +.SO translate diff --git a/man/trim.man b/man/trim.man new file mode 100644 index 0000000..7a06712 --- /dev/null +++ b/man/trim.man @@ -0,0 +1,335 @@ +.TH trim "" "" "/rdb Command" +.PC "Trim excess white space from a table" +\fBtrim [-[l|n|ln] \fIcol...\fB][-r[\fIn\fB] \fIcol...\fB][\-t'\fIc\fB']...< \fItable\fR +.PP +.II "white space^trim from table" +.II "table^trim white space" +.II "column^trim white space" +The command +.B trim +trims excess white space from each column in +.IR table . +This lets you print a wide table with the minimum width for each column. +Please note that +.B trim +removes all tabs from +.IR table , +which renders it unreadable by any +.B /rdb +command, +including +.B trim +itself. +.PP +.B trim +without any options does the best it can to squeeze +.I table +without discarding any information. +It make each column as wide as its widest column name or data item. +You can also tell +.B trim +to reduce a column's width, even if it has to throw away characters. +.PP +Note that +.B trim +trim all columns, even if some are listed with the options. +Most other +.B /rdb +commands, such as +.BR justify , +affect only the columns named on the command line \(em +unless none are listed, in which case they do the same thing to all of them. +.SH Options +.B trim +recognizes the following options: +.IP \fIn\fR +The number of characters remaining after the trim. +.IP "\fB\-\fIn column ...\fR" +Return only +.I n +characters from the left of +.IR column . +.IP "\fB\-l\fIn column ...\fR" +Return only +.I n +characters from the left of +.IR column . +.IP "\fB\-r\fIn column ...\fR" +Return only +.I n +characters from the right of column. +.IP "\fB\-t\(aa\fIc\fB\(aa\fR" +Use character +.I c +as the column separator. +For example, you may want to keep the tab character so that +the table can be used by other +.B /rdb +commands. +.sp \n(pDu +When use this option, +remember that the COHERENT shell gives special treatment to +certain characters, such as the tab and the vertical bar `|'. +You must quote these characters by enclosing them between apostrophes. +.PP +The above options affect the columns named. +The options are +.I sticky, +which means that you can follow an option with +as many column names as you wish. +.SH Example +Consider, once again, table +.BR inventory , +which appears as follows: +.DM +Item Amount Cost Value Description +------- ------- ------- ------- -------------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +.DE +.PP +To trim it, type: +.DM + trim < inventory +.DE +.PP +This prints the following: +.DM +Item Amount Cost Value Description +---- ------ ------ ------- -------------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +.DE +.PP +Note that the length of the column names +.B Item +and +.B Amount +determined the width of their columns, but in all other columns the +data determined the width. +.PP +To examine what +.B trim +did exactly, +you can use the command +.B see . +For example: +.DM +Item Amount Cost Value Description +---- ------ ------ ------- -------------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +.DE +.PP +Note that there are no tabs \fB^I\fR. +This table is only for printing. +Don't input or pipe this output to another +.B /rdb +command unless you use the +.B -t +option, which retains the tab as a column separator. +.PP +We can further squeeze the table with the options +\fB\-\fIn\fR, \fB\-l\fIn\fR, and \fB\-r\fIn\fR. +For example, the command +.DM + trim -1 Item < inventory +.DE +.PP +reduces column +.B Item +to only one character. +This produces the following: +.DM +I Amount Cost Value Description +- ------ ----- ------- -------------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +.DE +.PP +Note that to reduce +.I Item +to one character, +.B trim +had to throw away all but the `I' +of the column name +.BR Item . +Note that the other columns were trimmed normally. +.PP +Because options are sticky, you can name several columns after one option: +For example, the command +.DM + trim -5 Item Amount Cost Description < inventory +.DE +.PP +reduces the four named columns to a maximum of five characters. +This produces the following: +.DM +Item Amoun Cost Value Descr +----- ----- ----- ------- ----- +1 3 5.00 15.00 rubbe +2 100 0.50 50.00 test +3 5 8.00 40.00 clamp +4 23 1.98 45.54 plate +5 99 2.45 242.55 clean +6 89 14.75 1312.75 bunse +7 5 175 875.00 scale +.DE +.PP +You can use the option \fB\-r\fIn\fR +both to save the +.I n +right characters of a column and to line up numbers on the right. +For example, the command +.DM + trim -r3 Amount < inventory +.DE +.PP +produces: +.DM +Item Amo Cost Value Description +---- --- ----- ------- -------------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +.DE +.PP +Note that the column name +.B Amount +has been reduced to +.BR Amo . +Also, the column is right justified as a result of removing the +right three characters of each field in the column. +.PP +A complex trim might be done like this: +.DM + trim -1 Item -r3 Amount -r5 Cost -l7 Value Description < inventory +.DE +.PP +This command produces: +.DM +I Amo Cost Value Descrip +- --- ----- ------- ------- +1 3 5.00 15.00 rubber +2 100 0.50 50.00 test tu +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleanin +6 89 14.75 1312.75 bunsen +7 5 175.0 875.00 scales +.DE +.PP +We have squeezed this table a lot. +.PP +We have been working with an unjustified table. +What if the table had been justified? +First let's justify the table: +.DM + justify < inventory > tmp ; mv tmp inventory +.DE +.PP +Now +.B inventory +is justified, as follows: +.DM + Item Amount Cost Value Description +------- ------- ------- ------- --------------- + 1 3 5.00 15.00 rubber gloves + 2 100 0.50 50.00 test tubes + 3 5 8.00 40.00 clamps + 4 23 1.98 45.54 plates + 5 99 2.45 242.55 cleaning cloth + 6 89 14.75 1312.75 bunsen burners + 7 5 175.00 875.00 scales +.DE +.PP +Now, if we trim, you see: +.DM +Item Amount Cost Value Description +---- ------ ------ ------- -------------- + 5.0 15.00 rubber gloves + 10 0.5 50.00 test tubes + 8.0 40.00 clamps + 2 1.9 45.54 plates + 9 2.4 242.55 cleaning cloth + 8 14.7 1312.75 bunsen burners + 175.0 875.00 scales +.DE +.PP +This mess results from trimming the right characters in +the right justified columns +.BR Item , +.BR Amount , +and +.BR Cost . +This preserved the blank spaces, which are to the left in each column, +and threw away the data. +One way to fix this is to tell +.B trim +that the columns are right justified, like this: +.DM + trim -r Item Amount Cost < inventory +.DE +.PP +This produces: +.DM +Item Amount Cost Value Description +---- ------ ------ ------- -------------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +.DE +.PP +You can also compress the file before piping it into +.BR trim . +For further compression, use the command: +.DM + trim -r1 Item -r3 Amount -r6 Cost -l11 Description < inventory +.DE +.PP +This produces: +.DM +I Amo Cost Value Description +- --- ---- ----- ----------- +1 3 5.00 15.00 rubber glov +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cl +6 89 14.75 1312.75 bunsen burn +7 5 175.00 875.00 scales +.DE +.SH "See Also" +.B +compresss, +justify, +trimblank +.R diff --git a/man/trimblank.man b/man/trimblank.man new file mode 100644 index 0000000..bec79f6 --- /dev/null +++ b/man/trimblank.man @@ -0,0 +1,28 @@ +.TH trimblank "" "" "/rdb Command" +.PC "Remove leading and trailing blanks from a string" +\fBtrimblank [\fIstring\fB] [ < \fIfile\fB ]\fR +.PP +.II "string^trim white space" +.II "white space^trim from string" +The command +.B trimblank +trims all white-space characters from the beginning and end of +.IR string . +This is useful in shell programming, in which you need to trim white space +from a string that has been padded (justified) with space characters. +.SH Example +The command +.DM +echo ' here is the good stuff ' | trimblank +.DE +.PP +prints: +.DM + here is the good stuff +.DE +.SH "See Also" +.B +compresss, +justify, +trim +.R diff --git a/man/tset.man b/man/tset.man new file mode 100644 index 0000000..be13449 --- /dev/null +++ b/man/tset.man @@ -0,0 +1,66 @@ +.TH tset "" "" "/rdb Command" +.PC "Fetch termcap entry for a terminal type" +\fBtset [ \fITERM \fB]\fR +.PP +.II "termcap^find entry for TERM" +.II TERM^find termcap entry" +The command +.B tset +duplicates the Berkeley UNIX program by the same name. +It finds the entry in file +.B /etc/termcap +for terminal type +.IR TERM . +If no terminal type is named on the command line, it returns the +.B termcap +entry for the type of terminal you are now using. +.PP +You can use +.B tset +to initialize an environmental variable, which you then filter with other +.B /rdb +or COHERENT commands. +.SH Example +If you are working on your computer's console, your terminal type is +probably +.BR ansipc . +In that case, the command +.B tset +prints the following on the standard output: +.DM +:al=\eE[L:am:bs:bt=\eE[Z:bw:cd=\eE[O:ce=\eE[K:ch=\eE[%i%d`:cl=\eE[2O:\e +:cm=\eE[%i%d;%dH:co#80:cs=\eE[%i%d;%dr:cv=\eE[%i%dd:dl=\eE[M:ho=\eE[H:\e +:is=\eE[25f\eE[7m 1=Line_L 2=Line_R 3=D_Ln 4=Und_Ln 5=Undo 6=Und_Blk 7=Tag\e + 8=Join 9=Rptx 10=Rptd \eE[m\eE[H:\e +:k0=\eE[0x:k1=\eE[1x:k2=\eE[2x:k3=\eE[3x:k4=\eE[4x:\e +:k5=\eE[5x:k6=\eE[6x:k7=\eE[7x:k8=\eE[8x:k9=\eE[9x:\e +:kb=^h:kd=\eE[B:kh=\eE[H:kl=\eE[D:kr=\eE[C:ku=\eE[A:\e +:li#24:ll=\eE[24;1H:hd=\eE[C:se=\eE[m:sf=\eE[S:sg#0:so=\eE[7m:sr=\eE[T:\e +:te=\eEc:ue=\eE[m:up=\eE[A:us=\eE[4m:\e +:KI=\eE[5x:KD=\eE[3x:Kd=\eE[P:KB=\eE[6x:KU=\eE[4x:Ku=\eE[@:KM=\eE[7x:KJ=\eE[8x:\e +:Kt=\eE[Z:KT=\et:KL=\eE[1x:KR=\eE[2x:KP=\eE[U:Kp=\eE[V:KX=\eE[9x:KC=\eE[0x:\e +:KE=\eE[24H:KW=^F:Kw=^R:Kr=^N:do=\eE[B: +.DE +.PP +The command +.DM + TERMCAP=`tset` +.DE +.PP +initializes the environmental variable +.B TERMCAP +to your terminal's +.B termcap +entry. +Storing this information in an environmental variable greatly speeds +processing of programs that manipulate the screen. +.SH "See Also" +.B +termput +.R +.br +COHERENT Lexicon: +.B +termcap +.R +.SO tset diff --git a/man/union.man b/man/union.man new file mode 100644 index 0000000..15eeca4 --- /dev/null +++ b/man/union.man @@ -0,0 +1,87 @@ +.TH union "" "" "/rdb Command" +.PC "Concatenate tables" +\fBunion \fItableorlist ... \fB[\-] [< \fItableorlist \fB]\fR +.PP +.II "table^concatenate" +.II "concatenate tables" +The command +.B union +concatenates all of the +.IR tableorlist s +named on the command to form a new table or list. +The new table or list contains +all the rows in the first input table or list followed +by the rows of the second, and so on. +The input tables or lists must have the same number of columns. +It is best that the columns have the same name and the same type +of data, in the same order. +.PP +The hyphen `-' tells where in the list of files the standard input is to go. +This gives you the freedom to use +.B union +in a pipe and to place the input file anywhere in the output. +.SH Example +Consider table +.BR journal , +as follows: +.DM +Date Amount Account Ref Description +---- ------ ------- --- ----------- +820107 14.00 meal v meal with jones +820119 81.72 vitamin c sundown vitamins +820121 20.83 meal v meal with scott +820121 2500.00 keogh c keogh payment +820125 99.00 dues v dues to uni-ops +.DE +.PP +and table +.BR carexpense , +as follows: +.DM +Date Amount Account Ref Description +---- ------ ------- --- ----------- +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate +820114 93.00 car c car registration dmv +.DE +.PP +The command +.DM + union journal carexpense +.DE +.PP +produces the following output: +.DM +Date Amount Account Ref Description +---- ------ ------- --- ----------- +820107 14.00 meal v meal with jones +820119 81.72 vitamin c sundown vitamins +820121 20.83 meal v meal with scott +820121 2500.00 keogh c keogh payment +820125 99.00 dues v dues to uni-ops +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate +820114 93.00 car c car registration dmv +.DE +.PP +The command +.DM + echo journal | union - carexpense +.DE +.PP +accomplishes the same thing as the previous example, +but shows demonstrates how to use +.B union +with a pipe. +.SH "See Also" +.B +jointable, +split +.R +.br +COHERENT Lexicon: +.B +cut, +paste +.R +.SO union diff --git a/man/uniondict.man b/man/uniondict.man new file mode 100644 index 0000000..e66fad1 --- /dev/null +++ b/man/uniondict.man @@ -0,0 +1,55 @@ +.TH uniondict "" "" "/rdb Command" +.PC "Combine three tables into translation dictionary" +\fBuniondict \fIlangtolang \fB> \fIdefinefile\fR +.PP +.II m4 +.II "table^convert to m4" +.II "define file" +The command +.B uniondict +creates an +.B m4 +define-file out of three tables. +.I langtolang +is a two-letter code that indicates which language is being translated +to which; for example, +.B ed +stands for ``English to German'' (Deutsch). +.PP +.B uniondict +expects one table of lower-case letters, such as +.BR ed.low , +and one of all capital letters, such as +.BR ed.allcap , +and builds its own table with initial capitals. +Finally +.B uniondict +uses the +.B /rdb +command +.B tabletom4 +to create a definefile for +.BR m4 . +.SH Example +To generate a definefile for English-to-German translation, type the command: +.DM + uniondict ed +.DE +.PP +If the two precursor files already exist, then this command generates +a new file named +.B ed +that can be used by +.BR translate . +.SH "See Also" +.B +tabletom4, +translate, +word +.R +.br +COHERENT Lexicon: +.B +m4 +.R +.SO uniondict diff --git a/man/unlock.man b/man/unlock.man new file mode 100644 index 0000000..d6f4205 --- /dev/null +++ b/man/unlock.man @@ -0,0 +1,103 @@ +.TH unlock "" "" "/rdb Command" +.PC "Unlock a record or field of a file" +\fBunlock \fItableorlist processid from to indexfrom indexto\fR +.PP +.II table^unlock +.II "record locking" +The command +.B unlock +unlocks a record or file by removing a row from a common unlock file. +File \fB/tmp/L\fItableorlist\fR +contains one line for each locked record or field plus +.I processid +of the process that locked it. +.I from +and +.I to +give, respectively, the offsets of the beginning and end of the locked +record or field; +and +.I indexfrom +and +.I indexto +give, respectively, the offsets of the beginning and end of the row or +field's entry in the secondary index file. +.SH Example +Let us use +.B unlock +on table +.BR inventory , +which is as follows: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 5 500 test tubes + 3 5 80 400 clamps + 4 23 19 437 plates + 5 99 24 2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +First, we use the +.B /rdb +command +.B seek +to locate the record and return the offset and size: +.DM + LOCATION=`echo 5 | seek -mb inventory Item` +.DE +.PP +The above command returns the offset and size of column +.B Item +in row 5 of table +.BR inventory , +and writes the informatio into environmental variable +.BR LOCATION . +When we look at the contents of +.BR LOCATION , +we see the following: +.DM + 207 245 0 8 +.DE +.PP +This means that the record begins 207 bytes into the file, ends +245 bytes into the file, and therefore is 38 bytes long. +.PP +Now we can call +.B unlock +to unlock the record: +.DM + unlock inventory $$ $LOCATION +.DE +.PP +Note that we use the environmental variable +.B $LOCATION +to supply the location. +We also use the shell's built-in variable \fB$$\fR to +write the process identifier onto the command line. +.PP +Let's see what the lock file looks like. +Since we're locking table +.BR inventory , +the lock file is named +.BR Linventory . +If we type the command: +.DM + cat /tmp/Linventory +.DE +.PP +we see the message: +.DM + cat: cannot open /tmp/Linventory +.DE +.PP +.B unlock +removes the lock file if it removes the last lock line. +.SH "See Also" +.B +lock, +seek +.R +.SO unlock diff --git a/man/update.inv.man b/man/update.inv.man new file mode 100644 index 0000000..ac8428b --- /dev/null +++ b/man/update.inv.man @@ -0,0 +1,67 @@ +.TH update.inv "" "" "/rdb Command" +.PC "Multi-user update with screen form and record locking" +.B update.inv +.PP +.II "update inventory" +.II "inventory^update" +The command +.B update.inv +is a sample shell program that demonstrates several features of +.BR /rdb , +including multiuser input, fast access, screen-form input, and record locking. +It updates an inventory file named +.B inv +that is in the directory +.BR $RDB/demo +Use it to see how to use the COHERENT shell to tie +.B /rdb +and COHERENT programs together to form +fast, multiuser, large-file applications. +.PP +The environmental variable +.B TERM +must be set to the correct name of your terminal, +as described in the COHERENT system file +.BR /etc/termcap . +To make the reverse video and underlining work, +you will have to initialize environmental variables in your +.BR .profile . +.SH Example +.B cd +to directory +.B $RDB/demo +and run +.BR update.inv : +.DM + cd rdb/demo + update.inv +.DE +.PP +You'll the following template on your screen: +.B1 +Makeapile, Inc. Inventory Update Mon Jan 8, 1990 +.sp \n(pDu +Item Number: _______ (or hit Return key to exit.) +.sp \n(pDu +Item Cost Value Description +---- ---- ----- ----------- +.sp \n(pDu +Amount Onhand: _______ +.B2 +.PP +Try the program for yourself and study the program source. +.SH "See Also" +.B +lock, +search, +seek, +replace, +unlock +.R +.br +COHERENT Lexicon: +.B +TERM, +termcap +.R +.SO update.inv diff --git a/man/update.rdb.man b/man/update.rdb.man new file mode 100644 index 0000000..e0f4a44 --- /dev/null +++ b/man/update.rdb.man @@ -0,0 +1,360 @@ +.TH update.rdb "" "" "/rdb Command" +.PC "Display and edit records in any sized file" +\fBupdate.rdb [-l -m[bhirs] -v] \fItableorlist \fB[\fIkeycolumn ... \fB]\fR +.PP +.II "table^update" +.II "record^update" +.II "update table" +The command +.B update.rdb +lets you find a record in +.IR tableorlist , +display it, and update it. +.PP +.B update.rdb +can find a record in any of several different ways: +by moving up and down in the file, +by record number, +by matching a string pattern in a record, +and a fast-access method search. +.PP +.B update.rdb +lets you use a text editor to edit the record you find. +To lock out other users who might be looking at your file, +.B update.rdb +uses the +.B /rdb +command +.B lock +to lock the record being edited. +When you finish editing, +.B update.rdb +replaces the edited record back into +.IR tableorlist . +.PP +If the new record is smaller than the old record, +it adds space characters to the end of the last column to pad out the record. +If the new record is larger than +the old record, it trims +trailing and leading spaces from the columns. +If +.B update.rdb +cannot find enough blank spaces to trim, +it does not replace the record, +but appends it to the end of the table and updates the +fast-access method indexes, if any. +If this happens, you have several options: +You can reedit the record. +You can append the record to the end of the file using +.B enter +and delete the current record. +You can also pad each record with extra spaces so that there +will be room when you do updates. +To add padding, use the command +.BR pad . +.PP +Because +.B update.rdb +replaces an edited record to where the original was extracted, you +do not have to rebuilt entirely any fast-access indexes that +.I fileorlist +owns. +As long as you don't change the key columns, the fast-access +methods can still find the records. +This can mean a considerable savings in the time needed to update a record. +.PP +Also, +.B update.rdb +can handle any sized file, whereas the text editors +and word processors mostly cannot handle files of more than a few +thousand characters. +.SH Options +.B update.rdb +recognizes the following options: +.IP \fB\-l\fR 0.3i +No lock. +Do not lock the record by blanking out the record in the file while updating. +This is much faster than using record-locking; but it is truly safe if you +are the only person who can update +.IR tableorlist . +If more than one person can access +.I tableorlist +at the same time, do not use this option. +.IP \fB-m\fR +Fast access method. +See the manual page for the +.B /rdb +command +.B search +for a description of these options. +.IP \fB\-v\fR +Verbose. +With this option, +.B update.rdb +prints information about what it is doing during each step of updating +a record. +You can use this information to find the bottlenecks in updating; for +example, you may find that you are not using the best method of +accessing the records you wish to update. +.SH Getting In and Out +To invoke +.B update.rdb +to edit +.IR tableorlist , +simply type: +.DM + update.rdb tableorlist +.DE +.PP +.B update.rdb +prompt you with a `>'. +.PP +You can exit from +.B update.rdb +by any of three methods: +Type \fB\fR, type ``quit'', or simply type `q' +(because +.B update.rdb +looks at only the first character of the one-word commands). +.PP +Finally, the +.I kill +character on your terminal (usually \fB\fR) +kills the program, as it does other COHERENT commands. +.SH "Help Menu" +.B update.rdb +has a help menu built into it. +To see it, type +.B help +at the `>' prompt. +You will see the following: +.DM + Command Description | update.rdb [-l -m[bhirs] -l] file +--------------------------------------------------------------- +CTRL-d exit, hold down CTRL key and press \fBd\fP + (see quit below) +Return key display next record +number display record number you enter + (number = 1,29,...) ++number move forward the number of records in the file +-number move backwards the number of records in the file +. redisplay the current record +/pattern find next record with pattern +=key find records with key matching what you type +!command execute the unix command while still in update.rdb +!enter file enter new records at end of the file +.sp \n(pDu +(You need only type the first letter of the commands below) +.DE +.DM +delete delete the current record +m[bhirs] [col] set up fast access method for key column + b=binary, h=hash, i=index, r=record, s=sequential +lock lock update.rdb record by blanking (toggle, true) +search key find records with matching key +help help list displayed (this help list) +quit quit (also cntl-d) +update.rdb update current record, allows you to edit, replace +verbose report internal actions (toggle, false) +.DE +.SH "Finding a Record" +There are several ways to find the record you want. +.PP +When +.B update.rdb +comes up, it displays the first record in +.IR tableorlist . +To move to the next record, simply press +.BR . +You can move forward any number of records with a plus sign and a number, +for example, +.BR +5 ; +and you can move backwards with a minus sign and a number, for example: +.BR \-16 . +You can also select a specific record by typing its number. +Record numbers are displayed with each record. +.SH "Pattern Search" +If you try to go beyond the last record or before the first record, +.B update.rdb +warns you. +.PP +You can also search for a record that has a given string +by typing a slash character followed by string. +For example, to search for pattern ``Ronald'', type: +.BR /Ronald . +.B update.rdb +searches forward starting with the next record, then +wraps around and goes to the first record in the file +and continues till it finds a match or returns to the current record. +.SH "Fast-Access Methods" +.B update.rdb +can use fast-access methods to find a record within +.IR tableorlist . +You can turn on the fast-access methods in either of two ways: +.IP \fB1.\fR 0.3i +Use the appropriate option on the command line. +For example, the command +.DM + update.rdb -mh inventory Description +.DE +.sp \n(pDu +invokes the fast-access method for table +.BR inventory . +.IP \fB2.\fR +Type the appropriate option at +.BR update.rdb 's +prompt. +For example, typing +.DM + mh Description +.DE +.PP +at +.BR update.rdb 's +`>' prompt invokes the fast-access method for column +.BR Description . +.PP +Both ways tell +.B update.rdb +which method to use and which column is the key column. +.PP +Once invoked, you can search with either the +.B = +or +.B s +commands. +For example: +.DM + > =keyvalue + > s keyvalue +.DE +.SH Updating +To update the current record, simply type: +.DM + > update +.DE +.PP +Just the letter +.B u +is sufficient. +.PP +The steps in using +.B update.rdb +are as follows: +.IP \fB1.\fR 0.3i +Invoke +.B update.rdb +with the name of the table or list to be edited. +.IP \fB2.\fR +Find the record you want by entering its row number, a string it contains, +or through a fast-access method. +.IP \fB3.\fR +When the record is found, type +.B u +to update it. +.IP \fB4.\fR +.B update.rdb +copies the record to a file with a header +so that it is a one record table or list. +.IP \fB5.\fR +.B update.rdb +replaces the selected record with a string of blanks of the same length, +hold the record's place within its table, and to lock out other users. +.IP \fB6.\fR +.B update.rdb +then copies the temporary record file to a backup file in case you +damage it. +.IP \fB7.\fR +.B update.rdb +then reads environmental variable +.B EDITOR +and calls the editor it names to let you edit the record you selected. +If +.B EDITOR +is not set in your environment, +it invokes +.B vi +by default. +.IP \fB8.\fR +Use the editor to edit the record. +.IP \fB9.\fR +When you exit your editor, +.B update.rdb +returns the edited the record into its table or list. +It adds or removes trailing spaces to make the record fit into the same +amount of space it previously occupied in its table or list. +If the record doesn't fit into its old space, +.B update.rdb +lets you choose whether to re-edit the record, +or append it to the end of the table or list. +.sp \n(pDu +You can also validate a record before leaving your editor by typing: +.DM + validate file.v < file +.DE +.PP +See the COHERENT Lexicon entries for the editor you are using for +information on how to pass that command to the shell without exiting +from the editor. +The entry for the command +.B validate +in this manual describes how it works. +.SH "Other Useful Commands" +If you need to see the current record again because it has +run off the screen, simply type a period at +.BR update.rdb 's +prompt. +.PP +To execute a COHERENT shell command, type an exclamation point +and the command you wish to execute. +For example, to execute the command +.B ls +from within +.BR update.rdb , +type +.DM + > !ls +.DE +.PP +This feature also lets you append new records to a table while still within +.BR update.rdb . +To do so, just type: +.DM + > !enter thisfilename +.DE +.PP +When you leave the +.B enter +command, you will be back in +.BR update.rdb . +.PP +You can also index the file you are working with from within +.BR update.rdb . +Just type: +.DM + > !index -mi filename keycolumn +.DE +.PP +This command creates index file \fIfilename\fB.i\fR, which +.B update.rdb +can then use for fast-access searching of file you are updating. +.SH Padding +When you set up a table +that you intend to update, you probably should pad it \(em +that is, add extra space characters to each record. +Then you can add characters to a record and +.B update.rdb +will trim off the space characters to fit it back into the file. +For details, +see the manual page for the command +.BR pad . +.SH Notes +This command is named +.B update.rdb +rather than +.BR update , +as in other implementations of +.BR /rdb , +to avoid clashing with the COHERENT command +.BR update , +which does something very different. diff --git a/man/uppercase.man b/man/uppercase.man new file mode 100644 index 0000000..aedbd45 --- /dev/null +++ b/man/uppercase.man @@ -0,0 +1,56 @@ +.TH uppercase "" "" "/rdb Command" +.PC "Convert input to all upper-case characters" +\fBuppercase [\fIstring ...\fB] [< \fIfile\fB]\fR +.PP +.II "string^convert to uppercase" +.II "convert string to uppercase" +The command +.B uppercase +converts to to upper-case characters every letter in +.IR string . +If you do not pass it a +.IR string , +it reads the standard input. +It uses the COHERENT command +.B tr +to perform conversion. +.SH Example +Here we convert a string to upper case: +.DM + uppercase little words +.DE +.PP +This displays +.DM + LITTLE WORDS +.DE +.PP +on the standard output. +.PP +If the file +.B lowers +contains the text +.DM + these are all lowercase. +.DE +.PP +then the command +.DM + uppercase < lowers +.DE +.PP +prints the following on the standard output: +.DM + THESE ARE ALL LOWERCASE. +.DE +.SH "See Also" +.B +cap, +lowercase +.R +.br +COHERENT Lexicon: +.B +tr +.R +.SO uppercase diff --git a/man/validate.man b/man/validate.man new file mode 100644 index 0000000..f0ab4ed --- /dev/null +++ b/man/validate.man @@ -0,0 +1,146 @@ +.TH validate "" "" "/rdb Command" +.PC "Find invalid data" +\fBvalidate '\fIpattern \fB{ \fIaction \fB} ...' < \fItableorlist\fR +.PP +.II "table^validate" +.II "data^validate" +The command +.B validate +checks your tables and a list of invalid data. +It also prints messages of your choosing, and shows +you the line that caused the error. +It is almost identical to a combination of the commands +.B row +and +.BR compute . +.PP +.B validate +passes your commands to +.B awk +after it has converted your column names to +.BR $1 , +.BR $2 , +and so on, which is how +.B awk +refers to columns. +.PP +.I pattern +is a logical condition that indicates invalidity in the data; +for example, it may give values that are negative and should not be, +or greater than they should be. +.I action +is what to do about it; +usually, this is to print a message. +.SH Example +The first example is simple. +Once again, consider table +.BR inventory , +which is as follows: +.DM +Item# Onhand Cost Value Description +----- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 X 500 test tubes + 3 -5 80 -400 clamps + 4 23 19 437 plates + 5 -99 24 -2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +Several invalid values have crept into this table. +You might wish to check that no item in column +.B Onhand +is less than zero. +To do so, type the command: +.DM +validate 'Onhand < 0 {print "negative Onhand in line " NR}' < badinventory +.DE +.PP +This prints the following on the standard output: +.DM +Item# Onhand Cost Value Description +----- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves + 2 100 X 500 test tubes +negative Onhand in line 3 + 3 -5 80 -400 clamps + 4 23 19 437 plates +negative Onhand in line 5 + 5 -99 24 -2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +The variable +.B NR +means number of record or row. +This variable is built into +.BR awk , +which +replaces it with the number of the line it is processing. +.PP +The next example is complex. +Note that if you wish, you can write a command line for +.B validate +into a file, to create a customized validation command. +You can put many tests and messages in one validation file. +For example, the following command file, called +.BR validateinv , +thoroughly checks table inventory: +.DM +echo validating badinventory +.sp \n(pDu +validate '{if (Onhand < 0 ) print "negative Onhand in line " NR; \e + if (Value < 0 ) print "negative Value in line " NR; \e + if (Cost ~ /[A-Za-z]/) print "letter in Cost in line " NR; }' \e + < badinventory +.DE +.PP +To validate table +.BR inventory , +just type: +.DM + validating badinventory +.DE +.PP +This prints the following on the standard output: +.DM +Item# Onhand Cost Value Description +----- ------ ---- ----- -------------- + 1 3 50 150 rubber gloves +letter in Cost in line 2 + 2 100 X 500 test tubes +negative Onhand in line 3 +negative Value in line 3 + 3 -5 80 -400 clamps + 4 23 19 437 plates +negative Onhand in line 5 +negative Value in line 5 + 5 -99 24 -2376 cleaning cloth + 6 89 147 13083 bunsen burners + 7 5 175 875 scales +.DE +.PP +You may wish to redirect the output of +.B validate +to a file, bring it into your editor, and +correct the errors and remove the error lines as you go. +To make it easier to find those error lines in +the editor, you could have all error lines contain a string +that is easy to find, such as +.BR ERROR . +.PP +If you only want to see the error lines, use the command +.BR row . +.SH "See Also" +.SH "Logical Conditions" +.B +compute, +row +.R +.br +COHERENT Tutorials: +.B +Introduction to the awk Language +.R diff --git a/man/ve.man b/man/ve.man new file mode 100644 index 0000000..a5614c0 --- /dev/null +++ b/man/ve.man @@ -0,0 +1,381 @@ +.TH ve "" "" "/rdb Command" +.PC "Visually edit a table" +\fBve [\fItable [\-n[\fIn\fB] \-f\fIc\fB \-m\fIc\fB \-i \-s [\fIfile\fB] \-h [\fIfile\fB] \-a [\fIfile\fB] \-v [\fIfile\fB]]][\-b]\fR +.PP +.II "editor^ve" +.II "form^editor" +.II "table^editor" +\fBve\fR is an editor that is designed to edit tables +in a multi-user environment. +Because it is designed to work with tables, it understands rows and columns; +and because it is designed for a multi-user environment, it knows how to +lock records and perform other useful tasks to protect the integrity of +of your data. +.PP +.B ve +defines a row, or \fIrecord\fR, as containing a fixed number of columns, +or \fIfields\fR. +It defines a field as being a string delimited by an exclusive +\fIcolumn separation character\fR. +Normally, the first two rows in a \fBve\fR table are \fIheader +records\fR that describe the columns in each row. +Each field in the first header record names its column; +fields in the second header record are hyphens `-'. +When you invoke \fBve\fR +without an argument, +it displays a menu of on-line \fBve\fR help topics and instructions. +.PP +\fItable\fR is the table you wish to edit. +When you name a +\fItable\fR on the command line, all other arguments must follow it. +If +.I table +does not exist, +\fBve\fR create sthe necessary header records from a screen file +or a header table. +.SH Options +.B ve +recognizes the following options: +.IP \fB\-b\fR +Use the terminal's highlighting capability. +If specified, \fBve\fR does its best to emphasize +non-data text and the on-line help file's keywords by displaying them +in high intensity. +This may produce odd results if the entry in file +\fB/etc/termcap\fR for your terminal is incorrect or missing, +or if your terminal type is set incorrectly. +.IP \fB-n\fIn\fR +Use automatic record numbering. +\fBve\fR assumes that the data in the first column of each record are +unique numbers, and keeps track of the largest value in this column. +As you add new records to the table, +.B ve +increments this number and stores it in the first column of the new record. +.sp \n(pDu +When you specify \fIn\fR, \fBve\fR begins numbering records at that value. +For example, \fB-n2001\fR means, +``Use automatic record numbering and begin the numbers at 2001.'' +.IP \fB-f\fIc\fR +Use the character \fIc\fR to delimit columns +instead of the default tab character. +With the exception of the screen file, all files associated with +the \fBve\fR table \fImust\fR use the same column separation +character. +.IP \fB-m\fIc\fR +Invoke \fBve\fR in mode \fIc\fR, where +.I c +is one of the following: +.DM + \fBi\fR Insert mode + \fBn\fR Next mode + \fB/\fR Search mode +.DE +.sp \n(pDu +\fBve\fR returns to this mode after each successful \fIwrite\fR. +Invoking +.B ve +in insert mode is convenient when entering data. +.IP \fB\-i\fR +Initialize the table. +\fBve\fR maintains a private sequential index of all records in a table, +so it can track the records in use and the records that have been deleted +during an editing session. +It also uses this index to clean up the table when the edit session is over. +If the table is modified by a program other than \fBve\fR, then you must use +the \fB\-i\fR switch during the next \fBve\fR edit session to +reinitialize its private index. +You can use this index for fast searching by record number +if each record has a number and no records are ever deleted. +.sp \n(pDu +If the current directory contains any files +that follow \fBve\fR's naming convention \fBdata-\fIc\fR, +where \fI-c\fR is a file-option switch (\fB\-s\fR, \fB\-h\fR, \fB\-a\fR, +\fB\-v\fR), +.B ve +incorporates them automatically into the command line before it +processes the argument list. +You must name on the command line all +files that not named according to this convention; +please note that this overrides the inclusion of any default names +in the command line. +.IP "\fB-s \fIfile\fR" +Tell \fBve\fR to use \fIfile\fR as the screen file, +which contains a template of how you want the screen to appear. +The entire screen file should not exceed 23 lines. +\fBve\fR scans the screen file for column names enclosed between angle +brackets (\fB<\^>\fR) and displays the data in those columns between the +brackets. +Both brackets must be on the same line. +If +.I file +is used to initialize the table, +all column names must be listed; otherwise, excluding a column name from +a template also keeps data in those columns from being viewed, +sought, or modified. +If a column name is preceded by an exclamation point, +.B ve +lets you search for that column's data and display them, +but no user can modify them. +.IP "\fB-h \fIfile\fR" +The header records are in +.IR file . +If no +.I file +is named, +.B ve +assumes that the header records are in file +.BR data-h . +\fBve\fR can create a header file only when the table +is initialized with a screen file. +.IP "\fB-a \fIfile\fR" +If the audit table does not exist, tell \fBve\fR to create it and write it +into +.IR file . +If you do not name +.IR file , +.B ve +writes the audit table into +.BR data-a . +.sp \n(pDu +\fBve\fR audits a table by recording each addition, +modification, and deletion you make to it, +and storing this information in its audit table. +The audit table looks like the table, +with the addition of two columns in the beginning of each record: +the \fItime stamp\fR column and the \fImodification code\fR column. +.sp \n(pDu +The first column of each row in the table must be a unique number +so that rows can be tracked accurately. +\fBve\fR manages these numbers and assigns new ones to the rows +you add. +If the table already exists, +\fBve\fR begins record numbering at the largest record number in the table plus +one; for new tables, \fBve\fR begins record numbering at one. +.sp \n(pDu +Each time you add a record (code \fBa\fR), change a record (code \fBc\fR), +or delete one (code \fBd\fR), +\fBve\fR records in the audit table the date and time, the modification code, +and the number of the record. +When you change or delete a record, +.B ve +also stores it in the audit table the data that the record contained before +you changed or deleted it. +.IP "\fB-v \fIfile\fR" +Use +.I file +as the validation table. +If you do not name +.IR file , +.B ve +uses +.B data-v +by default. +If the validation table does not exist, +.B ve +creates it, +then uses itself to edit the new validation table so you +can enter validation requirements for desired columns +before you edit the table. +.PP +When you include a validation table in the command line, +\fBve\fR checks the data as it is entered into columns, +which are specified in the validation table, +to make sure they meet certain requirements. +.PP +The first header record of the validation table contains +a \fIname\fR column, +a \fIcharacter\fR column, +a \fIlength\fR column, and +a \fItable look-up\fR column, +in that order. +.IP "\fIname column\fR" +This names a column from the table being validated. +.IP "\fIcharacter column\fR" +Give the valid +characters or ranges of characters for data in this column +Characters and ranges must be separated by commas (,). +Ranges or characters that are preceded by an `!' exclude +that range or character from the valid list. +For example, the validation string \fBa-z,!y,1-9,#,., ,,,\fR +means that all lower-case characters with the exception of \fIy\fR are valid, +as are numerals from 1 to 9, pound symbols, periods, blanks, and commas. +By contrast, the string \fB!0-9\fR excludes all numerals from the column. +.IP "\fIlength column\fR" +Set the length limit of the column, as follows: +.RS +.IP \fB<\fIn\fR +The column cannot exceed +.I n +characters in length. +.IP \fB>\fIn\fR +The column cannot be less than +.I n +characters long. +The \fB<\fR and \fB>\fR symbols can be used with each other. +For example, +\fI>10,<28\fR specifies that the length of the data in this column +must be at least 11 and not more than 27 characters long. +.IP \fB=\fIn\fR +The column can contain only +.I n +characters. +.IP \fB!\fR +Zero indicator. +This can used with any of the above symbols to indicate that the column +can also have a length of zero. +For example, \fI!,=5\fR means that the column can contain zero characters; +if it does not contain zero characters, it must contain exactly five +characters. +.RE +.IP "\fItable look-up column\fR" +Tell +.B ve +to check the data entered against the contents of an existing table. +When the name of the table is preceded by `!', data entered +in the column must \fInot\fR be in the table; otherwise they \fImust\fR +be in the table. +.sp \n(pDu +You can create an index table with the command +.BR vindex . +It names them after the column you are \fBvindex\fRing, which is +called the \fIkey column\fR. +If the name of the key column is longer than 11 characters, +.B vindex +truncates the name to 11 characters. +It also converts blanks in the key column's name to `_'. +.sp \n(pDu +For example, the specification \fI!name\fR tells \fBve\fR to +check the data against a table comprised +of all text located in a column named \fIname\fR. +If the data are \fInot\fR found in the table, +then it passes validation. +.SH Commands +.\".ds EK \s-1\s+1 +.ds EK +The following table gives the \fBve\fR edit commands. +\fBve\fR uses two modes when editing a table: +\fIcommand mode\fR and \fIinsert mode\fR. +Commands that switch \fBve\fR from command to insert mode are indicated +by \*(EK. +The ESC (escape) key switches \fBve\fR from insert to command mode. +.PP +The commands \fBc\fR and \fBd\fR commands require \fItargets\fR. +A target is the destination for the command being executed. +For example, the command \fBdfa\fR means, +``delete text from the cursor +up to and including the first \fIa\fR character encountered.'' +In this case, the target command is \fIf\fR. +In the table, commands that can be used as targets for the \fBc\fR and +\fBd\fR commands are indicated by \fB(t)\fR. +.PP +Some commands recognize a number that indicates how many times it should +execute. +For example, the command \fB5l\fR means, ``Move right five characters\fR''; +the command \fB3rP\fR means, +``Replace the next three characters with the character \fIP\fR.'' +Counts may also be used with targets; for example, +command \fBc2w\fR means, +``Change the next two words.'' +Commands that recognize counts are indicated by a \fB(c)\fR. +.nf +.sp \n(pDu +.ta 0.5i 2.0i + \fICommand Description\fR +.tc . + \fBA\fItext\fR\*(EK Append text at field end + \fBa\fItext\fR\*(EK Append text after cursor + \fBB\fR Back field (c) + \fBb\fR Back word (t,c) + \fBC\fR\*(EK Change to field end + \fBc\fR\*(EK Change to target + \fBD\fR Delete to field end + \fBd\fR Delete to target + \fBdd\fR Delete record + \fBe\fR End of word (t,c) + \fBF\fIx\fR Find x left (t,c) + \fBf\fIx\fR Find x right (t,c) + \fBG\fR Go to last record (c) + \fBH\fR First field + \fBh\fR Left character (t,c) + \fBI\fItext\fR\*(EK Insert text at field start + \fBi\fItext\fR\*(EK Insert text before cursor + \fBj\fR Down field (c) + \fBk\fR Up field (c) + \fBL\fR Last field + \fBl\fR Right character (t,c) + \fBm\fR Mark search field + \fBM\fR Unmark search field + \fBn\fR Get next record + \fBO\fItext\fR\*(EK Open new record + \fBo\fItext\fR\*(EK Open new field + \fBP\fR Put yanked record + \fBp\fR Put yanked field + \fBq\fR Quit \fBve\fR + \fBR\fR\*(EK Replace + \fBr\fIx\fR Replace with \fIx\fR + \fBS\fR Redraw the screen + \fBs\fR\*(EK Substitute text (c) + \fBt\fR Table display + \fBu\fR Undo last command + \fBv\fR Validation display + \fBW\fR Write record + \fBw\fR Next word (t,c) + \fBx\fR Delete character (c) + \fBY\fR Yank current record + \fBy\fR Yank current field + \fBZZ\fR Write record & quit + \fBz\fR Field display + \fB;\fR Repeat F/f cmd (t) + \fB^\fR Start of field (t) + \fB$\fR End of field (t) + \fB/\fItext\fB\*(EK\fR Search for text + \fB/\*(EK\fR Repeat search + \fB-\fR Get preceding record + \fB%\fR Execute the shell + \fB!\fR Shell escape + \fB!!\fR Repeat shell escape + \fB>\fR Scroll field left + \fB<\fR Scroll field right + \fB#\fR Help file(s) display + RETURN Next field (c) + TAB Next field (c) + SPACE Right character(t,c) +.tc +.fi +.PP +The next table +gives two other kinds of \fBve\fR commands \(em +\fIcolon\fR commands and \fIcontrol-key\fR commands, all of which +map directly to commands in given in the previous table. +Colon commands are provided for users familiar with \fBvi\fR or \fBed\fR. +Control-key commands let you define command macros +by specifying the control key character and the command or commands +to be executed each time the control key is hit. +These definitions, or \fImacros\fR are defined in the +\fB/rdb\fR-formatted file \fB.verc\fR located in the your home directory. +If there is no \fB.verc\fR file, then the default control-key +commands apply. +If you are in insert mode when a control key is hit, +\fBve\fR attempts to restore that mode after successful execution of the macro. +.DS +.ta 1.0iC 2.0iC 3.0iC 4.0iC + \fICommand Maps to Command Maps to\fR + :h[elp] # \fB\fR # + :n[ext] n \fB\fR S + :o[pen] O \fB\fR t + :q[uit] q \fB\fR ZZ + :s[hell] \fB\fR % + :w[rite] \fB\fR W + :!cmd !cmd \fB\fR v + :!! !! \fB\fR dd +.DE +.SH "See Also" +.B +vindex +.R +.br +COHERENT Lexicon: +.B +elvis, +vi +.R diff --git a/man/vilock.man b/man/vilock.man new file mode 100644 index 0000000..6969427 --- /dev/null +++ b/man/vilock.man @@ -0,0 +1,48 @@ +.TH vilock "" "" "/rdb Command" +.PC "Lock a table before editing, unlock afterward" +\fBvilock \fItableorlist\fR +.PP +.II "table^lock while editing" +.II "vi^lock table" +.II "lock table while editing" +.II "record locking" +The command +.B vilock +stops others from editing the same file you are editing. +It locks the table, invokes the +.B vi +with which you can edit the table, then unlocks the table when you +have finished editing. +.PP +To lock +.IR tableorlist , +.B vilock +changes its name to +\fBLOCK\fItableorlist\fR; +this is a temporary file that exists only as long as you are editing. +.B vilock +then invokes +.B vi +for \fBLOCK\fItableorlist\fR. +When you finish editing and exit from +.BR vi , +.B vilock +moves \fBLOCK\fItableorlist\fR back to its original name. +If anyone else trys to edit +.I tableorlist +while you are editing it with +.BR vilock , +he receives a message that the file is locked or missing. +.SH "See Also" +.B +lock, +unlock, +ve +.R +.br +COHERENT Lexicon: +.B +elvis, +vi +.R +.SO vilock diff --git a/man/vindex.man b/man/vindex.man new file mode 100644 index 0000000..db766af --- /dev/null +++ b/man/vindex.man @@ -0,0 +1,74 @@ +.TH vindex "" "" "/rdb Command" +.PC "Create and display ve look-up tables" +\fBvindex \fItable \fB[ \fIkey-column \fB[: \fIdecode-column \fB] ... ]]\fR +.PP +.II "table^validate" +.II "validate table" +The command +.B vindex +creates and displays tables composed of columns in +.B ve +tables. +These tables are used by +.B ve +when validating column entries. +.I table +names the +.B ve +table. +When no options are specified, +.B vindex +displays the contents of all existing column tables associated with +.IR table . +.PP +For each +.IR "key-column" , +.B vindex +collects the text from that column and stores it in two files; +each is prefixed by the name of the +.I key-column +and is suffixed by +.B \-A +or +.BR \-B . +If the name of the key column +exceeds 14 characters, +the table-name prefix is truncated to 14 characters. +Key column whose names +contain blanks must be surrounded by quotation marks; +blanks are converted to underscores `_' in the table name. +These files are referred to in the fourth column of +.B ve +validation files by the table-name prefix. +.PP +When the colon `:' argument follows a key-column name, +the column name following this argument is assumed to be the name of a +.IR decode-column . +As key-column text is collected in the table, the associated +.B vindex +links the decode-column's text to it as a cross-reference. +.SH Example +The following +.B vindex +command create a look-up table for the key columns +.B Snumber +and +.B State +in table +.BR personnel ; +it also cross-references the information in +.B Snumber +with the text in the decode-column +.BR Employee : +.DM + vindex personnel "Snumber" : Employee State +.DE +.PP +The resulting tables are named +.B Snumber +and +.BR State . +.SH "See Also" +.B +ve +.R diff --git a/man/whatis.man b/man/whatis.man new file mode 100644 index 0000000..f26c523 --- /dev/null +++ b/man/whatis.man @@ -0,0 +1,48 @@ +.TH whatis "" "" "/rdb Command" +.PC "Display the command description and syntax" +\fBwhatis \fIcommand\fR +.PP +.II "help message, print" +.II "command^print summary" +The command +.B whatis +displays the description and syntax of +.IR command . +It resembles the COHERENT command +.BR help , +except that it handles +.B /rdb +commands. +.SH Example +The command +.DM + whatis column +.DE +.PP +displays the following: +.DM + column - display columns of a table in any order + column column ... < tableorlist +.DE +.PP +And the command +.DM + whatis whatis +.DE +.PP +displays: +.DM + whatis - displays the command description and syntax + whatis command +.DE +.SH "See Also" +.B +whatwill +.R +.br +COHERENT Lexicon: +.B +help, +man +.R +.SO whatis diff --git a/man/whatwill.man b/man/whatwill.man new file mode 100644 index 0000000..dc7331b --- /dev/null +++ b/man/whatwill.man @@ -0,0 +1,60 @@ +.TH whatwill "" "" "/rdb Command" +.PC "Display commands with functions in description" +\fBwhatwill \fIfunction\fR +.PP +.II "command^print syntax" +The command +.B whatwill +displays the description and syntax of the command that performs +.IR function . +It resembles the Berkeley UNIX command +.BR appropos . +It also resembles the +.B /rdb +command +.B whatis +except that it seeks +.I function +anywhere in the name of the command, the description, and the syntax. +For example, if you ask for +.B total, +.B whatwill +returns both +.B total +and +.BR subtotal . +.SH Example +The first command looks for information about columns: +.DM + whatwill column +.DE +.PP +This returns: +.DM + column - display columns of a table in any order + column column ... < table +.DE +.PP +And the command +.DM + whatwill total +.DE +.PP +returns: +.DM + subtotal - outputs the subtotal of columns in a table + subtotal [ -l ][ break-column column ...] < table + total - sums up each column selected and displays + total [ -l ][ column ... ] < table +.DE +.SH "See Also" +.B +whatis +.R +.br +COHERENT Lexicon: +.B +help, +man +.R +.SO whatwill diff --git a/man/widest.man b/man/widest.man new file mode 100644 index 0000000..6a51fbc --- /dev/null +++ b/man/widest.man @@ -0,0 +1,39 @@ +.TH widest "" "" "/rdb Command" +.PC "Output the width of the widest entries in a table" +\fBwidest < \fItable\fR +.PP +.II "column^find widest entry" +.II "column^find widest rows" +The command +.B widest +reads +.I table +and writes to the standard output a second table +that gives the width of the widest entry in each column. +.SH Example +The command +.DM + widest < inventory +.DE +.PP +reads table +.B inventory +and writes something like the following to the standard output: +.DM +Item Amount Cost Value Description +---- ------ ---- ----- -------------- +4 6 4 5 14 +.DE +.PP +Each number is the width, in characters, +.BR inventory 's +the widest entry in that column. +You can use this information for formatting. +.B /rdb +does not require a schema because it gets the parameters +of the tables from the tables themselves. +.SH "See Also" +.B +width +.R +.SO widest diff --git a/man/width.man b/man/width.man new file mode 100644 index 0000000..ac4e50e --- /dev/null +++ b/man/width.man @@ -0,0 +1,26 @@ +.TH width "" "" "/rdb Command" +.PC "Display the width of each column" +\fBwidth [ \-l ] [ \fIcolumn ... \fB] < \fItable\fR +.PP +.II "column^print maximum width" +The command +.B width +displays the maximum width of each +.I column +in +.IR table . +If no column is named, it returns this information for every column in +.IR table . +The option +.B \-l +displays the entire table as well as information about the maximum width +in each +.IR column . +.SH Example +For an example of this command, see the entry for +.BR total . +.SH "See Also" +.B +total, +widest +.R diff --git a/man/word.man b/man/word.man new file mode 100644 index 0000000..56bd7ca --- /dev/null +++ b/man/word.man @@ -0,0 +1,73 @@ +.TH word "" "" "/rdb Command" +.PC "Convert text file into list of unique words" +\fBword < \fItext \fB> \fItable\fR +.PP +.II "text^find unique words" +The command +.B word +reads +.I text +converts it to a list of the unique words it contains, and writes the +list to the standard output. +You can use +.B word +to help construct tables for the +.B /rdb +command +.BR translate . +.SH Example +If you were to type the above paragraph into a file called +.BR FOO , +the command +.DM + word < FOO +.DE +.PP +would print the following to the standard output: +.DM +The +You +a +and +can +command +construct +contains +converts +for +help +it +list +of +output +rdb +reads +standard +tables +text +the +to +unique +use +word +words +writes +.DE +.PP +.SH "See Also" +.B +translate +.R +.br +COHERENT Lexicon: +.B +sort, +tr, +uniq +.R +.SH Notes +Because +.B word +is a shell script, you can modify it to map upper-case characters to +lower case, if you wish. +.SO word diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..422feb5 --- /dev/null +++ b/mise.toml @@ -0,0 +1,19 @@ +[tools] +ruby = "3.4" + +[env] +_.path = ["{{config_root}}/bin"] +RDB = "{{config_root}}" + +[tasks.install] +description = "Symlink every /rdb command (row, column, ...) into bin/" +run = "rdb install" + +[tasks.test] +description = "Run the rdb tests" +run = "ruby test/rdb_test.rb" + +[tasks.testall] +description = "Run the demo script and show what changed since the last known-good run" +dir = "{{config_root}}/demo" +run = "testall | diff testall.old - && echo 'testall: no differences'" diff --git a/test/rdb_test.rb b/test/rdb_test.rb new file mode 100644 index 0000000..d296b3e --- /dev/null +++ b/test/rdb_test.rb @@ -0,0 +1,481 @@ +# frozen_string_literal: true + +require_relative 'test_helper' + +class SelectionTest < RdbCase + def test_row_numeric_condition + out = rdb('row', 'Cost > 5', file: 'inventory') + assert_equal %w[3 6 7], data(out).map(&:first) + end + + def test_row_regex_and_or + out = rdb('row', 'Description ~ /burner/ || Amount < 10', file: 'inventory') + assert_equal %w[1 3 6 7], data(out).map(&:first) + end + + def test_select_is_row + assert_equal rdb('row', 'Cost > 5', file: 'inventory'), rdb('select', 'Cost > 5', file: 'inventory') + end + + def test_column_reorders_and_adds_empty + out = rdb('column', 'Cost', 'Nope', 'Item', file: 'inventory') + assert_equal %w[Cost Nope Item], out.lines.first.chomp.split("\t") + assert_equal ['5.00', '', '1'], data(out).first + end + + def test_column_without_arguments_is_identity + assert_equal read('inventory'), rdb('column', file: 'inventory') + end + + def test_compute_sprintf + out = rdb('compute', 'Value = sprintf("%5.2f", Cost * Amount)', file: 'inventory') + assert_equal '1312.75', data(out)[5][3].strip + end + + def test_sorttable_numeric_reverse + out = rdb('sorttable', '-nr', 'Cost', file: 'inventory') + assert_equal 'scales', data(out).first.last + end + + def test_sorttable_two_keys + out = rdb('sorttable', 'Account', 'Date', file: 'journal') + assert_equal %w[101 101 101], data(out).first(3).map { |r| r[1] } + end + + def test_record + assert_equal ['3', '5', '8.00', '40.00', 'clamps'], data(rdb('record', '3', file: 'inventory')).first + end + + def test_number + out = rdb('number', file: 'inventory') + assert_equal %w[number Item], out.lines.first.split("\t").first(2).map(&:chomp) + assert_equal '7', data(out).last.first + end + + def test_rename_addcol_headoff_headon + assert_equal 'Qty', rdb('rename', 'Amount', 'Qty', file: 'inventory').lines.first.split("\t")[1] + assert_equal 'Extra', rdb('addcol', 'Extra', file: 'inventory').lines.first.chomp.split("\t").last + assert_equal 7, rdb('headoff', file: 'inventory').lines.size + assert_equal %w[a b], rdb('headon', 'a', 'b', stdin: "1\t2\n").lines.first.chomp.split("\t") + end +end + +class SetsTest < RdbCase + def test_union_intersect_difference + assert_equal 6, data(rdb('union', 'carexpense', 'expense')).size + assert_equal [%w[820113 101.62 car v car\ repairs], ['820114', '81.80', 'insur', 'c', 'car insurance allstate']], + data(rdb('intersect', 'carexpense', 'expense')) + assert_equal ['meal'], data(rdb('difference', 'expense', 'carexpense')).map { |r| r[2] } + end + + def test_union_with_stdin_dash + out = rdb('union', '-', 'carexpense', file: 'expense') + assert_equal '820107', data(out).first.first + end + + def test_jointable_natural_and_outer + write('owners', "Item\tOwner\n----\t-----\n1\tann\n3\tbob\n9\tzed\n") + inner = data(rdb('jointable', 'inventory', 'owners')) + assert_equal %w[ann bob], inner.map(&:last) + left = data(rdb('jointable', '-a1', 'inventory', 'owners')) + assert_equal 7, left.size + right = data(rdb('jointable', '-a2', 'inventory', 'owners')) + assert_equal 'zed', right.last.last + end + + def test_paste_and_split + out = rdb('paste.rdb', 'expense', 'carexpense') + assert_equal 10, out.lines.first.split("\t").size + rdb('splittable', '-4', 'inventory') + assert_equal 4, data(read('inventoryaa')).size + assert_equal 3, data(read('inventoryab')).size + end +end + +class SummaryTest < RdbCase + def test_total_mean_max_min + assert_equal '324', data(rdb('total', 'Amount', file: 'inventory')).first[1] + assert_equal '175.00', data(rdb('maximum', 'Cost', file: 'inventory')).first[2] + assert_equal '0.50', data(rdb('minimum', 'Cost', file: 'inventory')).first[2] + assert_equal '46.29', data(rdb('mean', 'Amount', file: 'inventory')).first[1] + end + + def test_datatype_width_precision_widest + assert_equal %w[0 0 2 2 -1], data(rdb('datatype', file: 'inventory')).first + assert_equal '14', data(rdb('width', 'Description', file: 'inventory')).first.last + assert_equal '2', data(rdb('precision', 'Cost', file: 'inventory')).first[2] + assert_equal %w[4 6 6 7 14], data(rdb('widest', file: 'inventory')).first + end + + def test_total_l_prints_table_too + out = rdb('total', '-l', 'Amount', file: 'inventory') + assert_equal 11, out.lines.size + end + + def test_subtotal + out = data(rdb('subtotal', 'Date', 'Debit', 'Credit', file: 'journal')) + assert_equal [%w[861222 25000 25000], %w[861223 10000 10000], %w[861224 30000 30000]], + out.map { |r| [r[0], r[2], r[3]] } + end + + def test_schema_dbdict + out = data(rdb('schema', 'inventory')) + assert_equal %w[inventory 5 Description], out.last + assert_equal %w[5 Description], data(rdb('dbdict', file: 'inventory')).last + end + + def test_fd + assert_equal "true\n", rdb('fd', 'Item', 'Cost', file: 'inventory') + out, _e, st = run3('fd', 'Amount', 'Cost', stdin: read('inventory')) + assert_equal "false\n", out + refute st.success? + end +end + +class FormatTest < RdbCase + def test_justify_aligns_decimals + out = data(rdb('justify', file: 'inventory')) + assert_equal ' 14.75', out[5][2] + assert_equal '175.00', out[6][2] + assert_equal ' 1', out[0][0] + end + + def test_compresss_pad_rmblank_blank + assert_equal data(read('inventory').then { |s| rdb('compresss', stdin: rdb('justify', stdin: s)) }), + data(read('inventory')) + assert_equal 'rubber gloves' + ' ' * 70, data(rdb('pad', file: 'inventory')).first.last + t = "A\tB\n-\t-\n1\t2\n \t \n3\t4\n" + assert_equal 2, data(rdb('rmblank', stdin: t)).size + assert_equal [' '], data(rdb('blank', stdin: "A\n-\nab\n")).flatten + end + + def test_trim_squeezes_columns + out = rdb('trim', file: 'inventory').lines + assert_equal 'Item Amount Cost Value Description ', out.first.chomp + end + + def test_case_commands + assert_equal "Rubber Gloves\n", rdb('cap', stdin: "rubber gloves\n") + assert_equal "BIG WORDS\n", rdb('uppercase', 'big', 'words') + assert_equal "big words\n", rdb('lowercase', 'BIG', 'WORDS') + assert_equal "here is the good stuff\n", rdb('trimblank', stdin: " here is the good stuff \n") + end + + def test_padstring_length_ascii_chr + assert_equal " string\n", rdb('padstring', '10', 'string') + assert_equal "string \n", rdb('padstring', '-10', 'string') + assert_equal "11\n", rdb('length', 'word1 word2') + assert_equal "97 65 49 \n", rdb('ascii', 'aA1') + assert_equal "aA1\n", rdb('chr', '97', '65', '49') + end + + def test_see_shows_tabs_and_ends + assert_equal "a^Ib$\n", rdb('see', stdin: "a\tb\n") + end + + def test_insertdash + assert_equal "A\tBB\n-\t--\n1\t2\n", rdb('insertdash', stdin: "A\tBB\n1\t2\n") + end + + def test_word + assert_equal "I\nlove\nyou\n", rdb('word', stdin: "I love you love\n") + end +end + +class ConvertTest < RdbCase + def test_list_round_trip + assert_equal read('inventory'), rdb('listtotable', stdin: rdb('tabletolist', file: 'inventory')) + end + + def test_commands_accept_lists + out = rdb('column', 'Name', 'City', file: 'maillist') + assert out.start_with?("\n"), 'a list in gives a list out' + assert_includes out, "City\tMemphis" + assert_includes rdb('row', 'Number == 2', file: 'maillist'), 'Chiquita' + end + + def test_listtosh + out = rdb('listtosh', stdin: "\nA\tx y\nB\tit's\n") + assert_equal "A='x y'\nB='it'\\''s'\n", out + end + + def test_tabletofix_and_back + fixed = rdb('tabletofix', 'Item=3r', 'Description=14', file: 'inventory') + assert_equal " 1rubber gloves \n", fixed.lines.first + t = rdb('fixtotable', 'Item=1,3', 'Description=4,14', stdin: fixed) + assert_equal ['1', 'rubber gloves'], data(t).first + end + + def test_tabletostruct + out = rdb('tabletostruct', 'Table', 'table', stdin: "A\tB\tC\n-\t-\t-\n1\t2\t3\n") + assert_includes out, "struct Table\n{\nchar *A;" + assert_includes out, '{ "1","2","3" }' + end + + def test_prolog_and_translation_formats + assert_equal "parent(Sally,Fred).\nparent(Mike,Fred).\nparent(Fred,Jane).\nfemale(Jane).\nfemale(Sally).\n", + rdb('tabletofact', 'parent', 'female') + assert_equal "mother(X,Y) :- female(X), parent(X,Y).\n", rdb('tabletorule', 'ruletable') + assert_equal "define(I,Ich)\ndefine(love,liebe)\ndefine(you,dich)\n", rdb('tabletom4', file: 'ed.t') + assert_equal "s/I/Ich/g\ns/love/liebe/g\ns/you/dich/g\n", rdb('tabletosed', file: 'ed.t') + end + + def test_translate + write('ed', rdb('tabletom4', file: 'ed.t')) + assert_equal "Ich liebe dich.\n", rdb('translate', 'ed', stdin: "I love you.\n") + end + + def test_tabletotbl + assert_includes rdb('tabletotbl', file: 'inventory'), ".TS\nn n n n l." + end + + def test_dates + assert_equal "890515\n", rdb('computedate', '890331', '45') + assert_equal "890201\n", rdb('computedate', '890132', '0') + assert_equal '1752827', data(rdb('julian', 'Date', file: 'journal')).first.first + assert_equal '861222', data(rdb('gregorian', 'Date', stdin: rdb('julian', 'Date', file: 'journal'))).first.first + assert_equal '22/12/86', data(rdb('gregorian', '-e', 'Date', stdin: rdb('julian', 'Date', file: 'journal'))).first.first + assert_match(/\A\d{6}\n\z/, rdb('todaysdate')) + end + + def test_soundex_and_like + assert_equal 'A352', data(rdb('soundex', stdin: "Name\n----\nAdams\n")).first[1] + assert_equal 'R163', data(rdb('soundex', stdin: "Name\n----\nRobert\n")).first[1] + write('name.x', rdb('soundex', file: 'name')) + assert_equal %w[Mann Moon], data(rdb('like', 'Mann', 'name')).flatten.uniq.sort + end + + def test_hashkey + assert_equal "2\n", rdb('hashkey', '11', 'ABCD') + assert_equal "4\n", rdb('hashkey', '29', 'abcdefg') + end +end + +class FastAccessTest < RdbCase + # Same rows come back whichever method finds them. + %w[b h i s].each do |m| + define_method("test_search_method_#{m}") do + rdb('index', "-m#{m}", 'inventory', 'Description') unless m == 's' + out = rdb('search', "-m#{m}", 'inventory', 'Description', stdin: "scales\nclamps\n") + assert_equal %w[scales clamps].sort, data(out).map(&:last).sort + end + end + + def test_record_number_method + rdb('index', '-mr', 'inventory') + assert_equal 'clamps', data(rdb('search', '-mr', 'inventory', stdin: "3\n")).first.last + end + + def test_partial_match + rdb('index', '-mi', '-x', 'inventory', 'Description') + out = rdb('search', '-mi', '-x', 'inventory', 'Description', stdin: "cl\n") + assert_equal %w[clamps cleaning\ cloth], data(out).map(&:last).sort + end + + def test_keys_from_a_table_and_no_head + out = rdb('search', '-h', 'inventory', 'Description', stdin: "Description\n-----------\nscales\n") + assert_equal "7\t5\t175.00\t875.00\tscales\n", out + end + + def test_seek_reports_offsets + out = rdb('seek', 'inventory', 'Item', stdin: "5\n") + from, to = out.split.map(&:to_i) + assert_equal "5\t99\t2.45\t242.55\tcleaning cloth\n", read('inventory').byteslice(from, to - from) + end + + def test_delete_blanks_in_place_and_keeps_offsets + size = File.size(File.join(@dir, 'inventory')) + rdb('delete', 'inventory', 'Item', stdin: "4\n") + assert_equal size, File.size(File.join(@dir, 'inventory')) + assert_equal '', read('inventory').lines[5].delete("\t \n") + assert_equal 6, data(rdb('rmblank', file: 'inventory')).size + end + + %w[r i h].each do |m| + define_method("test_delete_updates_index_#{m}") do + rdb('index', "-m#{m}", 'inventory', 'Item') + rdb('delete', "-m#{m}", 'inventory', 'Item', stdin: "2\n") + key = m == 'r' ? "2\n" : "2\n" + assert_equal '', rdb('search', "-m#{m}", '-h', 'inventory', 'Item', stdin: key) if m != 'r' + assert_equal 3, data(rdb('search', "-m#{m}", 'inventory', 'Item', stdin: "3\n")).first.first.to_i if m != 'r' + end + end + + def test_append_updates_each_index + %w[i h r].each do |m| + rdb('index', "-m#{m}", 'inventory', 'Item') + rdb('append', '-h', "-m#{m}", 'inventory', 'Item', stdin: "#{8 + %w[i h r].index(m)}\t1\t1.00\t1.00\tnew #{m}\n") + end + assert_equal 'new i', data(rdb('search', '-mi', 'inventory', 'Item', stdin: "8\n")).first.last + assert_equal 'new h', data(rdb('search', '-mh', 'inventory', 'Item', stdin: "9\n")).first.last + assert_equal 'new r', data(rdb('search', '-mr', 'inventory', stdin: "10\n")).first.last + end + + def test_append_with_head_line + rdb('append', 'inventory', stdin: "Item\tAmount\tCost\tValue\tDescription\n----\t------\t----\t-----\t-----------\n8\t1\t1\t1\tnew\n") + assert_equal 8, data(read('inventory')).size + end + + def test_replace_in_place_pads_last_column + out = rdb('seek', 'inventory', 'Item', stdin: "3\n") + from, to = out.split.map(&:to_i) + size = File.size(File.join(@dir, 'inventory')) + rdb('replace', 'inventory', from.to_s, to.to_s, stdin: "3\t6\t8.00\t48.00\tclamp\n") + assert_equal size, File.size(File.join(@dir, 'inventory')) + assert_equal '6', data(rdb('search', 'inventory', 'Item', stdin: "3\n")).first[1] + end + + def test_replace_too_big_moves_record_to_end_and_fixes_index + rdb('index', '-mi', 'inventory', 'Item') + from, to, xf, xt = rdb('seek', '-mi', 'inventory', 'Item', stdin: "3\n").split.map(&:to_i) + big = "3\t5\t8.00\t40.00\t#{'a very long description ' * 3}\n" + rdb('replace', '-mi', 'inventory', from.to_s, to.to_s, xf.to_s, xt.to_s, stdin: big) + hit = rdb('search', '-mi', 'inventory', 'Item', stdin: "3\n") + assert_includes hit, 'a very long description' + assert_equal 1, data(hit).size + assert_equal 'inventory', 'inventory' # the blanked old slot stays, so offsets of other rows are unchanged + end + + def test_replace_with_a_no_append_flag_fails_when_too_big + from, to = rdb('seek', 'inventory', 'Item', stdin: "3\n").split.map(&:to_i) + _o, _e, st = run3('replace', '-a', 'inventory', from.to_s, to.to_s, stdin: "3\t5\t8\t40\t#{'x' * 80}\n") + refute st.success? + end + + def test_lock_and_unlock + _o, _e, st = run3('lock', 'inventory', '111', '10', '20', '0', '0') + assert st.success? + _o, e, st = run3('lock', 'inventory', '222', '15', '25', '0', '0') + refute st.success? + assert_match(/locked by process 111/, e) + assert run3('lock', 'inventory', '222', '30', '40', '0', '0').last.success? + run3('unlock', 'inventory', '111', '10', '20') + run3('unlock', 'inventory', '222', '30', '40') + assert run3('lock', 'inventory', '333', '15', '25', '0', '0').last.success? + run3('unlock', 'inventory', '333', '15', '25') + end + + def test_enter_appends_rows + out = rdb('enter', '-1', 'inventory', stdin: "8\n2\n3\n4\nnew thing\n") + assert_includes out, 'Item: ' + assert_equal 'new thing', data(read('inventory')).last.last + end +end + +class CheckingTest < RdbCase + def test_check_rdb_finds_short_rows + _o, e, st = run3('check.rdb', stdin: "A\tB\n-\t-\n1\t2\n3\n") + refute st.success? + assert_match(/Columns \(1\) do not equal headline columns \(2\)/, e) + assert run3('check.rdb', stdin: read('inventory')).last.success? + end + + def test_domain + out = rdb('domain', 'colors', stdin: rdb('column', 'Colors', file: 'orders')) + assert_equal %w[farble yellow], data(out).flatten + assert_equal "x\n", rdb('domain', 'colors', 'black', 'x') + end + + def test_validate_prints_message_before_bad_row + out = rdb('validate', 'Onhand < 0 {print "negative Onhand in line " NR}', file: 'badinventory') + assert_includes out, "negative Onhand in line 3\n3\t-5" + assert_equal 5, out.scan(/rubber|test tubes|clamps|plates|cleaning/).size + end + + def test_searchtree + write('tree', "Parent\tChild\n------\t-----\nSally\tFred\nFred\tJane\n") + assert_equal "TRUE\n", rdb('searchtree', 'tree', 'Sally', 'Jane', 'Parent', 'Child') + _o, _e, st = run3('searchtree', 'tree', 'Jane', 'Sally', 'Parent', 'Child') + refute st.success? + end + + def test_not + assert_equal 255, run3('not', 'true').last.exitstatus + assert run3('not', 'false').last.success? + end +end + +class ReportTest < RdbCase + def test_report_fills_form + out = rdb('report', 'form', file: 'maillist') + assert_includes out, "Ronald McDonald\nMcDonalds\n123 Mac Attack\nMemphis, TENN 30000\nHi Ronald McDonald:" + assert_includes out, 'Hi Chiquita Banana:' + end + + def test_report_runs_commands_and_leaves_unknown_names + write('f2', "|!>|\n") + assert_equal "Ronald McDonald|Memphis\n|\nChiquita Banana|San Jose\n|\n", rdb('report', 'f2', file: 'maillist') + end + + def test_label + assert_equal "Ronald McDonald\nMcDonalds\n123 Mac Attack\nMemphis, TENN 30000\n", rdb('label', file: 'maillist').lines.first(4).join + end + + def test_screen_generates_a_script + out = rdb('screen', stdin: "Name : \nCity : \n") + assert_includes out, 'cursor 1 9 ; read Name;' + assert_includes out, 'cursor 2 9 ; read City;' + assert_includes out, 'echo "$Name' + "\t" + '$City"' + end + + def test_reportwriter_pages + out = rdb('reportwriter', '-3', 'Prices', file: 'inventory') + assert_equal 3, out.scan('Page').size + assert_includes out, 'Report Date:' + end +end + +class HelpTest < RdbCase + def test_whatis_whatwill_commands + assert_includes rdb('whatis', 'column'), " column - display columns of a table in any order\n column column [ Column ... ] < tableorlist\n" + assert_includes rdb('whatwill', 'total'), 'subtotal' + assert_operator data(rdb('commands')).size, :>, 120 + assert_operator rdb('howmany').to_i, :>, 120 + end + + def test_rdb_lists_commands_and_man_renders + assert_includes rdb('rdb'), 'jointable' + page = rdb('rdb', 'man', 'addcol') + assert_includes page, 'Add a column to a table' + assert_includes page, 'EXAMPLE' + end + + def test_termput_and_cursor + assert_equal "\e[H\e[2J", rdb('termput', 'cl') + assert_equal "\e[3;8H", rdb('cursor', '3', '8') + end + + def test_testall_runs_demo_script + out = rdb('testall') + assert_includes out, '$ row' + end +end + +class ActTest < RdbCase + def test_tax_foot_cashflow_explode_bom + assert_equal " 3369\n", rdb('tax', '20000', file: 'X83') + assert_equal %w[10001 2], data(rdb('explode', '10001', '2', 'parttable')).first + assert_equal '52', data(rdb('explode', '10001', '2', 'parttable')).last.last + assert_equal '1062', data(rdb('cashflow', file: 'cash'))[2][2] + end + + def test_adjust_balance_close + write('trialbalance', "Account\tDebit\tCredit\tFormat\tTaxline\tShort\tName\n-------\t-----\t------\t------\t-------\t-----\t----\n1010\t11178\t\tB-A\t4L01\tcash\tCash\n3010\t\t13500\tI-I\t101a\tsales\tSales\n") + write('journaladjust', "Account\tDebit\tCredit\tFormat\tTaxline\tName\n-------\t-----\t------\t------\t-------\t----\n1010\t100\t\tB-A\t4L01\tCash\n") + rdb('adjust') + assert_equal %w[1010 11278], data(read('adjustedtrial')).first.first(2) + rdb('balance') + assert_includes read('balancesheet'), 'Total Assets' + rdb('close') + assert_includes read('journalnext'), 'brought forward' + end + + def test_chartdup + write('chart', "Account\tShort\n-------\t-----\n1\ta\n1\tb\n2\ta\n") + _o, err, st = run3('chartdup') + out, = run3('chartdup') + assert_includes out, 'Duplicate Account numbers in chart' + assert_includes out, 'Duplicate Short names in chart' + assert st.success?, err + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..73fb758 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require 'open3' +require 'tmpdir' +require 'fileutils' + +ROOT = File.expand_path('..', __dir__) +BIN = File.join(ROOT, 'bin') +DEMO = File.join(ROOT, 'demo') + +# Each test runs in a scratch copy of demo/, so commands that change files +# (append, delete, replace ...) never touch the originals. +class RdbCase < Minitest::Test + def setup + @dir = Dir.mktmpdir('rdb-test') + FileUtils.cp_r(Dir[File.join(DEMO, '*')], @dir) + end + + def teardown + FileUtils.remove_entry(@dir) + end + + # Run a command through its symlink; returns [stdout, stderr, status]. + def run3(cmd, *args, stdin: '', env: {}) + Open3.capture3({ 'PATH' => "#{BIN}:#{ENV['PATH']}" }.merge(env), File.join(BIN, cmd), *args, + stdin_data: stdin, chdir: @dir) + end + + def rdb(cmd, *args, stdin: nil, file: nil) + stdin = File.read(File.join(@dir, file)) if file + out, err, st = run3(cmd, *args, stdin: stdin.to_s) + assert st.success?, "#{cmd} #{args.join(' ')} failed: #{err}" + out + end + + # Output as rows of cells, without the head and dash lines. + def data(out) + out.lines.drop(2).map { |l| l.chomp.split("\t", -1) } + end + + def read(name) + File.read(File.join(@dir, name)) + end + + def write(name, text) + File.write(File.join(@dir, name), text) + end +end