Code

Initial commit.
authorSebastian Harl <sh@tokkee.org>
Sun, 13 Jun 2010 20:42:14 +0000 (22:42 +0200)
committerSebastian Harl <sh@tokkee.org>
Sun, 13 Jun 2010 20:42:14 +0000 (22:42 +0200)
So far, the tool is able to parse most (all but tables) of the Linux-Magazin
markup language and output an appropriate LaTeX file.

14 files changed:
.gitignore [new file with mode: 0644]
COPYING [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/l2l.c [new file with mode: 0644]
src/l2l.h [new file with mode: 0644]
src/l2l_features.h.in [new file with mode: 0644]
src/l2l_scanner.l [new file with mode: 0644]
src/lm2latex.c [new file with mode: 0644]
version-gen.sh [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..84dd827
--- /dev/null
@@ -0,0 +1,29 @@
+# build system stuff
+.deps
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.*
+configure
+depcomp
+install-sh
+missing
+stamp-h1
+version
+ylwrap
+
+# ltdl stuff
+libtool
+ltmain.sh
+
+# build output
+.libs
+*.la
+*.lo
+*.o
+l2l_features.h
+l2l_scanner.[ch]
+lm2latex
+
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..1228c7b
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,24 @@
+Copyright (c) 2010 Sebastian Harl <sh@tokkee.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..23e5f25
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory.  After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).  Here is a another example:
+
+     /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..688649f
--- /dev/null
@@ -0,0 +1,4 @@
+SUBDIRS = src
+
+EXTRA_DIST = autogen.sh version-gen.sh
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..8e3a6c3
--- /dev/null
+++ b/README
@@ -0,0 +1,39 @@
+lm2latex
+========
+
+  lm2latex (Linux-Magazin to LaTeX) converts a text file formatted using the
+  "Linux Magazin" markup to a LaTeX file.
+
+  This is free and open source software, licensed under the 2-clause BSD
+  license. See COPYING for details.
+
+Prerequisites
+-------------
+
+  To compile the lm2latex package from source you need:
+
+  * A build environment: autotools, libtool, C compiler, ...
+
+  * A POSIX + Single UNIX Specification compatible C library.
+
+  * The lexical analyzer generator "flex".
+
+Configuring / Compiling / Installing
+------------------------------------
+
+  To configure, build and install lm2latex with the default settings, run
+  `./configure && make && make install'. For detailed, generic instructions
+  see INSTALL. For a complete list of configure options and their description,
+  run `./configure --help'.
+
+  By default, lm2latex will be installed into `/opt/lm2latex'. You can adjust
+  this setting by specifying the `--prefix' configure option - see INSTALL for
+  details. If you pass DESTDIR=<path> to `make install', <path> will be
+  prefixed to all installation directories. This might be useful when creating
+  packages for lm2latex.
+
+Author
+------
+
+  Sebastian "tokkee" Harl <sh@tokkee.org>
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..7c7c72f
--- /dev/null
@@ -0,0 +1,17 @@
+#! /bin/sh
+
+libtoolize=libtoolize
+
+if which glibtoolize > /dev/null 2>&1; then
+       libtoolize=glibtoolize
+fi
+
+set -ex
+
+aclocal --force --warnings=all
+$libtoolize --automake --copy --force
+aclocal
+autoconf --force --warnings=all
+autoheader --force --warnings=all
+automake --add-missing --copy --foreign --warnings=all
+
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..31d2f03
--- /dev/null
@@ -0,0 +1,197 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl
+dnl This is the lm2latex configure script.
+dnl
+dnl Copyright (C) 2010 Sebastian Harl <sh@tokkee.org>
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions
+dnl are met:
+dnl 1. Redistributions of source code must retain the above copyright
+dnl    notice, this list of conditions and the following disclaimer.
+dnl 2. Redistributions in binary form must reproduce the above copyright
+dnl    notice, this list of conditions and the following disclaimer in the
+dnl    documentation and/or other materials provided with the distribution.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+dnl TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+dnl PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+dnl CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+dnl EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+dnl PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+dnl OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+dnl WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+dnl OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+dnl ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+AC_INIT([lm2latex],[m4_esyscmd(./version-gen.sh)],
+               [Sebastian Harl <sh@tokkee.org>], [],
+               [http://git.tokkee.org/?p=lm2latex.git])
+AC_CONFIG_SRCDIR([src/lm2latex.c])
+AC_CONFIG_HEADERS([src/config.h])
+AC_PREFIX_DEFAULT([/opt/lm2latex])
+
+AM_INIT_AUTOMAKE([foreign -Wall])
+
+AC_LANG(C)
+
+AC_SYS_LARGEFILE
+
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+
+AM_PROG_CC_C_O
+AM_PROG_LEX
+
+m4_ifdef([LT_INIT],
+       [
+        LT_INIT
+       ],
+       # else
+       # (older libtools)
+       [
+        AC_PROG_LIBTOOL
+       ]
+)
+
+test_cc_flags() {
+       AC_LANG_CONFTEST([int main() {}])
+       $CC -c conftest.c $CFLAGS $@ > /dev/null 2> /dev/null
+       ret=$?
+       rm -f conftest.o
+       return $ret
+}
+
+m4_divert_once([HELP_ENABLE], [
+Build options:])
+
+dnl Optionally stick to standard C99 and POSIX:2001 as close as possible.
+AC_ARG_ENABLE([standards],
+               AS_HELP_STRING([--enable-standards],
+                               [C99 / POSIX standards compliance mode @<:@default=no@:>@]),
+               [enable_standards="$enableval"],
+               [enable_standards="no"])
+
+if test "x$enable_standards" = "xyes"; then
+       AC_DEFINE([_ISOC99_SOURCE], 1,
+                       [Define to enforce ISO/IEC 9899:1999 (C99) compliance.])
+       AC_DEFINE([_POSIX_C_SOURCE], 200112L,
+                       [Define to enforce IEEE 1003.1-2001 (POSIX:2001) compliance.])
+       AC_DEFINE([_XOPEN_SOURCE], 600,
+                       [Define to enforce X/Open 6 (XSI) compliance.])
+       AC_DEFINE([_REENTRANT], 1,
+                       [Define to enable reentrant interfaces.])
+       AC_DEFINE([_THREAD_SAFE], 1,
+                       [Define to enable reentrant interfaces.])
+
+       for flag in -std=c99 -pedantic; do
+               AC_MSG_CHECKING([whether $CC accepts $flag])
+
+               if test_cc_flags $flag; then
+                       CFLAGS="$CFLAGS $flag"
+                       AC_MSG_RESULT([yes])
+               else
+                       AC_MSG_RESULT([no])
+               fi
+       done
+fi
+
+dnl Hardening (see e.g. http://wiki.debian.org/Hardening for a motivation).
+AC_DEFINE([_FORTIFY_SOURCE], 2,
+               [Define to enable protection against static sized buffer overflows.])
+AC_ARG_ENABLE([hardening],
+               AS_HELP_STRING([--disable-hardening],
+                               [hardening options @<:@default=yes@:>@]),
+               [enable_hardening="$enableval"],
+               [enable_hardening="yes"])
+
+if test "x$enable_hardening" = "xyes"; then
+       hardening=0
+       hardening_tests=0
+       for flag in -Wformat -Wformat-security; do
+               hardening_tests=$(($hardening_tests + 1))
+               AC_MSG_CHECKING([whether $CC accepts $flag])
+
+               if test_cc_flags $flag; then
+                       CFLAGS="$CFLAGS $flag"
+                       hardening=$(($hardening + 1))
+                       AC_MSG_RESULT([yes])
+               else
+                       AC_MSG_RESULT([no])
+               fi
+       done
+       if test $hardening -ne $hardening_tests; then
+               AC_MSG_WARN(
+                               [Some hardening options are not supported by your compiler!])
+       fi
+fi
+
+dnl Strict checking for potential problems.
+AC_ARG_ENABLE([strict-checks],
+               AS_HELP_STRING([--disable-strict-checks],
+                               [strict compiler checks @<:@default=yes@:>@]),
+               [enable_strict_checks="$enableval"],
+               [enable_strict_checks="yes"])
+
+STRICT_CFLAGS=""
+for flag in -Wall -Werror; do
+       AC_MSG_CHECKING([whether $CC accepts $flag])
+
+       if test_cc_flags $flag; then
+               STRICT_CFLAGS="$STRICT_CFLAGS $flag"
+               AC_MSG_RESULT([yes])
+       else
+               AC_MSG_RESULT([no])
+       fi
+done
+
+if test "x$enable_strict_checks" = "xyes"; then
+       for flag in -Wextra \
+                       -Wbad-function-cast \
+                       -Wcast-align \
+                       -Wcast-qual \
+                       -Wconversion \
+                       -Wdeclaration-after-statement \
+                       -Wmissing-prototypes \
+                       -Wpointer-arith \
+                       -Wshadow \
+                       -Wstrict-prototypes \
+                       -Wunreachable-code; do
+               AC_MSG_CHECKING([whether $CC accepts $flag])
+
+               if test_cc_flags $flag; then
+                       STRICT_CFLAGS="$STRICT_CFLAGS $flag"
+                       AC_MSG_RESULT([yes])
+               else
+                       AC_MSG_RESULT([no])
+               fi
+       done
+fi
+AC_SUBST([STRICT_CFLAGS])
+
+AC_CHECK_HEADERS(libgen.h)
+
+AC_CONFIG_FILES([Makefile src/Makefile])
+AC_OUTPUT
+
+cat <<EOF;
+
+$PACKAGE_NAME has been configured successfully.
+
+Run 'make' to compile the software and use 'make install' to
+install the package into $prefix.
+
+Configuration summary:
+
+  package version: $PACKAGE_VERSION
+  build date: `date --utc '+%F %T'` (UTC)
+
+Please report bugs to $PACKAGE_BUGREPORT.
+
+EOF
+
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..f1cb9c7
--- /dev/null
@@ -0,0 +1,48 @@
+AM_CFLAGS = @STRICT_CFLAGS@
+
+# we cannot set this as '%option header-file', since flex is called thru the
+# ylwrap wrapper script, which runs in a temporary directory
+AM_LFLAGS = --header-file=@abs_srcdir@/l2l_scanner.h
+
+BUILT_SOURCES = l2l_features.h l2l_scanner.l
+
+CLEANFILES = l2l_features.h l2l_scanner.c
+
+l2l_scanner.h: l2l_scanner.c
+
+# build this as a separate private library to be able to use different CFLAGS
+noinst_LTLIBRARIES = libl2l_scanner.la
+libl2l_scanner_la_SOURCES = l2l_scanner.l
+libl2l_scanner_la_CFLAGS = -Wall -Werror \
+               -Wno-unused-function -Wno-unused-variable \
+               -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600
+
+include_HEADERS = l2l.h l2l_features.h
+lib_LTLIBRARIES = libl2l.la
+
+libl2l_la_SOURCES = l2l.c l2l.h l2l_features.h
+libl2l_la_LDFLAGS = -version-info 0:0:0
+libl2l_la_LIBADD = libl2l_scanner.la
+
+bin_PROGRAMS = lm2latex
+
+lm2latex_SOURCES = lm2latex.c l2l.h
+lm2latex_CFLAGS = $(AM_CFLAGS) \
+               -DBUILD_DATE="\"$$( date --utc '+%F %T' ) (UTC)\""
+lm2latex_LDADD = libl2l.la
+
+../version: FORCE
+       @# As a side-effect, this updates ../version.
+       @echo Building $(PACKAGE_NAME) version $$( cd .. && ./version-gen.sh )
+
+l2l_features.h: l2l_features.h.in ../version
+       source ../version; sed \
+           -e "s/@L2L_VERSION_MAJOR@/$$VERSION_MAJOR/g" \
+           -e "s/@L2L_VERSION_MINOR@/$$VERSION_MINOR/g" \
+           -e "s/@L2L_VERSION_PATCH@/$$VERSION_PATCH/g" \
+           -e "s/@L2L_VERSION_EXTRA@/$$VERSION_EXTRA/g" \
+           -e "s/@L2L_VERSION_STRING@/$$VERSION_STRING/g" \
+           l2l_features.h.in > l2l_features.h
+
+.PHONY: FORCE
+
diff --git a/src/l2l.c b/src/l2l.c
new file mode 100644 (file)
index 0000000..2fae2b9
--- /dev/null
+++ b/src/l2l.c
@@ -0,0 +1,161 @@
+/*
+ * lm2latex - src/l2l.c
+ * Copyright (C) 2010 Sebastian Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * A Linux-Magazin markup to LaTeX converter -- core library.
+ */
+
+#include "l2l.h"
+#include "l2l_scanner.h"
+#include "l2l_features.h"
+
+#include <assert.h>
+
+#include <stdio.h>
+
+/*
+ * private helper functions
+ */
+
+static void
+write_tex_preamble(FILE *out)
+{
+       assert(out);
+
+       fprintf(out,
+"\\documentclass[11pt,a4paper,twoside]{article}\n"
+"\n"
+"\\usepackage{ifpdf}\n"
+"\n"
+"\\usepackage{a4}\n"
+"\\usepackage{fancyhdr}\n"
+"\\ifpdf\n"
+"\\usepackage[pdftex]{graphicx}\n"
+"\\else\n"
+"\\usepackage[dvips]{graphicx}\n"
+"\\fi\n"
+"\n"
+"\\usepackage{listings}\n"
+"\\lstset{breaklines=true,breakatwhitespace=true}\n"
+"\\lstset{prebreak=\\mbox{$\\hookleftarrow$}}\n"
+"\\lstset{basicstyle=\\footnotesize\\ttfamily}\n"
+"\n"
+"\\usepackage[ngerman]{babel}\n"
+"\\usepackage[latin1]{inputenc}\n"
+"\\usepackage[T1]{fontenc}\n"
+"\\usepackage{lmodern}\n"
+"\n"
+"\\ifpdf\n"
+"\\usepackage[a4paper,margin=15mm,top=25mm,bottom=20mm,pdftex]{geometry}\n"
+"\\else\n"
+"\\usepackage[a4paper,margin=15mm,top=25mm,bottom=20mm,dvips]{geometry}\n"
+"\\fi\n"
+"\n"
+"\\usepackage{caption}\n"
+"\\usepackage{url}\n"
+"\n"
+"\\setlength{\\parskip}{6pt}\n"
+"\n"
+"\\renewcommand{\\headrulewidth}{0.4pt}\n"
+"\\renewcommand{\\footrulewidth}{0pt}\n"
+"\n"
+"\\fancypagestyle{headings}\n"
+"\\fancyhead{}\n"
+"\\fancyhead[LO,RE]{}\n"
+"\\fancyhead[LE,RO]{\\thepage}\n"
+"\\fancyfoot{}\n"
+"\n"
+"\\pagestyle{fancy}\n"
+"\n");
+} /* write_tex_preamble */
+
+/*
+ * public API
+ */
+
+int
+l2l_convert_file_fh(FILE *in, FILE *out)
+{
+       yyscan_t scanner;
+
+       int status;
+
+       write_tex_preamble(out);
+
+       l2l_yylex_init(&scanner);
+       l2l_yyset_in(in, scanner);
+       l2l_yyset_out(out, scanner);
+
+       status = l2l_yylex(scanner);
+
+       l2l_yylex_destroy(scanner);
+       return status;
+} /* l2l_convert_file_fh */
+
+int
+l2l_convert_file(const char *infile, const char *outfile)
+{
+       FILE *in, *out;
+
+       int status;
+
+       in = fopen(infile, "r");
+       if (! in)
+               return -1;
+
+       out = fopen(outfile, "w");
+       if (! out) {
+               fclose(in);
+               return -1;
+       }
+
+       status = l2l_convert_file_fh(in, out);
+       fclose(in);
+       fclose(out);
+       return status;
+} /* l2l_convert_file */
+
+unsigned int
+l2l_version(void)
+{
+       return L2L_VERSION;
+} /* l2l_version */
+
+const char *
+l2l_version_string(void)
+{
+       return L2L_VERSION_STRING;
+} /* l2l_version_string */
+
+const char *
+l2l_version_extra(void)
+{
+       return L2L_VERSION_EXTRA;
+} /* l2l_version_extra */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/l2l.h b/src/l2l.h
new file mode 100644 (file)
index 0000000..b4a9014
--- /dev/null
+++ b/src/l2l.h
@@ -0,0 +1,54 @@
+/*
+ * lm2latex - src/l2l.h
+ * Copyright (C) 2010 Sebastian Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * A Linux-Magazin markup to LaTeX converter -- core library.
+ */
+
+#ifndef L2L_H
+#define L2L_H 1
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int
+l2l_convert_file_fh(FILE *in, FILE *out);
+
+int
+l2l_convert_file(const char *infile, const char *outfile);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! L2L_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/l2l_features.h.in b/src/l2l_features.h.in
new file mode 100644 (file)
index 0000000..47b3575
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * lm2latex - src/l2l_features.h
+ * Copyright (C) 2010 Sebastian Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Feature description of lm2latex.
+ */
+
+#ifndef L2L_FEATURES_H
+#define L2L_FEATURES_H 1
+
+#define L2L_VERSION_MAJOR @L2L_VERSION_MAJOR@
+#define L2L_VERSION_MINOR @L2L_VERSION_MINOR@
+#define L2L_VERSION_PATCH @L2L_VERSION_PATCH@
+
+#define L2L_VERSION_EXTRA "@L2L_VERSION_EXTRA@"
+
+#define L2L_VERSION_STRING "@L2L_VERSION_STRING@"
+
+#define L2L_VERSION_ENCODE(major, minor, patch) \
+       ((major) * 10000 + (minor) * 100 + (patch))
+
+#define L2L_VERSION L2L_VERSION_ENCODE(L2L_VERSION_MAJOR, \
+               L2L_VERSION_MINOR, L2L_VERSION_PATCH)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+unsigned int
+l2l_version(void);
+
+const char *
+l2l_version_string(void);
+
+const char *
+l2l_version_extra(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! L2L_FEATURES_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/l2l_scanner.l b/src/l2l_scanner.l
new file mode 100644 (file)
index 0000000..d769e91
--- /dev/null
@@ -0,0 +1,675 @@
+/*
+ * lm2latex - src/l2l_scanner.l
+ * Copyright (C) 2010 Sebastian Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * A Linux-Magazin markup to LaTeX converter -- LM markup scanner.
+ */
+
+%{
+#if HAVE_CONFIG_H
+#      include "config.h"
+#endif
+
+#include <assert.h>
+
+#include <ctype.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+       char  *data;
+       size_t size;
+       size_t len;
+} l2l_strbuf_t;
+
+#define L2L_STRBUF_INIT { NULL, 0, 0 }
+#define L2L_STRBUF_FREE(buf) ((buf)->size - (buf)->len)
+#define L2L_STRBUF_END(buf) ((buf)->data + (buf)->len)
+#define L2L_STRBUF_CLEAR(buf) ((buf)->len = 0)
+
+static int
+l2l_strbuf_append(l2l_strbuf_t *buf, const char *fmt, ...);
+
+static void
+l2l_strbuf_chomp(l2l_strbuf_t *buf);
+
+static void
+l2l_strbuf_destroy(l2l_strbuf_t *buf);
+
+#define L2L_YY_APPEND_OR_ECHO(strbuf, ...) \
+       do { \
+               if ((strbuf)) { \
+                       if (l2l_strbuf_append((strbuf), __VA_ARGS__)) \
+                               YY_FATAL_ERROR("ERROR: internal error"); \
+               } \
+               else \
+                       fprintf(yyout, __VA_ARGS__); \
+       } while (0)
+
+#define L2L_STATE_HEADER_OR_ABORT \
+       do { \
+               if (l2l_scanner_state != L2L_HEADER) \
+                       YY_FATAL_ERROR("ERROR: unexpected header definition"); \
+       } while (0)
+
+#define L2L_PATTERN_IS(str) (! strncmp(yytext, (str), strlen(str)))
+#define L2L_PATTERN_IS_HEADER \
+       (L2L_PATTERN_IS("@R:") || L2L_PATTERN_IS("@SW:") \
+               || L2L_PATTERN_IS("@D:") || L2L_PATTERN_IS("@T:") \
+               || L2L_PATTERN_IS("@A:") || L2L_PATTERN_IS("@V:"))
+%}
+
+%option yylineno
+%option noyywrap
+
+/* optimize for "fast scanning"; create an 8-bit scanner */
+%option fast 8bit
+
+/* generate a reentrant scanner in its own namespace */
+%option reentrant
+%option prefix="l2l_yy" outfile="lex.yy.c"
+
+%option verbose warn
+
+%s box
+
+%x caption box_caption
+%x command
+%x headertxt
+%x itemize
+%x listing
+%x title
+%x url
+
+COMMENT @#:.*
+WHITESPACE [ \t]
+
+ANY_COMMAND @(R|SW|D|T|V|A|L|ZT|LI|B|Bi|IT|IL|IE|KT|KL|KE|TT|TH|TL|TE):
+
+%%
+
+%{
+       enum {
+               L2L_HEADER,
+               L2L_BODY,
+               L2L_FIGURE,
+
+               L2L_UNQUOTED_TEXT,
+               L2L_QUOTED_TEXT,
+       };
+
+       int l2l_scanner_state = L2L_HEADER;
+       int l2l_quote_state   = L2L_UNQUOTED_TEXT;
+
+       /* text formatting */
+       _Bool l2l_italic      = 0;
+       _Bool l2l_bold        = 0;
+       _Bool l2l_code        = 0;
+       _Bool l2l_sup         = 0;
+       _Bool l2l_sub         = 0;
+       _Bool l2l_li_italic   = 0;
+
+       /* title page information */
+       l2l_strbuf_t l2l_category = L2L_STRBUF_INIT;
+       l2l_strbuf_t l2l_keyword  = L2L_STRBUF_INIT;
+       l2l_strbuf_t l2l_title    = L2L_STRBUF_INIT;
+       l2l_strbuf_t l2l_headline = L2L_STRBUF_INIT;
+       l2l_strbuf_t l2l_author   = L2L_STRBUF_INIT;
+       l2l_strbuf_t l2l_abstract = L2L_STRBUF_INIT;
+
+       /* temp. buffers */
+       l2l_strbuf_t l2l_caption  = L2L_STRBUF_INIT;
+       l2l_strbuf_t l2l_ititle   = L2L_STRBUF_INIT;
+
+       l2l_strbuf_t *l2l_buf_ptr = NULL;
+
+       int l2l_last_yystart = INITIAL;
+%}
+
+{COMMENT} { fprintf(yyout, "%%%s", yytext + strlen("@#:")); }
+
+<INITIAL,box,caption,box_caption,command,headertxt,itemize,title>{
+       /* LaTeX reserved characters */
+       [#$%&_{}] { L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "\\%s", yytext); }
+       [\^~]     { L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "\\%s{}", yytext); }
+       [\\]      { L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "$\\backslash$"); }
+
+       /* special characters */
+       /* '---' and '--' are handled by LaTeX */
+       \*\* L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "~");
+
+       :\* L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "\\,");
+
+       ["] {
+               if (l2l_quote_state == L2L_UNQUOTED_TEXT) {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "\"`");
+                       l2l_quote_state = L2L_QUOTED_TEXT;
+               }
+               else {
+                       assert(l2l_quote_state == L2L_QUOTED_TEXT);
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "\"'");
+                       l2l_quote_state = L2L_UNQUOTED_TEXT;
+               }
+       }
+
+       /* text formatting */
+       \<[iI]> {
+               if (l2l_italic) {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "}");
+                       l2l_italic = 0;
+               }
+               else {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "{\\itshape ");
+                       l2l_italic = 1;
+               }
+       }
+
+       \<[bB]> {
+               if (l2l_bold) {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "}");
+                       l2l_bold = 0;
+               }
+               else {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "{\\bfseries ");
+                       l2l_bold = 1;
+               }
+       }
+
+       \<[cC]> {
+               if (l2l_code) {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "}");
+                       l2l_code = 0;
+               }
+               else {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "{\\ttfamily ");
+                       l2l_code = 1;
+               }
+       }
+
+       \<[\+]> {
+               if (l2l_sup) {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "}}");
+                       l2l_sup = 0;
+               }
+               else {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr,
+                                       "\\ensuremath{^{\rmfamily ");
+                       l2l_sup = 1;
+               }
+       }
+
+       \<[\-]> {
+               if (l2l_sub) {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "}}");
+                       l2l_sub = 0;
+               }
+               else {
+                       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr,
+                                       "\\ensuremath{_{\rmfamily ");
+                       l2l_sub = 1;
+               }
+       }
+}
+
+\<[uU]> {
+       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "\\url{");
+       BEGIN(url);
+}
+
+<url>\<[uU]> {
+       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "}");
+       BEGIN(l2l_last_yystart);
+}
+
+<url>.|\n {
+       L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "%s", yytext);
+}
+
+       /*
+        * header
+        */
+
+@R:{WHITESPACE}* {
+       L2L_STATE_HEADER_OR_ABORT;
+       l2l_buf_ptr = &l2l_category;
+       BEGIN(headertxt);
+}
+
+@SW:{WHITESPACE}* {
+       L2L_STATE_HEADER_OR_ABORT;
+       l2l_buf_ptr = &l2l_keyword;
+       BEGIN(headertxt);
+}
+
+@D:{WHITESPACE}* {
+       L2L_STATE_HEADER_OR_ABORT;
+       l2l_buf_ptr = &l2l_title;
+       BEGIN(headertxt);
+}
+
+@T:{WHITESPACE}* {
+       L2L_STATE_HEADER_OR_ABORT;
+       l2l_buf_ptr = &l2l_headline;
+       BEGIN(headertxt);
+}
+
+@A:{WHITESPACE}* {
+       L2L_STATE_HEADER_OR_ABORT;
+       l2l_buf_ptr = &l2l_author;
+       BEGIN(headertxt);
+}
+
+@V:{WHITESPACE}* {
+       L2L_STATE_HEADER_OR_ABORT;
+       l2l_buf_ptr = &l2l_abstract;
+       BEGIN(headertxt);
+}
+
+<headertxt>{ANY_COMMAND} {
+       if (l2l_buf_ptr) {
+               l2l_strbuf_chomp(l2l_buf_ptr);
+               l2l_buf_ptr = NULL;
+       }
+
+       if (! L2L_PATTERN_IS_HEADER) {
+               assert(l2l_scanner_state == L2L_HEADER);
+
+               if (l2l_category.len || l2l_keyword.len
+                               || l2l_title.len || l2l_headline.len) {
+                       fprintf(yyout, "\\title{");
+
+                       if (l2l_category.len || l2l_keyword.len) {
+                               fprintf(yyout, "{\\scriptsize [");
+                               if (l2l_category.len) {
+                                       fprintf(yyout, "%s", l2l_category.data);
+                                       if (l2l_keyword.len)
+                                               fprintf(yyout, " $\\rightarrow$ ");
+                               }
+                               if (l2l_keyword.len)
+                                       fprintf(yyout, "%s", l2l_keyword.data);
+                               fprintf(yyout, "]}\\\\\n\\vspace{5mm}\n");
+                       }
+
+                       if (l2l_headline.len) {
+                               if (l2l_title.len)
+                                       fprintf(yyout, "\\normalsize %s\\\\\n\\LARGE ",
+                                                       l2l_title.data);
+                               fprintf(yyout, "%s", l2l_headline.data);
+                       }
+                       else if (l2l_title.len)
+                               fprintf(yyout, "%s", l2l_title.data);
+
+                       fprintf(yyout, "}\n");
+               }
+
+               if (l2l_author.len)
+                       fprintf(yyout, "\\author{%s}\n", l2l_author.data);
+               fprintf(yyout, "\\date{\\today}\n\n");
+
+               fprintf(yyout, "\\begin{document}\n\n");
+               l2l_scanner_state = L2L_BODY;
+
+               fprintf(yyout, "\\maketitle\n\n");
+
+               if (l2l_abstract.len)
+                       fprintf(yyout, "\\begin{abstract}\n%s\n"
+                                       "\\end{abstract}\n\n", l2l_abstract.data);
+
+               fprintf(yyout, "\\twocolumn\n\n");
+       }
+
+       yyless(0);
+       BEGIN(l2l_last_yystart);
+}
+
+<headertxt>{COMMENT} { /* ignore */ }
+
+<headertxt>.|\n {
+       if (l2l_buf_ptr)
+               if (l2l_strbuf_append(l2l_buf_ptr, "%s", yytext))
+                       YY_FATAL_ERROR("ERROR: internal error");
+       /* else ignore */
+}
+
+       /*
+        * body
+        */
+
+\n\n{WHITESPACE}*\*{WHITESPACE}* {
+       fprintf(yyout, "\n\n\\begin{itemize}\n\\item ");
+       BEGIN(itemize);
+}
+
+<itemize>({WHITESPACE}|\n)*\n\n |
+<itemize>({WHITESPACE}|\n)*{ANY_COMMAND} {
+       fprintf(yyout, "\n\\end{itemize}");
+       yyless(0);
+       BEGIN(l2l_last_yystart);
+}
+
+       /* this will produce a longer match and, thus, take precedence over
+        * the pattern above matching an empty line after the itemize */
+<itemize>({WHITESPACE}|\n)*\n{WHITESPACE}*\*{WHITESPACE}* {
+       fprintf(yyout, "\n\\item ");
+}
+<itemize>({WHITESPACE}|\n)*\n\n({WHITESPACE}{2,}|\t) ECHO;
+
+<itemize>. ECHO;
+
+@ZT:{WHITESPACE}* {
+       fprintf(yyout, "\\subsection*{");
+       BEGIN(command);
+}
+
+<command>({WHITESPACE}|\n)+{ANY_COMMAND} {
+       fprintf(yyout, "}");
+       yyless(0);
+       BEGIN(l2l_last_yystart);
+}
+
+<command>. ECHO;
+
+@LI:{WHITESPACE}*\n? {
+       /* \xb7 = MIDDLE DOT (iso-8859-1 and iso-8859-15) */
+       fprintf(yyout, "\\begin{lstlisting}[escapechar=\xb7]\n");
+       BEGIN(listing);
+}
+
+<listing>\n*{ANY_COMMAND} {
+       fprintf(yyout, "\n\\end{lstlisting}");
+       yyless(0);
+       BEGIN(l2l_last_yystart);
+}
+
+<listing>\<§§I> {
+       if (l2l_li_italic) {
+               L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "\xb7");
+               l2l_li_italic = 0;
+       }
+       else {
+               L2L_YY_APPEND_OR_ECHO(l2l_buf_ptr, "\xb7\\itshape ");
+               l2l_li_italic = 1;
+       }
+}
+
+<listing>§§\n /* ignore => lstlisting will insert appropriate arrows */;
+
+<listing>. ECHO;
+
+       /*
+        * images/boxes
+        */
+
+@(B|KT):{WHITESPACE}* {
+       if (l2l_scanner_state != L2L_FIGURE) {
+               fprintf(yyout, "\\begin{figure}\n");
+               l2l_scanner_state = L2L_FIGURE;
+       }
+       else {
+               l2l_scanner_state = L2L_BODY;
+       }
+
+       l2l_buf_ptr = &l2l_caption;
+       if (! strncmp(yytext, "@B:", 3))
+               BEGIN(caption);
+       else if (! strncmp(yytext, "@KT:", 4))
+               BEGIN(box_caption);
+       else
+               assert(0);
+}
+
+@Bi:.*\n {
+       char *filename;
+
+       yytext[yyleng - 1] = '\0';
+
+       filename = yytext + strlen("@Bi:");
+       while (isspace((int)*filename))
+               ++filename;
+
+       if (l2l_scanner_state != L2L_FIGURE) {
+               fprintf(yyout, "\\begin{figure}\n");
+               l2l_scanner_state = L2L_FIGURE;
+       }
+       else {
+               l2l_scanner_state = L2L_BODY;
+       }
+
+       fprintf(yyout, "\\includegraphics[width=\\columnwidth]{%s}\n", filename);
+
+       if (l2l_scanner_state == L2L_BODY)
+               fprintf(yyout, "\\end{figure}\n");
+}
+
+@KL: {
+       if (l2l_scanner_state != L2L_FIGURE) {
+               fprintf(yyout, "\\begin{figure}\n");
+               l2l_scanner_state = L2L_FIGURE;
+       }
+       else {
+               l2l_scanner_state = L2L_BODY;
+       }
+
+       l2l_last_yystart = box;
+       BEGIN(box);
+}
+
+<box>@(KE|L):{WHITESPACE}* {
+       fprintf(yyout, "\\end{figure}");
+       l2l_scanner_state = L2L_BODY;
+       l2l_last_yystart = INITIAL;
+       BEGIN(l2l_last_yystart);
+}
+
+<caption,box_caption>{ANY_COMMAND} {
+       if (l2l_buf_ptr) {
+               l2l_strbuf_chomp(l2l_buf_ptr);
+               l2l_buf_ptr = NULL;
+       }
+
+       if (l2l_caption.len) {
+               fprintf(yyout, "\\caption*{%s}\n", l2l_caption.data);
+               L2L_STRBUF_CLEAR(&l2l_caption);
+       }
+
+       if ((l2l_scanner_state == L2L_BODY) && (YY_START != box_caption))
+               fprintf(yyout, "\\end{figure}\n\n");
+
+       yyless(0);
+       if (YY_START == caption)
+               BEGIN(l2l_last_yystart);
+       else if (YY_START == box_caption) {
+               l2l_last_yystart = box;
+               BEGIN(box);
+       }
+       else
+               assert(0);
+}
+
+<caption,box_caption>{COMMENT} { /* ignore */ }
+
+<caption,box_caption>.|\n {
+       if (l2l_buf_ptr)
+               if (l2l_strbuf_append(l2l_buf_ptr, "%s", yytext))
+                       YY_FATAL_ERROR("ERROR: internal error");
+       /* else ignore */
+}
+
+       /*
+        * infos
+        */
+
+@IT:{WHITESPACE}* {
+       l2l_buf_ptr = &l2l_ititle;
+       BEGIN(title);
+}
+
+<title>{ANY_COMMAND} {
+       if (l2l_buf_ptr) {
+               l2l_strbuf_chomp(l2l_buf_ptr);
+               l2l_buf_ptr = NULL;
+       }
+
+       if (l2l_ititle.len) {
+               fprintf(yyout, "\\subsection*{\\rule{\\columnwidth}{1pt}"
+                               "\\newline %s}\n", l2l_ititle.data);
+               L2L_STRBUF_CLEAR(&l2l_ititle);
+       }
+
+       yyless(0);
+       BEGIN(l2l_last_yystart);
+}
+
+<title>{COMMENT} { /* ignore */ }
+
+<title>.|\n {
+       if (l2l_buf_ptr)
+               if (l2l_strbuf_append(l2l_buf_ptr, "%s", yytext))
+                       YY_FATAL_ERROR("ERROR: internal error");
+       /* else ignore */
+}
+
+@IL: { /* ignore */ }
+
+@IE: { /* ignore */ }
+
+       /*
+        * tables
+        */
+
+@TT: /* TODO */;
+
+@TH: /* TODO */;
+
+@TL: /* TODO */;
+
+@TE: /* TODO */;
+
+       /* let all other rules that match "@L:" have a higher preceedence */
+@L:{WHITESPACE}* { /* nothing to do */ }
+
+<<EOF>> {
+       if (l2l_scanner_state == L2L_BODY) {
+               fprintf(yyout, "\\end{document}\n");
+       }
+       else
+               YY_FATAL_ERROR("ERROR: incomplete document");
+
+       l2l_strbuf_destroy(&l2l_category);
+       l2l_strbuf_destroy(&l2l_keyword);
+       l2l_strbuf_destroy(&l2l_title);
+       l2l_strbuf_destroy(&l2l_headline);
+       l2l_strbuf_destroy(&l2l_author);
+       l2l_strbuf_destroy(&l2l_abstract);
+
+       l2l_strbuf_destroy(&l2l_caption);
+       l2l_strbuf_destroy(&l2l_title);
+
+       yyterminate();
+}
+
+%%
+
+static int
+l2l_strbuf_append(l2l_strbuf_t *buf, const char *fmt, ...)
+{
+       size_t  str_len;
+       va_list ap;
+
+       int status;
+
+       if ((! buf) || (! fmt))
+               return 0;
+
+       va_start(ap, fmt);
+       status = vsnprintf(NULL, 0, fmt, ap);
+       va_end(ap);
+
+       if (status <= 0)
+               return -1;
+
+       str_len = (size_t)status;
+
+       if (str_len > L2L_STRBUF_FREE(buf)) {
+               char  *new_data;
+               size_t new_size = 2 * buf->size;
+
+               if (str_len > buf->size)
+                       new_size += str_len;
+
+               new_data = realloc(buf->data, new_size + 1);
+               if (! new_data) {
+                       l2l_strbuf_destroy(buf);
+                       return -1;
+               }
+
+               buf->data = new_data;
+               buf->size = new_size;
+       }
+       assert(buf->len + str_len <= buf->size);
+
+       va_start(ap, fmt);
+       status = vsnprintf(L2L_STRBUF_END(buf), L2L_STRBUF_FREE(buf) + 1, fmt, ap);
+       va_end(ap);
+
+       if (status <= 0)
+               return -1;
+
+       assert((size_t)status == str_len);
+       buf->len += str_len;
+       buf->data[buf->len] = '\0';
+       return 0;
+} /* l2l_strbuf_append */
+
+static void
+l2l_strbuf_chomp(l2l_strbuf_t *buf)
+{
+       if (! buf)
+               return;
+
+       while (buf->len && isspace((int)buf->data[buf->len - 1])) {
+               buf->data[buf->len - 1] = '\0';
+               --buf->len;
+       }
+} /* l2l_strbuf_chomp */
+
+static void
+l2l_strbuf_destroy(l2l_strbuf_t *buf)
+{
+       if (! buf)
+               return;
+
+       if (buf->data)
+               free(buf->data);
+       memset(buf, 0, sizeof(*buf));
+} /* l2l_strbuf_destroy */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/lm2latex.c b/src/lm2latex.c
new file mode 100644 (file)
index 0000000..297dab7
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * lm2latex - src/lm2latex.c
+ * Copyright (C) 2010 Sebastian Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * A Linux-Magazin markup to LaTeX converter.
+ */
+
+#if HAVE_CONFIG_H
+#      include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "l2l.h"
+#include "l2l_features.h"
+
+#include <errno.h>
+
+#if HAVE_LIBGEN_H
+#      include <libgen.h>
+#else /* HAVE_LIBGEN_H */
+#      define basename(path) (path)
+#endif /* ! HAVE_LIBGEN_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+
+static void
+exit_usage(char *name, int status)
+{
+       printf(
+"Usage: %s <options> <infile> [<outfile>]\n"
+
+"\nOptions:\n"
+"  -h    display this help and exit\n"
+"  -V    display the version number and copyright\n"
+
+"\nlm2latex "L2L_VERSION_STRING L2L_VERSION_EXTRA", "PACKAGE_URL"\n",
+basename(name));
+       exit(status);
+} /* exit_usage */
+
+static void
+exit_version(void)
+{
+       printf("lm2latex version "L2L_VERSION_STRING L2L_VERSION_EXTRA", "
+                       "built "BUILD_DATE"\n"
+                       "Copyright (C) 2010 "PACKAGE_BUGREPORT"\n"
+
+                       "\nThis is free software under the terms of the BSD license, see "
+                       "the source for\ncopying conditions. There is NO WARRANTY; not "
+                       "even for MERCHANTABILITY or\nFITNESS FOR A PARTICULAR "
+                       "PURPOSE.\n");
+       exit(0);
+} /* exit_version */
+
+int
+main(int argc, char **argv)
+{
+       char *infile = NULL, *outfile = NULL;
+       FILE *in_fh, *out_fh;
+       int   args;
+
+       while (42) {
+               int opt = getopt(argc, argv, "hV");
+
+               if (-1 == opt)
+                       break;
+
+               switch (opt) {
+                       case 'h':
+                               exit_usage(argv[0], 0);
+                               break;
+                       case 'V':
+                               exit_version();
+                               break;
+                       default:
+                               exit_usage(argv[0], 1);
+               }
+       }
+
+       args = argc - optind;
+       if ((0 < args) && (args <= 2)) {
+               infile = argv[optind];
+
+               if (1 < args)
+                       outfile = argv[optind + 1];
+               else
+                       outfile = "-";
+       }
+       else
+               exit_usage(argv[0], 1);
+
+       if (! strcmp(infile, "-"))
+               in_fh = stdin;
+       else
+               in_fh = fopen(infile, "r");
+
+       if (! in_fh) {
+               fprintf(stderr, "lm2latex: FATAL: Failed to open ``%s'' "
+                               "for reading: %s\n", infile, strerror(errno));
+               return 1;
+       }
+
+       if (! strcmp(outfile, "-"))
+               out_fh = stdout;
+       else
+               out_fh = fopen(outfile, "w");
+
+       if (! out_fh) {
+               fprintf(stderr, "lm2latex: FATAL: Failed to open ``%s'' "
+                               "for writing: %s\n", outfile, strerror(errno));
+               return 1;
+       }
+
+       l2l_convert_file_fh(in_fh, out_fh);
+
+       if (in_fh != stdin)
+               fclose(in_fh);
+       if (out_fh != stdout)
+               fclose(out_fh);
+       return 0;
+} /* main */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/version-gen.sh b/version-gen.sh
new file mode 100755 (executable)
index 0000000..77457be
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/sh
+
+DEFAULT_VERSION="0.0.0.git"
+
+VERSION="$( git describe --tags 2> /dev/null \
+       | sed -e 's/lm2latex-//' || true )"
+
+if test -z "$VERSION"; then
+       VERSION="$DEFAULT_VERSION"
+else
+       git update-index -q --refresh || true
+       if test -n "$( git diff-index --name-only HEAD || true )"; then
+               VERSION="$VERSION-dirty"
+       fi
+fi
+
+VERSION="$( echo "$VERSION" | sed -e 's/-/./g' )"
+echo -n "$VERSION"
+
+OLD_VERSION=""
+if test -e version; then
+       OLD_VERSION=$( sed -ne 's/^VERSION="\(.*\)"/\1/p' version )
+fi
+
+if test "$OLD_VERSION" != "$VERSION"; then
+       VERSION_MAJOR=$( echo $VERSION | cut -d'.' -f1 )
+       VERSION_MINOR=$( echo $VERSION | cut -d'.' -f2 )
+       VERSION_PATCH=$( echo $VERSION | cut -d'.' -f3 )
+       VERSION_EXTRA="\"$( echo $VERSION | cut -d'.' -f4- )\""
+       test -z "$VERSION_EXTRA" || VERSION_EXTRA=".$VERSION_EXTRA"
+       (
+        echo "VERSION=\"$VERSION\""
+        echo "VERSION_MAJOR=$VERSION_MAJOR"
+        echo "VERSION_MINOR=$VERSION_MINOR"
+        echo "VERSION_PATCH=$VERSION_PATCH"
+        echo "VERSION_EXTRA=\"$VERSION_EXTRA\""
+        echo "VERSION_STRING=\"$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH\""
+       ) > version
+fi
+