# Pretty-print Meal-Master recipe to HTML format
# Usage: awk -f mmhtml.awk recipe.html
# Only works with single-column Meal-Master format
# https://github.com/wedesoft/anymeal/blob/master/anymeal/html.cc
function array_size(a) {
retval = 0
for (i in a) {
retval++
}
return retval
}
function html() {
printf "\n"
printf "\n"
printf "\n"
printf "%s\n", html_encode(title)
printf "\n"
printf "\n"
printf "%s
\n", title
if (length(categories) > 0) {
printf "Categories: %s
\n", html_encode(categories)
}
if (length(yield) > 0) {
printf "Yield: %s
\n", html_encode(yield)
}
if (array_size(ingredients) > 0) {
printf "Ingredients
\n"
printf "\n"
printf "\n"
printf "| Amount | \n"
printf "Unit | \n"
printf "Ingredient | \n"
printf "
\n"
for (gsection in gsections) {
printf "\n"
printf "| %s | \n", html_encode(gsection)
printf "
\n"
ingredient_count = gsections[gsection]
for (i = 1; i <= ingredient_count; i++) {
amount = amounts[gsection, i]
unit = units[gsection, i]
ingredient = ingredients[gsection, i]
printf "\n"
printf "| %s | \n",
html_encode(amount)
printf "%s | \n",
unit_name(unit)
printf "%s | \n", html_encode(ingredient)
printf "
\n"
}
}
printf "
\n"
}
if (array_size(instructions) > 0) {
printf "Instructions
\n"
list_open = 0
par_open = 0
for (tsection in tsections) {
html_close()
if (length(tsection) > 0) {
printf "%s
\n", html_encode(tsection)
}
instruction_count = tsections[tsection]
for (i = 1; i <= instruction_count; i++) {
line = instructions[tsection,i]
if (line ~ /^[ \t]*$/) {
if (list_open || par_open) {
html_close()
}
} else if (match(line, /^[ \t]*\* /)) {
if (par_open == 1) {
html_close()
}
if (list_open == 0) {
list_open = 1
printf "\n"
}
text = substr(line, RLENGTH+1)
printf "- %s
\n", html_encode(text)
} else if (match(line, /(gemini|gopher|http|https):\/\/[^ \t]*/)) {
pre = substr(line, 0, RSTART-1)
url = substr(line, RSTART, RLENGTH)
post = substr(line, RSTART+RLENGTH+1)
printf "%s%s%s\n",
html_encode(pre),
uri_encode(url),
html_encode_both(url),
html_encode(post)
} else {
if (list_open) {
html_close()
}
if (par_open == 0) {
printf "\n"
par_open = 1
}
printf "%s\n", html_encode(trim(line))
}
}
}
html_close()
}
printf "\n"
printf "\n"
return
}
function html_close() {
if (list_open) {
printf "
\n"
list_open = 0
} else if (par_open) {
printf "
\n"
par_open = 0
}
return
}
function html_encode(str) {
retval = html_encode_entities(str)
return retval
}
function html_encode_both(str) {
retval = html_encode_entities(str)
retval = html_encode_quotes(retval)
return retval
}
function html_encode_entities(str) {
gsub(/, "<", str)
gsub(/>/, ">", str)
return str
}
function html_encode_quotes(str) {
gsub(/"/, """, str)
gsub(/'/, "'", str)
return str
}
function ingredient_parse(line) {
new_amount = substr(line, 1, 7)
new_unit = substr(line, 9, 2)
new_ingredient = substr(line, 12)
if (new_amount == " " && new_unit == " " && match(new_ingredient, /^[ \t]*- */)) {
ingredient = ingredient " " substr(new_ingredient, RLENGTH+1)
is_continuation = 1
} else {
amount = new_amount
unit = new_unit
ingredient = new_ingredient
is_continuation = 0
}
return
}
function trim(str) {
retval = str
gsub(/^[ \t]+/, "", retval)
gsub(/[ \t]+$/, "", retval)
return retval
}
function unit_name(unit) {
if (unit in names) {
retval = names[unit]
} else {
retval = unit
}
return retval
}
function unit_names_init() {
names["x "] = "per serving"
names["sm"] = "small"
names["md"] = "medium"
names["lg"] = "large"
names["cn"] = "can"
names["pk"] = "package"
names["pn"] = "pinch"
names["dr"] = "drop"
names["ds"] = "dash"
names["ct"] = "carton"
names["bn"] = "bunch"
names["sl"] = "slice"
names["ea"] = "each"
names["t "] = "teaspoon"
names["ts"] = "teaspoon"
names["T "] = "tablespoon"
names["tb"] = "tablespoon"
names["fl"] = "fluid ounce"
names["c "] = "cup"
names["pt"] = "pint"
names["qt"] = "quart"
names["ga"] = "gallon"
names["oz"] = "ounce"
names["lb"] = "pound"
names["ml"] = "milliliter"
names["cb"] = "cubic cm"
names["cl"] = "centiliter"
names["dl"] = "deciliter"
names["l "] = "liter"
names["mg"] = "milligram"
names["cg"] = "centigram"
names["dg"] = "decigram"
names["g "] = "gram"
names["kg"] = "kilogram"
return
}
function uri_encode_init() {
for (i = 0; i <= 255; i++) {
c = sprintf("%c", i)
uri_encode_ord[c] = i
uri_encode_tab[i] = c
}
# Percent encode only control characters so that the higher unicode
# block characters are left plainly visible.
for (i = 0; i < 32; i++) {
uri_encode_tab[i] = sprintf("%%%02X", i)
}
# Percent encode higher unicode block characters too
for (i = 128; i <= 255; i++) {
uri_encode_tab[i] = sprintf("%%%02X", i)
}
# SPACE
uri_encode_tab[32] = "+"
# DEL
uri_encode_tab[127] = sprintf("%%%02X", 127)
return
}
function uri_encode(str) {
len = length(str)
retval = ""
for (j = 1; j <= len; j++) {
c = substr(str, j, 1)
retval = retval uri_encode_tab[uri_encode_ord[c]]
}
return retval
}
BEGIN {
after_ingredients = 0
at_end = 0
in_gsection = 0
in_heading = 0
in_ingredients = 0
in_instructions = 0
in_list = 0
unit_names_init()
uri_encode_init()
}
{
gsub(/\r/, "")
if (NR == 1) {
if (/^(MMMMM|-----)----- Recipe via Meal-Master/) {
in_heading = 1
} else {
print "Error: Not in Meal-Master format\n"
exit 0
}
} else if (NR == 2) {
# ignore second line
} else if (in_heading) {
if (match($0, /^[ \t]+Title: /)) {
title = substr($0, RLENGTH+1)
} else if (match($0, /^[ \t]+Categories: /)) {
categories = substr($0, RLENGTH+1)
} else if (match($0, /^[ \t]+Yield: /)) {
yield = substr($0, RLENGTH+1)
} else if (/^[ \t]*$/) {
in_heading = 0
in_ingredients = 1
}
} else if (in_ingredients) {
if (match($0, /^(MMMMM|-----)-+/)) {
in_ingredients = 0
in_gsection = 1
gsection = substr($0, RLENGTH+1)
gsub(/-+$/, "", gsection)
} else if (/^[ \t]*$/) {
in_ingredients = 0
after_ingredients = 1
} else {
gsection = ""
i = gsections[gsection]
ingredient_parse($0)
if (is_continuation == 0) {
i++
gsections[gsection] = i
}
amounts[gsection,i] = amount
units[gsection,i] = unit
ingredients[gsection,i] = ingredient
}
} else if (after_ingredients) {
if (match($0, /^(MMMMM|-----)-+/)) {
after_ingredients = 0
in_gsection = 1
gsection = substr($0, RLENGTH+1)
gsub(/-+$/, "", gsection)
} else {
after_ingredients = 0
in_instructions = 1
}
} else if (in_gsection) {
if (match($0, /^(MMMMM|-----)-+/)) {
in_gsection = 1
gsection = substr($0, RLENGTH+1)
gsub(/-+$/, "", gsection)
} else if (/^[ \t]*$/) {
in_gsection = 0
after_gsection = 1
} else {
i = gsections[gsection]
ingredient_parse($0)
if (is_continuation == 0) {
i++
gsections[gsection] = i
}
amounts[gsection,i] = amount
units[gsection,i] = unit
ingredients[gsection,i] = ingredient
}
} else if (after_gsection) {
if (match($0, /^(MMMMM|-----)-+/)) {
after_gsection = 0
in_gsection = 1
gsection = substr($0, RLENGTH+1)
gsub(/-+$/, "", gsection)
} else {
after_gsection = 0
in_instructions = 1
}
}
if (in_instructions) {
if (match($0, /^(MMMMM|-----)-+/)) {
tsection = substr($0, RLENGTH+1)
gsub(/-+$/, "", tsection)
} else if (/^(MMMMM|-----)$/) {
in_instructions = 0
at_end = 1
} else if (/^[ \t]*\* /) {
in_instructions = 0
in_list = 1
} else {
tsections[tsection]++
i = tsections[tsection]
instructions[tsection,i] = $0
}
}
if (in_list) {
if (/^[ \t]*$/) {
in_list = 0
in_instructions = 1
tsections[tsection]++
i = tsections[tsection]
instructions[tsection,i] = $0
} else if (/^[ \t]*\* /) {
tsections[tsection]++
i = tsections[tsection]
instructions[tsection,i] = $0
} else {
i = tsections[tsection]
line = trim($0)
instructions[tsection,i] = instructions[tsection,i] " " line
}
}
}
END {
html()
}