# Convert gopher lawn records to TSV format. # Version 1 # # Usage: awk -f lawn2tsv.awk gopher-lawn/db/* >lawn.tsv function encode(str) { gsub(/\t/, "\\t", str) gsub(/\r/, "\\r", str) gsub(/\n/, "\\n", str) return str } BEGIN { RS = "" FS = "\n" delete header delete seen } # Print TSV header before processing the first record NR == 1 { j = 0 for (i = 1; i <= NF; i++) { result = match($i, /^[^:]*: /) len = RLENGTH if (!result) { print "Error: Malformed line \"" $i "\"" exit -1 } name = substr($i, 1, len - 2) if (seen[name] > 0) { print "Error: Duplicate field name \"" name "\"" exit -1 } seen[name] = 1 j++ header[j] = name if (j == 1) { printf "%s", name } else { printf "\t%s", name } } print "" } # Process each gopher lawn record { delete blocks delete values name = "" for (i = 1; i <= NF; i++) { # Process blocks and line continuations result = match($i, /^ +/) if (result) { text = substr($i, RLENGTH + 1) if (length(value) == 0) { # nothing to continue yet value = text } else if (blocks[name] == 1) { # block value = value "\n" text } else { # line continuation value = value " " text } values[name] = value continue } # Process empty value with only a field name result = match($i, /^[^:]*:$/) if (result) { name = substr($i, 1, RLENGTH - 1) value = "" if (seen[name] < 1) { print "Error: Unexpected field name \"" name "\"" exit -1 } values[name] = value continue } # Process field name & value pair result = match($i, /^[^:]*: /) len = RLENGTH if (!result) { print "Error: Malformed line \"" $i "\"" exit -1 } name = substr($i, 1, len - 2) value = substr($i, len + 1) if (seen[name] < 1) { print "Error: Unexpected field name \"" name "\"" exit -1 } if (value == "\\") { blocks[name] = 1 value = "" } values[name] = value } # Print TSV row for (i = 1; i <= j; i++) { name = header[i] value = encode(values[name]) if (i == 1) { printf "%s", value } else { printf "\t%s", value } } print "" }