scripting

Passing Command Line Arguments to a LaTeX Document

Some time ago, someone asked me if I knew a way to pass command line arguments to LaTeX documents. My first response was that this ought to be impossible; being a compiler, LaTeX cannot in any way alter it’s input files. But command line parameters can alter the output without touching the input of course.

This has kept me thinking for a while, and at the end I came up with some kind of a solution.

Let’s take a simple LaTeX file:

\documentclass{article}
\begin{document}
\someinput{}
\end{document}

and call it test.tex.

We will define the output of \someinput{} as a command line parameter at LaTeX invocation, as follows:

echo '\\newcommand{\\someinput}{hello world} \\input test' | latex

Result: test.dvi containing the text “hello world”. Admitted, it’s not the easiest solution, but it does make LaTeX useful inside simple batch jobs!

For more advanced LaTeX templating, I advise using Perl and Text::Template.

Posted in Open Source Adventures, scripting   No Comments »

gettext management

Anybody remember gettext?
A couple of years ago I investigated the localisation system for a project and actually came to like it pretty much.
Managing your .po and .mo files can be a hassle though.
That’s why I created these two scripts to help me handle them:


#!/usr/local/bin/bash

function createpot {
# create new .po templates (.pot)
local POT=$1
touch ./locale/${locale}.UTF-8/LC_MESSAGES/${POT}.pot

xgettext --default-domain=${DOMAIN} \
--output-dir="./locale/${locale}.UTF-8/LC_MESSAGES/" --language PHP \
--force-po --indent --strict --no-wrap \
--copyright-holder='inGen cvba' \
--msgid-bugs-address='bart@inGen.be' --output=${POT}.pot \
--add-location ${phpfile}

chmod -f g+rw ./locale/${locale}.UTF-8/LC_MESSAGES/${POT}.pot
}

function updatepo {
# update the original with the new potfile
local PO=$1
touch ./locale/${locale}.UTF-8/LC_MESSAGES/${PO}.po
touch ./locale/${locale}.UTF-8/LC_MESSAGES/${PO}.pot

msgmerge --update --backup=simple --force-po --indent --strict \
--no-wrap --quiet --add-location \
./locale/${locale}.UTF-8/LC_MESSAGES/${PO}.po \
./locale/${locale}.UTF-8/LC_MESSAGES/${PO}.pot

chmod -f g+rw ./locale/${locale}.UTF-8/LC_MESSAGES/${PO}.po
}

function mergepot {
# merge the original po with the new pot
local PO=$1
local POT=$2
touch ./locale/${locale}.UTF-8/LC_MESSAGES/${PO}.po
touch ./locale/${locale}.UTF-8/LC_MESSAGES/${POT}.pot

msgcat --output-file=./locale/${locale}.UTF-8/LC_MESSAGES/${POT}.pot \
--to-code=UTF-8 --use-first --force-po --indent --add-location \
--strict --sort-output \
./locale/${locale}.UTF-8/LC_MESSAGES/${PO}.po \
./locale/${locale}.UTF-8/LC_MESSAGES/${POT}.pot

rm -f ./locale/${locale}.UTF-8/LC_MESSAGES/${PO}.po
chmod -f g+rw ./locale/${locale}.UTF-8/LC_MESSAGES/${POT}.pot
}

function backuppo {
# do a real backup of the existing pofiles
cp ./locale/${locale}.UTF-8/LC_MESSAGES/${DOMAIN}.po \
./locale/${locale}.UTF-8/LC_MESSAGES/pobackup/
}

for locale in nl_BE fr_BE en_GB
do
# generate .po messages
for phpfile in index.php
do
# the domain is the basename of the file
DOMAIN=`basename $phpfile .php`

backuppo
createpot ${DOMAIN}
updatepo ${DOMAIN}
done
done

and


#!/usr/local/bin/bash

# generate .mo messages of al .po files
for lang in nl_BE fr_BE en_GB
do
for pofile in ./locale/${lang}.UTF-8/LC_MESSAGES/*.po
do
rm -f ${profile/po/mo}
msgfmt ${pofile} -o ${pofile/po/mo} --strict --check --use-fuzzy
chmod -f g+r ${pofile/po/mo}
done
done

These scripts will not setup the working environment nor generate full headers for new .po files.
You will have to do this manually.
A corresponding working environment for the scripts in their posted configuration looks like this:

.
|-- images
|   `-- favicon.ico
|-- includes
|   `-- accept-to-gettext.inc
|-- index.php
|-- locale
|   |-- en_GB.UTF-8
|   |   `-- LC_MESSAGES
|   |       |-- index.mo
|   |       |-- index.po
|   |       |-- index.pot
|   |       |-- index.po~
|   |       `-- pobackup
|   |           `-- index.po
|   |-- fr_BE.UTF-8
|   |   `-- LC_MESSAGES
|   |       |-- index.mo
|   |       |-- index.po
|   |       |-- index.pot
|   |       |-- index.po~
|   |       `-- pobackup
|   |           `-- index.po
|   `-- nl_BE.UTF-8
|       `-- LC_MESSAGES
|           |-- index.mo
|           |-- index.po
|           |-- index.pot
|           |-- index.po~
|           `-- pobackup
|               `-- index.po
|-- makemo
|-- makepo
`-- styles
    `-- index.css

Mind the accept-to-gettext.inc file, a great script written by Wouter Verhelst to convert information in HTTP ‘Accept-*’ headers to gettext language identifiers.

Posted in Open Source Adventures, scripting   No Comments »

pyfconfig — how to get ifconfig data without regular expressions

After reading this post
about ifconfig output parsing by Kris
, I remembered I once needed a
cross-platform way to get the IP address of an interface in Python.

Of course I could just parse the output of `ifconfig`, but I really don’t like
such ugly hacks. I guess I’ve got too many (bad) experiences with libwww-perl scripts I wrote
for web harvesting various stuff. Basically, this is output parsing too, as
HTML is generally the result of some server side script. Each time the webpage
changed the way it looked (non-CSS changes) or worked, my scripts started to
fail.

That’s when I decided I’ll always try to avoid such clumsy dependencies on
third party software.

So, back to the Python question. I set out for a
short adventure on comp.lang.python
and came up with a solution
after some fiddling: pyfconfig, a cross platform Python module to query for the
IP address of an interface. Tested on FreeBSD x86, GNU/Linux x86 and GNU/Linux
x86_64, with Python 2.4 and Python 2.5. Works just fine.


#include "Python.h"

#include
#include
#include
#include
#include
#include
#include
#include
#include

// parameters: string (interface name)
// output: string (ip address of interface in decimal notation)
PyObject * ipaddr(PyObject *self, PyObject *args) {

char ip[ 200 ];
char *itf;

if (! PyArg_ParseTuple(args, "s", &itf)) {
PyErr_SetString(PyExc_Exception, "no interface given!");
return NULL;
}

struct ifaddrs *ifa = NULL, *ifp = NULL;

if (getifaddrs (&ifp) < 0) {
perror ("getifaddrs");
return NULL;
}

for (ifa = ifp; ifa; ifa = ifa->ifa_next) {
socklen_t salen;

if (ifa->ifa_addr->sa_family == AF_INET)
salen = sizeof (struct sockaddr_in);
else if (ifa->ifa_addr->sa_family == AF_INET6)
salen = sizeof (struct sockaddr_in6);
else
continue;

if (strncmp(ifa->ifa_name, itf, sizeof(itf))) {
continue;
}

if (getnameinfo (ifa->ifa_addr, salen, ip, sizeof (ip), NULL, 0, NI_NUMERICHOST) < 0) {
perror ("getnameinfo");
continue;
}
break;
}

freeifaddrs (ifp);

return Py_BuildValue("s", ip);
}

static PyMethodDef pyfconfig_methods[] = {
{"ipaddr", (PyCFunction)ipaddr, METH_VARARGS, "ipaddr(string)\n"},
{NULL, NULL, 0, NULL}
};

DL_EXPORT(void) initpyfconfig(void) {
Py_InitModule3("pyfconfig", pyfconfig_methods, "Provides a function to get an ip address of a certain interface.\n");
}

Compile with gcc -fPIC -shared -Wl -soname pyfconfig.so -o pyfconfig.so -I/usr/include/python2.5/ pyfconfig.c (for Python 2.5) and after an
import pyfconfig, pyfconfig.ipaddr('lo') should return '127.0.0.1' (YMMV).

No dependency on the output formatting of ifconfig. Less bugs.

Posted in Open Source Adventures, scripting   No Comments »

Poor man’s NTP

Yesterday I had to quickly (more or less) synchronize the clocks of a handful of computers I was working on. I didn’t have the time to work out an NTP based solution, so I came up with the following simple trick:


for i in `seq 1 6`
do
ssh root@192.168.1.20${i} date `date +%m%d%H%M%Y.%S`
done

Needless to say… it worked. :-)

Posted in Open Source Adventures, scripting   No Comments »

[office] scantopdf revisited

At the airport. Bored. Started hacking up my
scantopdf script
to cater for multiple page documents and a simple scanner.


#!/bin/sh

# scans an A4 image and saves it as a pdf file with given filename
# supports multiple page documents

colourmode="Gray"
depth=8
resolution=300

while getopts cd:r: option
do
case $option in
c) colourmode="Color";;
d) depth=${OPTARG};;
r) resolution=${OPTARG};;
?) printf "Usage: %s [-c] [-d depth] [-r resolution] filename.pdf [number of pages]\n" $0
exit 2;;
esac
done

shift $(($OPTIND - 1))

filename=$1

numpages=$2
if [ -z ${numpages} ]
then
numpages=1
fi

getpdf() {
/usr/bin/scanimage --mode=${colourmode} --depth=${depth} \
--resolution ${resolution} -x 215 -y 297 \
| /usr/bin/pnmtops -noshowpage -equalpixels -dpi=${resolution} \
| /usr/bin/ps2pdf -sPAPERSIZE=a4 - "$*"
}

TMPDIR=$(/usr/bin/mktemp -d)

currentpage=0
while [ ${currentpage} -lt ${numpages} ]
do
currentpage=$((currentpage+1))
echo "Please insert page ${currentpage}/${numpages}."
read -p "Press enter to continue..."
echo "Scanning..."
getpdf ${TMPDIR}/page${currentpage}.pdf
echo "Ready!"
done

/usr/bin/pdftk ${TMPDIR}/*pdf cat output ${filename}

echo "Result is ${filename}. Individual pages can be found in ${TMPDIR}."

It depends on some external tools though and I don’t really like the way their
paths are hard-coded now, but it’s useful as is, still (and very easy to
change). Make sure you have pdftk, scanimage, netpnm and ghostscript installed or it won’t work for
you.

Curious if it will work when I return to the office. :-)

Posted in scripting   No Comments »

[sysadmin] vhostlist

On our public web server, we are hosting a
lot of stuff. Currently, we have over one hundred virtual hosts defined in
Apache.

One day, I realised that I didn’t have an overview any more of all these
deployments. That’s when I decided to write a simple script to produce me a
list of all virtual hosts (and aliases) in HTML. As it’s all about file
parsing, and I got a bit bored of Perl, I decided to write it in AWK and wrap
it in sh so it can be installed as a CGI script on the same web server.


#!/bin/sh

echo 'Content-Type: text/html; charset=iso-8859-1'
echo 'Cache-Control: no-cache'
echo 'Cache-Control: no-store'
echo 'Pragma: no-cache'
echo 'Expires: Thu, 01 Dec 1994 16:00:00 GMT'
echo

echo '< !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">'
awk 'BEGIN {print "

Virtual Hosts configured on mars

    "} /^[ \t]*Server(Name|Alias)/ { $1=""; split($0, domains); for (i=0; i%s

    \n", domains[i+1], domains[i+1])}} END { print "

"}' /etc/apache2/vhosts.d/*

It’s still really basic, but it serves it’s purpose. Wished feature is to show
a difference between ServerName’s and ServerAlias’es. This is left as an
exercise to the reader.

Posted in scripting   No Comments »

[sysadmin] cupsquota.sh

Sometimes I want to know how many pages some user printed on some printer on
the network from some host on the network. This script tells me just that.


#!/bin/sh

# script to calculate some statistics from a cups page_log
#
# takes two (optional) arguments: a cups page_log and a username
#
# page_log is expected to be in the following format:
#
# samsung sven 279 [23/Nov/2007:18:44:01 +0100] 1 1 - localhost
# samsung sven 280 [23/Nov/2007:20:09:55 +0100] 1 1 - localhost
# HP loes 281 [25/Nov/2007:16:21:18 +0100] 1 1 - localhost
# HP loes 281 [25/Nov/2007:16:21:25 +0100] 2 1 - localhost
# HP loes 281 [25/Nov/2007:16:21:30 +0100] 3 1 - localhost
# HP loes 281 [25/Nov/2007:16:21:34 +0100] 4 1 - localhost
# HP loes 281 [25/Nov/2007:16:21:41 +0100] 5 1 - localhost
# HP loes 281 [25/Nov/2007:16:21:46 +0100] 6 1 - localhost
# HP loes 281 [25/Nov/2007:16:21:49 +0100] 7 1 - localhost
# samsung loes 282 [25/Nov/2007:23:43:22 +0100] 1 1 - localhost
# samsung sven 283 [28/Nov/2007:18:15:30 +0100] 1 1 - localhost
# samsung sven 283 [28/Nov/2007:18:15:32 +0100] 2 1 - localhost
# samsung sven 284 [28/Nov/2007:18:17:42 +0100] 1 1 - localhost
# samsung sven 284 [28/Nov/2007:18:17:46 +0100] 2 1 - localhost
# samsung loes 285 [30/Nov/2007:09:02:14 +0100] 1 1 - localhost
# samsung sven 286 [30/Nov/2007:14:29:33 +0100] 1 1 - localhost
# samsung sven 286 [30/Nov/2007:14:29:36 +0100] 2 1 - localhost
# samsung loes 287 [30/Nov/2007:18:24:37 +0100] 1 1 - localhost

if [ $# -eq 0 ]
then
USERNAME=${USER}
PAGELOG=/var/log/cups/page_log
elif [ $# -eq 1 ]
then
if [ -f $1 ]
then
USERNAME=${USER}
PAGELOG=$1
else
USERNAME=$1
PAGELOG=/var/log/cups/page_log
fi
elif [ $# -eq 2 ]
then
if [ -f $1 ]
then
PAGELOG=$1
USERNAME=$2
else
PAGELOG=$2
USERNAME=$1
fi
else
echo "Usage:"
echo "$0 [] [ ]"
exit 1
fi

/usr/bin/awk -v username=${USERNAME} '
BEGIN {
pagecount = 0
pagenum = 0
if (!username) {
username = ENVIRON["USER"]
}
}

$2 ~ username {
if ( $6 !~ "total" ) { # HACK :-(
if ( $6 == 1 ) {
pagecount += pagenum
}
pagenum = $6
}
}

END {
printf "%s has printed %d pages in total\n", username, pagecount
}
' ${PAGELOG}

It’s an awk
program with a bourne shell wrapper to calculate the total amount of pages a
user has printed on a host using cups.

Posted in scripting   No Comments »

[office] scantopdf

Another addition to my [office] collection of scripts. And again one which
deals with a scanner. scantopdf scans an A4 paper from any SANE device and
stores it as a PDf with a given filename.


#!/bin/sh

# scans an A4 image and saves it as a pdf file with given filename

colourmode="Gray"
depth=8
resolution=300

while getopts cd:r: option
do
case $option in
c) colourmode="Color";;
d) depth=${OPTARG};;
r) resolution=${OPTARG};;
?) printf "Usage: %s [-c] [-d depth] [-r resolution] filename.pdf\n" $0
exit 2;;
esac
done

shift $(($OPTIND - 1))

/usr/bin/scanimage --mode=${colourmode} --depth=${depth} \
--resolution ${resolution} -x 215 -y 297 \
| /usr/bin/pnmtops -noshowpage -equalpixels -dpi=${resolution} \
| /usr/bin/ps2pdf -sPAPERSIZE=a4 - "$*"

Posted in scripting   1 Comment »

[office] copymachine

At the office, we have a scanner attached to a desktop (elisa), published as a
:net device by SANE. Additionally,
a network printer (gutenberg) is attached to the network. This script allows
anyone on the network to combine both as a (basic) copymachine.


#!/bin/sh

colourmode="Gray"
depth=8
resolution=300
printername="samsung"
copies=1

while getopts cd:r:P:n: option
do
case $option in
c) colourmode="Color";;
d) depth=$OPTARG;;
r) resolution=$OPTARG;;
P) printername="$OPTARG";;
n) copies="$OPTARG";;
?) printf "Usage: %s [-c] [-d depth] [-r resolution] [-P printername] [-n copies]\n" $0
exit 2;;
esac
done

/usr/bin/scanimage --mode $colourmode --depth $depth \
--resolution $resolution -x 215 -y 297 \
| /usr/bin/pnmtops -noshowpage -equalpixels -dpi=$resolution \
| /usr/bin/lpr -P $printername -# $copies

Remember it’s statically configured for A4 papersize (215mm x 297mm).

Posted in scripting   No Comments »

[office] dvitopdf.sh

At our office we tend to document almost
everything with LaTeX. This script
is one which has saved us a lot of time already with creating high quality PDF
documents.

Basically, it’s just an alias to simplify creating prepress quality A4 PDFs of
a DVI file, typically created with LaTeX and hyperref with the dvipdfm driver.


# creates a pdf from a given dvi file on an a4 page
# all parameters are passed to dvipdft
/usr/bin/dvipdft -p a4 -D "/bin/cat %i | /usr/bin/gs -q -dCompatibilityLevel=1.2 -dUseFlateCompression=true -dSAFER -sDEVICE=pdfwrite -sOutputFile=%o -sPAPERSIZE=a4 -dPDFSETTINGS=/prepress - -c quit" $@

It’s the “prepress” setting at the end which guarantees the quality of the
output.

Posted in scripting   No Comments »