# dbTeX.awk - a simple preprocessor for generating TeX output from
#               database files
# Version 1.1.beta (1991-02-12) dr. w. kraml, KPMG Alpen-Treuhand Wien
# see dbTeX.tex for documentation!


BEGIN { console_msg("This is dbTeX Version 1.1.beta (1991-02-12)")
       printf("%% dbTeX (1.1.beta) Output generated " ctime() "\n")
                                                                                                       # ^ PolyAwk specific!
       printf("%% dbTeX       Input file: %s\n", ARGV[1])
# a number of initialisations and defs follow here:
       FieldS = FS
       RecordS = RS
       NRincluded = 0
       example_flag = 0
       buffer = ""
       STANDARD = 0
       STRING = 1
       TEMPLATE = 2
       ID = 3
       USER = 1
     }


# main loop - do this for each line in script

{       if ($0 ~ /^%!/) {                                       # embedded dbTeX-command
           in_template = 0
           buffer = substr($0,3)
           print $0                                            # output for reference
           dbTeX_command()
       }
       else if ($0 ~ /^%_/) {                          # template contin. line
           print $0
           if (in_template) {                          # start cont. with newlines
               nl = length(template[t_name]) ? "\n" : ""
               template[t_name] = template[t_name] nl substr($0,3)
           }
           else error_msg("INVALID CONTINUATION")
       }
       else if (example_flag) {                        # embedded examples
           in_template = 0
           print "%" $0
       }
       else {
           in_template = 0
           print $0                                            # original TeX line
       }
}

END {   s = NR " line(s) input, " NRincluded " record(s) included, "
       s = s (errors+0) " error(s)"
       info_msg(s)
   }



# functions performing helpful tasks with various messages

function console_msg(s) {                                       # output to console
   print s > "/dev/tty"
   }

function error_msg(msg,     s) {                        # print error on console
   s = "% (dbTeX) Error: " msg "!"                     # and in output file
   ++errors
   print s
   console_msg(s " -> line " NR ":")
   console_msg($0)
}

function info_msg(msg,     s) {                         # print info on console
   s = "% (dbTeX) Info: " msg "."                      # and in output file
   print s
   console_msg(s)
}


# dbTeX functions

function dbTeX_command() {                                      # the dbTeX main function
   advance(ID)
   example_flag = 0
   if (tok == "examples") example_flag = 1     # %! examples
   else if (tok == "record") {                         # %! record <id> <FS> [<RS>]
       if (advance(ID)) {
           if (tok ~ /^%[0-9]+%$/) {           # data-named template
               templ_typ = 1
               templ_field = substr(tok,2,length(tok)-2)
           }
           else {                                              # script-named template
               templ_typ = 0
               templ_name = tok
           }
       }
       FieldS  = advance(STRING) ? tok : FS
       RecordS = advance(STRING) ? tok : RS
   }
   else if (tok=="template") {                         # %! template <id> <template>
       in_template = 1
       if (advance(ID))                                # get name
           t_name = tok
       if (advance(TEMPLATE))
           template[t_name] = tok
   }
   else if (tok=="break") {                            # %! break %f% <id>
       if (advance(ID)) {
           if (tok ~ /^%[0-9]+%$/)
               breakfield = substr(tok,2,length(tok)-2)
           else
               breakfield = tok
       }
       if (advance(ID))
           breaktemplate[breakfield] = tok
       if (advance(ID))
           breakmode[breakfield] = 1
       else breakmode[breakfield] = 0
   }
   else if (tok=="clearbreaks") {                      # %! clearbreaks
         for (i in oldvalue)
           delete oldvalue[i]
         for (i in breaktemplate)
           delete breaktemplate[i]
         for (i in breakmode)
           delete breakmode[i]
   }
   else if (tok=="translate") {                        # %! translate <id>
       if (advance(ID))
           set_translation(tok)
   }
   else if (tok=="change") {                           # %! change <s1> <s2>
       add_trtbl(USER)
   }
   else if (tok=="include") {                          # %! include <filename>
       if (advance(ID))
           do_include(tok)
   }
   else error_msg("UNKNOWN COMMAND")
}


function advance(type,    sep,pos) {            # get next token of type
   sub(/^[ \t]+/, "", buffer)                          # remove white space
   if (type == ID) {
       match(buffer, /^[^ \t]+/)                       # all up to white space
       tok = tolower(substr(buffer, 1, RLENGTH)) # tolower(): PolyAwk & GNU only!
       buffer = substr(buffer, RLENGTH+1)
       return RLENGTH
   }
   else if (type == TEMPLATE) {                        # rest of line
       tok = buffer
       buffer = ""
       return length(tok)
   }
   else if (type == STRING) {                          # string delimited by
       sep = substr(buffer,1,1)                        # unique character
       buffer = substr(buffer,2)
       pos = index(buffer, sep)
       if (pos)  {
           tok = substr(buffer,1,pos-1)
           buffer = substr(buffer, pos+1)
           return pos
       }
       else {
           tok = ""
           return 0
       }
   }
   else return 0
}


function add_trtbl(tbl) {                                       # add an entry to tr table
   if (advance(STRING)) {
       tmp = tok
       if (advance(STRING))
         tbl==USER ? (usr_tbl[tmp] = tok) : (std_tbl[tmp] = tok)
       else
         tbl==USER ? (usr_tbl[tmp] = "") : (std_tbl[tmp] = "")
   }
}


function set_translation(id) {                          # install translation table
   if (toupper(id) == "OFF") {
       for (i in std_tbl)
           delete std_tbl[i]
       for (i in usr_tbl)
           delete usr_tbl[i]
   }
   else {                                                                      # read translation table
       while ((status = getline line < id) > 0) {
           buffer = line
           if (buffer !~ /^#/)
               add_trtbl(STANDARD)
       }
       if (status == -1)
           error_msg("TRANSLATION TABLE " id " NOT FOUND")
   }
}


function do_include(file) {                                     # include records of <file>
   old_FS = FS; old_RS = RS
   FS = FieldS; RS = RecordS
   inc = 0
   while (getline < file > 0) {
       for (i in breaktemplate)                        # check for breaks
           if (oldvalue[i] != $i) {
               oldvalue[i] = $i
               if (inc || breakmode[i])        # no break before 1st rec.,
                   incl_(template[breaktemplate[i]])
           }                                                           # if not explicitly wanted!

       if (templ_typ)
           incl_(template[$templ_field])
       else incl_(template[templ_name])
       ++NRincluded; ++inc
   }
   FS = old_FS; RS = old_RS
   close(file)
   if (!inc) error_msg("FILE " file " NOT FOUND OR NOT ACCESSIBLE!")
   info_msg(inc " record(s) included from " file)
}


function incl_(t) {                                                     # include record with template t
   s = ""
   while (match(t, /%[0-9]+%/)) {
       l = RSTART-1
       s = s substr(t,1,l)  txlate($(substr(t,RSTART+1,RLENGTH-2)))
                                                                                       # translated data field
       t = substr(t,l+RLENGTH+1)
   }
   print s t
}


function txlate(s) {                                            # perform translations on string s
   for (target in std_tbl)
       gsub(target, std_tbl[target], s)
   for (target in usr_tbl)
       gsub(target, usr_tbl[target], s)
   return s
}