Sorry, you need to enable JavaScript to visit this website.

Give Linux a Windows-style "Registry"

The Ultimate Abomination

First of all, let me make it clear that this is only meant to be a joke.

No really, it's a joke.

Please, for the love of God, do not pervert GNU/Linux into Windows, by giving it the worst configuration storage system of all time.

Having said that, it does actually work (FSVO: "work").

The following takes the contents of "/etc" and "$HOME/{userid}/{dotfiles}", and creates a Windows-style "Registry" from them, comprising two Sqlite3 databases: SYSTEM.DAT.db and USER.DAT.db respectively.

It isn't an elegant or even particularly useful implementation, but then it's not meant to be (it's a joke, remember). For a start, it stores both variables and their corresponding values as a single entity (literally one line of each configuration file at a time), rather than splitting them up into KEY_NAME and KEY_VALUE pairs.

I spent all of 5 minutes on this, and that's as long as I'm ever going to spend on it, so there was no way I was going to waste time parsing variables vs. values in every single configuration file. And since I didn't parse the files (and I mean at all), that means all the comments are included too, which the Windows Registry also doesn't do (although now I come to think of it, maybe it should, since then people might actually understand it).

Lastly, I discovered to my dismay that "/etc" contains a large number of non-text files (hitherto referred to as blobs), which AFAICT is contrary to the guidelines set out by the Linux FHS, but for completeness I've allowed them to be included in the database. The proper way to do this is using Sqlite's "BLOB" datatype, but that requires more than five minutes work (especially using Bash), so I resorted to base64 encoding them (which is slow and inefficient, as you'll discover if you actually run this script).

Sqlite also seems to have a limit to the total size of the values it stores (probably configurable during compilation), but again I wasn't going to waste time investigating, so I just scripted it to discard any "blob" greater than a certain size (which I discovered with a process of trial and error, as I couldn't see it documented anywhere). In all fairness, storing huge picture files, etc., is probably not what the Sqlite developers had in mind when they created it and, like I said, blobs don't belong in "/etc" anyway (and this demonstrates exactly why: hint), so I won't be overly critical.

The script itself runs like a sedated tortoise in a tub of treacle at a reasonable speed, depending on your hardware and the amount of junk configuration files you have. It won't set any records though, but at least it does show its progress as a countdown of remaining entries. Other than causing your PC to explode, killing everyone in a ten foot radius, and losing all your data eating CPU, memory and a small (probably less than 20MB total) amount of disk space for the database(s), this script is non-destructive: it does not change anything at all on your system. IOW it's safe (FSVO: "safe").

To query the Linux "Registry", you'd do something like this:

sqlite3 SYSTEM.DAT.db "select cfg_value from HKEY_LOCAL_MACHINE \
where cfg_filename='/etc/hosts'";

Which would spit out every line of that configuration file. You could just select specific lines, like this:

sqlite3 SYSTEM.DAT.db "select cfg_value from HKEY_LOCAL_MACHINE \
where cfg_filename='/etc/hosts' and cfg_line=11";

But it's debatable how useful that is, given how you'd need to know what value was on that line before you'd know whether you needed it. You could even do:

sqlite3 SYSTEM.DAT.db "select cfg_value from HKEY_LOCAL_MACHINE where \
cfg_filename='/etc/hosts' and cfg_value='# /etc/hosts: Local Host Database'";

But again, that's utterly pointless (pretty much like the script itself), since again you need to know the value before you query it (duh!).

This is why I'd need to go to the effort of parsing every single configuration file, in order to actually make this script useful (OMG! What am I saying?), so you could do some like this:

sqlite3 SYSTEM.DAT.db "select cfg_value from HKEY_LOCAL_MACHINE where \
cfg_filename='/etc/hosts' and cfg_var='127.0.0.1'

To get:

localhost

But I'll leave that as an exercise for someone who actually cares.

So here it is: The Linux "Registry" maker:

#!/bin/sh
# name: makereg.sh
# version: 0.1.0
# copyright: 2010 Homer, slated.org
# license: GPLv3+
# description: transforms /etc and dotfiles into a "Registry"

exitstatus=0
faillog="/tmp/makereg.failed"
conflist="/tmp/etclist"
rm -f "$conflist"

regfun () {

   local datfile="$1"
   local branch="$2"
   local directory="$3"

   echo -n "Removing old $datfile database ... "
   rm -f "$datfile"
   echo "done."

   echo -n "Creating empty $datfile database ... "
   sqlite3 "$datfile" "create table $branch (keyindex INTEGER PRIMARY \
      KEY, cfg_filename TEXT, cfg_line INTEGER, cfg_value TEXT, isblob \
      INTEGER);"
   echo "done."

   echo -n "Creating list of configuration files in $directory ... "
   case "$directory" in
      "/etc")
         find "$directory" -type f -exec file {} ';' >"$conflist"
      ;;
      "/home")
         find "$directory" -type d -name "\.*" -exec find {} -type f ';' \
            >"${conflist}.tmp"
         find "$directory" -type f -name "\.*" >>"${conflist}.tmp"
         cat "${conflist}.tmp" | sort | uniq >"$conflist"
         rm -f "${conflist}.tmp"
      ;;
      *)
         echo "$directory is not a valid configuration directory."
         exit 1
   esac
   echo "done."

   count=$(cat $conflist | wc -l)
   padsize=${#count}
   echo "Adding configuration files to the $datfile \"Registry\"."
   echo -n "Remaining: "

   while read config
      do
         printf "%${padsize}s" $count
         filename=$(echo $config | cut -d':' -f1)
         filetype=$(echo $config | cut -d':' -f2-)
         if [[ "$filetype" == *ASCII* ]] || [[ "$filetype" == *XML* ]] \
            || [[ "$filetype" == *text* ]]
            then
               index=1
               while read value
                  do
                     echo "$value" | grep -q "'"
                     if [ $? -eq 0 ]
                        then
                          value=$(echo "$value" | sed s/\'/\'\'/g)
                     fi
                     sqlite3 "$datfile" "insert into $branch \
                        (cfg_filename,cfg_line,cfg_value,isblob) \
                        values ('$filename','$index','$value',0);"
                     ((index++))
                  done<"$filename"
            elif [[ "$filetype" == *empty* ]]
               then
                  sqlite3 "$datfile" "insert into $branch \
                     (cfg_filename,cfg_line) values ('$filename',1);"
            else
               value=$(base64 -w0 "$filename")
               blobsize=${#value}
               if [ $blobsize -le 120000 ]
                  then
                     sqlite3 "$datfile" "insert into $branch \
                        (cfg_filename,cfg_line,cfg_value,isblob) \
                        values ('$filename',1,'$value',1);"
                  else
                     echo "$filename" >>"$faillog"
                     exitstatus="1"
               fi
         fi
   count=$((count-1))
   for ((backspace=1; backspace <= padsize ; backspace++))
      do
         printf '\b'
      done
   done<"$conflist"

}

# Main

regfun SYSTEM.DAT.db HKEY_LOCAL_MACHINE /etc
regfun USER.DAT.db HKEY_LOCAL_USER /home

echo

rm -f "$conflist"

if [ $exitstatus -eq 1 ]
   then
      echo "Some files could not be added to the \"Registry\","
      echo "because they were just too damned big."
      echo "To wallow in this failure some more, you could take a look"
      echo "at the full list in $faillog"
      echo
fi

echo "Thank you for flying Air Slated."

exit $exitstatus