#!/bin/bash
################################################################################
# sql_keywords.sh
#
# Generate sets of driver-specific keywords.
# This program generates files that can be used as part of KexiDB drivers
# that list keywords specific to that driver, i.e. words that have to be
# escaped if they are to be used as identifiers in the database.
#
# It extracts keywords from the lexer of the DB sources, deletes keywords that
# are already going to be escaped because they are part of Kexi's SQL dialect,
# and writes the resulting keywords to a "char *keywords[]" construct in a .cpp 
# file that can then be used in the driver.
#
# To use:
# Put the DB source tarballs (e.g. mysql-4.1.7.tar.gz, 
# postgresql-base-7.4.6.tar.gz) in the current directory
# then run. (Makefile provided for installs)
#
# Sed, awk, grep have been used without much thought -
# CHECK THE OUTPUT BEFORE INCLUDING IT IN A DRIVER!
#
# Martin Ellis <m.a.ellis@ncl.ac.uk>
# 11/2004

set -e
progname="sql_keywords.sh"

################################################################################
# C++ file generator
# params : array - scoped name of the array to generate
#          include - a file to include (or "" if none)
#          inFile - file containing raw keywords
#          outfile - file to write 
header () {
  local array="$1"
  local include="$2"
  local inFile="$3"
  local outFile="$4"
  echo "Writing keywords in $inFile to $outFile"
  cat <<EOF1 > "$outFile";
 /*
 * This file has been automatically generated from
 * koffice/kexi/tools/sql_keywords/$progname and
 * $inFile.
 *
 * Please edit the $progname, not this file!
 */
EOF1
  if [ "$include" != "" ] ; then
    echo "#include <$include>" >> "$outFile"
  fi
  cat <<EOF2 >> "$outFile";

namespace KexiDB {
  const char* ${array}[] = {
EOF2
}

body() {
  local inFile="$1"
  local outFile="$2"
  awk '/^[a-zA-Z_0-9]*/ { print "\t\t\""$$1"\","; } ' "$inFile" >> "$outFile"
}

footer() {
  local outFile="$1"
  cat <<EOF >> "$outFile";
		0
  };
}
EOF

}

################################################################################
# Keyword comparison functions
# Globals: keywords

# readKeywords
# params: filename - file of keywords to read
# sets:   keywords - array of keywords in the file
readKeywords () {
  local filename="$1" 
  local kexiSQL="$2"
  i=0
  while read keyword ; do
    keywords[$i]="$keyword"
    (( i++ ))
  done < "$filename"
}

# compareKeywords
# reads: kexiSQL - 
#        driverSQL
# sets:  keywords - driver keywords that are not keywords in Kexi
compareKeywords () {
  numFound=0
  for(( i=0; i < ${#driverSQL[@]}; i++ )) ; do
    found="no"
    for(( j=0; j < ${#kexiSQL[@]}; j++ )) ; do
      if [ "${driverSQL[$i]}" == "${kexiSQL[$j]}" ] ; then
        found="yes"
      fi
    done
    if [ "$found" == "no" ] ; then
      keywords[$numFound]="${driverSQL[$i]}"
      (( numFound++ ))
    fi
  done
}


# getDriverKeywords
# params : kexi - 
#          driver - 
#          outFile -
getDriverKeywords () {
  local kexi="$1"
  local driver="$2"
  local outFile="$3"

  declare -a kexiSQL
  declare -a driverSQL

  echo "Looking for driver-specific keywords in \"$driver\""
  readKeywords $kexi
  for(( i=0; i < ${#keywords[@]}; i++ )) ; do
    kexiSQL[$i]=${keywords[$i]}
  done
  unset keywords

  readKeywords $driver
  for(( i=0; i < ${#keywords[@]}; i++ )) ; do
    driverSQL[$i]=${keywords[$i]}
  done
  unset keywords

  compareKeywords
  echo "Writing driver-specific keywords for \"$driver\" to \"$outFile\""
  rm -f $outFile
  for(( i=0; i < ${#keywords[@]}; i++ )) ; do
    echo ${keywords[$i]} >> $outFile
  done
  unset keywords
}
################################################################################


################################################################################
# Kexi lexer

checkKexiKeywords () {
  local scanner="../../kexidb/parser/sqlscanner.l"
  if [ ! -r kexi.all -o "$scanner" -nt "kexi.all" ] ; then
    echo "Getting Kexi keywords"
    grep '^(\?"[a-zA-Z_0-9]' "$scanner" | \
       sed 's/(\?"\([^"]*\)"[^"]*/\1\n/g' | \
       awk '/^[a-zA-Z_0-9]+$/ {print $1;}' | 
       sort | uniq > "kexi.all"
    awk '/^[a-zA-Z_0-9]+$/ {print $1;}' kexi_reserved >> "kexi.all"
  fi
}

################################################################################
# DB lexer functions
# These functions munge the extracted lexers from DBs and write the collected
# keywords to file

# getSQLiteKeywords
# params : inFile  - SQLite3 lexer file
#          outFile - all SQLite3 keywords
getSQLiteKeywords () {
  local inFile="$1"
  local outFile="$2"
  
  echo "Getting SQLite keywords ($inFile -> $outFile)"
  sed -n '/^static Keyword aKeywordTable/,/};/p' $inFile | \
    awk '/  { "[a-zA-Z_0-9]*"/ { print $2;}' | \
    sed 's/"\(.*\)".*/\1/g' > $outFile
}

getPostgreSQLKeywords () {
  local inFile="$1"
  local outFile="$2"

  echo "Getting PostgreSQL keywords ($inFile -> $outFile)"
  sed -n '/^static const ScanKeyword ScanKeywords/,/};/p' $inFile | \
    awk '/\t{"[a-zA-Z_0-9]*"/ { print $1;}' | \
    sed 's/.*"\(.*\)".*/\1/g' | tr 'a-z' 'A-Z' > $outFile
}

# getMySQLKeywords
# params : inFile  - MySQL lexer file
#          outFile - all MySQL keywords
getMySQLKeywords () {
  local inFile="$1"
  local outFile="$2"

  echo "Getting MySQL keywords ($inFile -> $outFile)"
  sed -n '/^static SYMBOL symbols/,/};/p' $inFile | \
    awk '/  { "[a-zA-Z_0-9]*"/ { print $2;}' | \
    sed 's/"\(.*\)".*/\1/g' > $outFile
}

################################################################################
# DB tarball functions
# These functions extract the lexer files from the DB source tarballs

# checkExtracted
# params : tarball - tarball containing backend DB source
#          file - file in tarball containing DB's lexer
checkExtracted () {
  local tarball="$1"
  local file="$2"

  if [ ! -r "$file" ] ; then
    echo "Getting file \"$file\" from \"$tarball\""
    tar -zxf "$tarball" "$file"
  fi
}

# checkTarballs
checkTarballs () {
  local pathInTar
  local appName
  local appVer

  # SQLite (native DB backend) keywords
  appName="SQLite"
  appVer=sqlite
  inFile="../../3rdparty/kexisql3/src/tokenize.c"
  filePrefix="sqlite"
  if [ ! -r "$appVer.all" ] || [ ! -r "$appVer.new" ] ; then
    getSQLiteKeywords "$inFile" "$appVer.all"
  fi
  if [ "$appVer.all" -nt "$appVer.new" ] ; then
    getDriverKeywords "kexi.all" "$appVer.all" "$appVer.new"
    header "${appName}Driver::keywords" "${filePrefix}driver.h" "$inFile" "${filePrefix}keywords.cpp"
    body   "$appVer.new" "${filePrefix}keywords.cpp"
    footer "${filePrefix}keywords.cpp"
  fi

  ls mysql-*.tar.gz postgresql-*.tar.gz 2>/dev/null | while read tarball ; do
   case "$tarball" in
     mysql-4.1.[0-9\.]*.tar.gz)
       pathInTar="sql/lex.h"
       appName="MySql"
       filePrefix="mysql"
       appVer="${tarball%.tar.gz}"
       if [ ! -r "$appVer.all" ] || [ ! -r "$appVer.new" ] ; then
         checkExtracted "$tarball" "$appVer/$pathInTar"
         getMySQLKeywords "$appVer/$pathInTar" "$appVer.all"
	 rm -rf "$appVer"
       fi

       if [ "$appVer.all" -nt "$appVer.new" ] ; then
         getDriverKeywords "kexi.all" "$appVer.all" "$appVer.new"
         header "${appName}Driver::keywords" "${filePrefix}driver.h" "$appVer/$pathInTar" "${filePrefix}keywords.cpp"
         body   "$appVer.new" "${filePrefix}keywords.cpp"
         footer "${filePrefix}keywords.cpp"
       fi
       ;;

     postgresql-base-7.4.[0-9\.]*.tar.gz)
       pathInTar="src/backend/parser/keywords.c"
       appName="pqxxSql"
       filePrefix="pqxx"
       appVer=`echo "${tarball%.tar.gz}" | sed 's/-base//'`
       if [ ! -r "$appVer.all" ] || [ ! -r "$appVer.new" ] ; then
         checkExtracted "$tarball" "$appVer/$pathInTar"
         getPostgreSQLKeywords "$appVer/$pathInTar" "$appVer.all"
	 rm -rf "$appVer"
       fi

       if [ "$appVer.all" -nt "$appVer.new" ] ; then
         getDriverKeywords "kexi.all" "$appVer.all" "$appVer.new"
         header "${appName}Driver::keywords" "${filePrefix}driver.h" "$appVer/$pathInTar" "${filePrefix}keywords.cpp"
         body   "$appVer.new" "${filePrefix}keywords.cpp"
         footer "${filePrefix}keywords.cpp"
       fi
       ;;

     *)
       echo "Don't know how to deal with $tarball - ignoring"
       ;;
    esac
  done
}

checkKexiKeywords
src=`printf "koffice/kexi/kexidb/parser/sqlscanner.l\n"\
" * and koffice/kexi/tools/sql_keywords/kexi__reserved"`
header "DriverPrivate::kexiSQLKeywords" "driver_p.h" "$src" "keywords.cpp"
body "kexi.all" "keywords.cpp"
footer "keywords.cpp"

checkTarballs
wc -l *.all *.new | awk '{print $2" "$1}' |sort|awk '{print $1"\t"$2}'
