#!/usr/bin/env ruby # ratexdb Version 0.14 # Database Access in LaTeX # Copyright (C) 2007-2010 Robin Höns, Integranova GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # # For more information see the web pages # # Robin Höns: http://www.hoens.net/robin # Integranova and the Programming Machine: # http://www.programmiermaschine.de # http://www.care-t.com require 'dbi' require 'FileUtils' $filedebug = nil # primitive debug output routine def debugoutput line if $filedebug $filedebug.puts "#{line}" end end def replace_latex dbtext # Replace Latex special characters in DB result #Unterstrich _ \_ #Rückstrich\Backslash \ \setminus #Dollarzeichen $ \$ #Kaufmanns-Und & \& #Raute # \# #Geschweifte Klammern { } \{ \} #Prozentzeichen % \% debugoutput "replatex rein: " + dbtext specialhash = { ?\\ => "\\\\ensuremath{\\\\backslash}", ?_ => "\\\\_", ?$ => "\\\\$", ?& => "\\\\&", ?# => "\\\\#", ?{ => "\\\\{", ?} => "\\\\}", ?~ => "\\\\~{}", ?% => "\\\\%" } result = "" for i in (0..dbtext.length) maski = specialhash[dbtext[i]] if maski result << maski else result << dbtext[i,1] end end debugoutput "replatex raus: " + result return result end def replace_sql dbtext # Replace SQL special characters in DB result # (important against SQL injection!) return dbtext.gsub("'", "''") end def check_number dbtext # This text must contain a single numeric value # (important against SQL injection!) unless dbtext =~ /\A(\+|-)?[\da-fA-F]+\Z/ raise "Numeric variable contains non-numeric value: #{dbtext}" end return dbtext end def execute_if texblock debugoutput "Pruefe if #{$select_to_handle}" debugoutput texblock selectkram = $select_hash[$select_to_handle] $select_to_handle = nil $modus_collect = false selectbefehl = selectkram[0] debugoutput "Select: #{selectbefehl}" row = $dbconn.select_one( selectbefehl ) if row # Ja, es gibt Daten zu diesem Select for nowline in texblock.split("\n") debugoutput "nowline " + nowline handle_line(nowline) end end end def execute_for_loop texblock debugoutput "Mache #{$select_to_handle} mit:" debugoutput texblock selectkram = $select_hash[$select_to_handle] $select_to_handle = nil $modus_collect = false selectbefehl = selectkram[0] variablen = selectkram[1] debugoutput "Select: #{selectbefehl}" for myvar in variablen debugoutput "Var: #{myvar}" end rows = $dbconn.select_all( selectbefehl ) for row in rows ergebnis = texblock vari = 0 while vari < variablen.length variable_regexs = variablen[vari].split("/") varname = variable_regexs[0] debugoutput "ok: " + varname if varname[0,2] != "##" raise "Variable #{varname} does not begin with ##" end varnamesql = "$$" + varname[2..-1] varnamenum = "&&" + varname[2..-1] original_db_result = row[vari].to_s current_db_result_latex = replace_latex(original_db_result) current_db_result_sql = replace_sql(original_db_result) if ergebnis.index(varnamenum) current_db_result_num = check_number(original_db_result) else current_db_result_num = original_db_result end debugoutput "latex: #{current_db_result_latex}" debugoutput "sql: #{current_db_result_sql}" debugoutput "num: #{current_db_result_num}" # Perform all Regexp replaces rei = 2 while rei < variable_regexs.length debugoutput variable_regexs[rei-1] rei_regex = Regexp.new( variable_regexs[rei-1] ) debugoutput rei_regex rei_replace = variable_regexs[rei] debugoutput rei_replace current_db_result_latex = current_db_result_latex.gsub(rei_regex, rei_replace) current_db_result_sql = current_db_result_sql.gsub(rei_regex, rei_replace) current_db_result_num = current_db_result_num.gsub(rei_regex, rei_replace) debugoutput "latex: #{current_db_result_latex}" debugoutput "sql: #{current_db_result_sql}" debugoutput "num: #{current_db_result_num}" rei += 2 end # Handle the three variable flavours ergebnis = ergebnis.gsub(varname, current_db_result_latex) ergebnis = ergebnis.gsub(varnamesql, current_db_result_sql) if ergebnis.index(varnamenum) ergebnis = ergebnis.gsub(varnamenum, current_db_result_num) end vari += 1 end debugoutput "Ergebnis: #{ergebnis}" for nowline in ergebnis.split("\n") handle_line(nowline) end end end def execute_defselect texblock debugoutput "defselect #{$select_to_define}" debugoutput texblock $defining_select_statement = texblock end def execute_defvariablen texblock debugoutput "defvariablen #{$select_to_define}" debugoutput texblock # remove all white space from variable list varliste = Array.new for var in texblock.split(",") varliste << var.strip end varliste.each {|var| debugoutput "Got Var: " + var} $select_hash[$select_to_define] = [$defining_select_statement, varliste] $select_to_define = nil $modus_collect = false end def handle_line_unless_empty(line) debugoutput "handle unless empty" unless line == nil unless line.length == 0 debugoutput "Handle: " + line handle_line line else # debugoutput "line is empty" end else # debugoutput "line is nil" end end def handle_line(line) # debugoutput "Handle: " + line ## matchDef=/\A([^%]*)\\texdbdef\{##([^\}]*)\}\{(.*)\}\{(.*)\}(.*)/ matchDef=/\A([^%]*)\\texdbdef\{##([^\}]*)\}\{(.*)/ ## matchFor=/\A(.*)\\texdbfor\{##([^\}]*)\}\{(.*)\}(.*)/ matchFor=/\A([^%]*)\\texdbfor\{##([^\}]*)\}\{(.*)/ matchIf=/\A([^%]*)\\texdbif\{##([^\}]*)\}\{(.*)/ matchDbDef=/\A([^%]*)\\texdbconnection\{([^\}]*)\}(.*)/ matchDbCmd=/\A([^%]*)\\texdbcommand\{([^\}]*)\}(.*)/ if $modus_collect == false if line =~ matchDef debugoutput "Found Def!" handle_line_unless_empty($1) $select_to_define = $2 $bracedepth = 1 $modus_collect = 1 $collected_text = "" $kommando="defselect" handle_line_unless_empty($3) elsif line =~ matchFor debugoutput "Found For!" handle_line_unless_empty($1) $select_to_handle = $2 $bracedepth = 1 $modus_collect = 1 $collected_text = "" $kommando="for" handle_line_unless_empty($3) elsif line =~ matchIf debugoutput "Found If!" handle_line_unless_empty($1) $select_to_handle = $2 $bracedepth = 1 $modus_collect = 1 $collected_text = "" $kommando="if" handle_line_unless_empty($3) elsif line =~ matchDbDef debugoutput "Found DbDef!" handle_line_unless_empty($1) debugoutput "Datenbank: #{$2}" dbpar = $2.split(",") $dbconn = DBI.connect(dbpar[0], dbpar[1], dbpar[2], dbpar[3]) handle_line_unless_empty($3) elsif line =~ matchDbCmd debugoutput "Found DbCmd!" handle_line_unless_empty($1) debugoutput "Command: #{$2}" unless $dbconn raise "Please put texdbconnection before texdbcommand!" end sth = $dbconn.execute($2) sth.finish handle_line_unless_empty($3) else $fileout.puts "#{line}" end else # wir sammeln Zeilen if line == nil linelength = 0 else linelength = line.length end # debugoutput linelength i = 0 backslashgelesen = 0 while i < linelength && $bracedepth > 0 currchar = line[i] # debugoutput "curr #{currchar} backsl #{backslashgelesen}" if currchar == ?\\ if backslashgelesen == 0 backslashgelesen = 1 else backslashgelesen = 0 end elsif currchar == ?{ if backslashgelesen == 0 $bracedepth += 1 debugoutput "Tiefe: #{$bracedepth}" else debugoutput "kein ++, Backslash!" end backslashgelesen = 0 elsif currchar == ?} if backslashgelesen == 0 $bracedepth -= 1 debugoutput "Tiefe: #{$bracedepth}" else debugoutput "kein --, Backslash!" end backslashgelesen = 0 else backslashgelesen = 0 end i += 1 end # while debugoutput "Zeile Tiefe: #{$bracedepth}" $collected_text << line[0, i] i -= 1 if $bracedepth == 0 # chop, um } wegzuschmeißen if $kommando == "for" execute_for_loop $collected_text.chop $modus_collect = false elsif $kommando == "if" execute_if $collected_text.chop $modus_collect = false elsif $kommando == "defselect" execute_defselect $collected_text.chop i += 1 if i >= linelength || line[i] != ?{ raise "In texdbdef of #{$select_to_define} there is nothing allowed between the select and variable block, but I did not find the \}\{." end $modus_collect = 1 $kommando = "defvariablen" $bracedepth = 1 $collected_text = "" elsif $kommando == "defvariablen" execute_defvariablen $collected_text.chop $modus_collect = false end handle_line_unless_empty line[i+1 .. -1] end $collected_text << "\n" end end def output_help puts "Ratexdb 0.14 Copyright (C) 2007-2010 Robin Hoens, Integranova GmbH" puts "This program comes with ABSOLUTELY NO WARRANTY." puts "This is free software, and you are welcome to redistribute it" puts "under certain conditions." puts "See file COPYING for details." puts "" puts "Usage:" puts "#{$0} [-p|-l] [Parameters...]" puts "-p = pdflatex" puts "-l = latex" puts "none of the two: just produce texfile1.tex" exit end if ARGV.length == 0 output_help end arg_index = 0 before_texfile = 1 while before_texfile if ARGV[arg_index] == "-p" texbefehl = "pdflatex" resultextension = ".pdf" elsif ARGV[arg_index] == "-l" texbefehl = "latex" resultextension = ".dvi" elsif ARGV[arg_index] == "-d" $filedebug = File.new("ratexdb.txt", "w") else before_texfile = false end arg_index += 1 if ARGV.length < arg_index output_help end end arg_index -= 1 datei_rein = ARGV[arg_index] datei_raus = datei_rein.sub(/\.tex\Z/, "1.tex") if datei_raus == datei_rein raise "Sorry: File name must end on .tex. I got #{datei_rein}" end debugoutput datei_rein debugoutput datei_raus $select_to_handle = nil $select_hash = {} $variable_hash = {} $modus_collect = false cmdlinehash = {} i=1 while arg_index + i < ARGV.length cmdlinehash[i.to_s] = ARGV[arg_index + i] i+=1 end filein = File.new(datei_rein, "r") $fileout = File.new(datei_raus, "w") while (line = filein.gets) cmdlinehash.each {|key, value| line = line.gsub("##" + key, replace_latex(value)) line = line.gsub("$$" + key, replace_sql(value)) if line.index("&&" + key) line = line.gsub("&&" + key, check_number(value)) end } handle_line line.chomp end filein.close $fileout.close $dbconn.disconnect if $dbconn if texbefehl system(texbefehl, datei_raus) rauspdf = datei_raus.sub(/\.tex\Z/, resultextension) ergebnispdf = datei_rein.sub(/\.tex\Z/, resultextension) File.mv(rauspdf, ergebnispdf) end