summaryrefslogtreecommitdiff
path: root/update-menus
diff options
context:
space:
mode:
Diffstat (limited to 'update-menus')
-rw-r--r--update-menus/Makefile.am8
-rw-r--r--update-menus/Makefile.in425
-rw-r--r--update-menus/common.h52
-rw-r--r--update-menus/compose.hpp393
-rw-r--r--update-menus/exceptions.h102
-rw-r--r--update-menus/parsestream.cc467
-rw-r--r--update-menus/parsestream.h176
-rw-r--r--update-menus/stringtoolbox.cc182
-rw-r--r--update-menus/stringtoolbox.h111
-rw-r--r--update-menus/update-menus.cc1097
-rw-r--r--update-menus/update-menus.h157
11 files changed, 3170 insertions, 0 deletions
diff --git a/update-menus/Makefile.am b/update-menus/Makefile.am
new file mode 100644
index 0000000..97d2796
--- /dev/null
+++ b/update-menus/Makefile.am
@@ -0,0 +1,8 @@
+bin_PROGRAMS = update-menus
+
+update_menus_SOURCES = update-menus.cc update-menus.h \
+ stringtoolbox.cc stringtoolbox.h \
+ parsestream.cc parsestream.h \
+ exceptions.h \
+ common.h \
+ compose.hpp
diff --git a/update-menus/Makefile.in b/update-menus/Makefile.in
new file mode 100644
index 0000000..f3bfefa
--- /dev/null
+++ b/update-menus/Makefile.in
@@ -0,0 +1,425 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+bin_PROGRAMS = update-menus$(EXEEXT)
+subdir = update-menus
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS)
+am_update_menus_OBJECTS = update-menus.$(OBJEXT) \
+ stringtoolbox.$(OBJEXT) parsestream.$(OBJEXT)
+update_menus_OBJECTS = $(am_update_menus_OBJECTS)
+update_menus_LDADD = $(LDADD)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
+ -o $@
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(update_menus_SOURCES)
+DIST_SOURCES = $(update_menus_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+MO_PROGRAMS = @MO_PROGRAMS@
+MO_SECTIONS = @MO_SECTIONS@
+MO_SUTOROOT = @MO_SUTOROOT@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PO_PROGRAMS = @PO_PROGRAMS@
+PO_SECTIONS = @PO_SECTIONS@
+PO_SUTOROOT = @PO_SUTOROOT@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+update_menus_SOURCES = update-menus.cc update-menus.h \
+ stringtoolbox.cc stringtoolbox.h \
+ parsestream.cc parsestream.h \
+ exceptions.h \
+ common.h \
+ compose.hpp
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu update-menus/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu update-menus/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+update-menus$(EXEEXT): $(update_menus_OBJECTS) $(update_menus_DEPENDENCIES)
+ @rm -f update-menus$(EXEEXT)
+ $(CXXLINK) $(update_menus_LDFLAGS) $(update_menus_OBJECTS) $(update_menus_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parsestream.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stringtoolbox.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/update-menus.Po@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-exec install-exec-am \
+ install-info install-info-am install-man install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-binPROGRAMS uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/update-menus/common.h b/update-menus/common.h
new file mode 100644
index 0000000..9338eb1
--- /dev/null
+++ b/update-menus/common.h
@@ -0,0 +1,52 @@
+// -*- mode: c++; -*-
+//
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <libintl.h>
+
+const int MAX_LINE = 10240;
+
+#define _(string) gettext(string)
+#define LOCALEDIR "/usr/share/locale"
+
+#define COMMAND_VAR "command"
+#define ICON_VAR "icon"
+#define NEEDS_VAR "needs"
+#define SECTION_VAR "section"
+#define BASESECTION_VAR "basesection"
+#define SORT_VAR "sort"
+#define TITLE_VAR "title"
+#define HOTKEY_VAR "hotkey"
+#define HINTS_VAR "hints"
+#define PACKAGE_VAR "package"
+#define PRIVATE_ENTRYCOUNT_VAR "__PRIVATE__ENTRYCOUNT"
+#define PRIVATE_ENTRYINDEX_VAR "__PRIVATE__ENTRYINDEX"
+#define PRIVATE_LEVEL_VAR "__PRIVATE__LEVEL"
+
+//conditionals in the menuentry files (like "package(vi)")
+#define COND_PACKAGE "package"
+
+#define CONFIGMENUS "/etc/menu"
+#define PACKAGEMENUSLIB "/usr/lib/menu"
+#define PACKAGEMENUS "/usr/share/menu"
+#define MENUMENUS "/usr/share/menu/default"
+#define USERMENUS ".menu"
+
+#define MENUMETHODS "/etc/menu-methods"
+#define USERMETHODS ".menu-methods"
+
+#define UPMEN_LOCKFILE "/var/run/update-menus.pid"
+#define DPKG_LOCKFILE "/var/lib/dpkg/lock"
+#define TRANSLATE_FILE "/etc/menu-methods/translate_menus"
+#define USERTRANSLATE ".menu-methods/translate_menus"
+#define CONFIG_FILE "/etc/menu-methods/menu.config"
+#define USERCONFIG ".menu-methods/menu.config"
+
+
+#define TRANSLATE_TRANS "translate"
+#define SUBSTITUTE_TRANS "substitute"
+#define SUBTRANSLATE_TRANS "subtranslate"
+#define ENDTRANSLATE_TRANS "endtranslate"
+
+#endif /* COMMON_H */
diff --git a/update-menus/compose.hpp b/update-menus/compose.hpp
new file mode 100644
index 0000000..4ea1621
--- /dev/null
+++ b/update-menus/compose.hpp
@@ -0,0 +1,393 @@
+/* Defines String::compose(fmt, arg...) for easy, i18n-friendly
+ * composition of strings.
+ *
+ * Version 1.0.
+ *
+ * Copyright (c) 2002 Ole Laursen <olau@hardworking.dk>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA.
+ */
+
+//
+// Basic usage is like
+//
+// std::cout << String::compose("This is a %1x%2 matrix.", rows, cols);
+//
+// See http://www.cs.auc.dk/~olau/compose/ or the included README.compose for
+// more details.
+//
+
+#ifndef STRING_COMPOSE_H
+#define STRING_COMPOSE_H
+
+#include <sstream>
+#include <string>
+#include <list>
+#include <map> // for multimap
+
+namespace StringPrivate
+{
+ // the actual composition class - using string::compose is cleaner, so we
+ // hide it here
+ class Composition
+ {
+ public:
+ // initialize and prepare format string on the form "text %1 text %2 etc."
+ explicit Composition(std::string fmt);
+
+ // supply an replacement argument starting from %1
+ template <typename T>
+ Composition &arg(const T &obj);
+
+ // compose and return string
+ std::string str() const;
+
+ private:
+ std::ostringstream os;
+ int arg_no;
+
+ // we store the output as a list - when the output string is requested, the
+ // list is concatenated to a string; this way we can keep iterators into
+ // the list instead of into a string where they're possibly invalidated on
+ // inserting a specification string
+ typedef std::list<std::string> output_list;
+ output_list output;
+
+ // the initial parse of the format string fills in the specification map
+ // with positions for each of the various %?s
+ typedef std::multimap<int, output_list::iterator> specification_map;
+ specification_map specs;
+ };
+
+ // helper for converting spec string numbers
+ inline int char_to_int(char c)
+ {
+ switch (c) {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ default: return -1000;
+ }
+ }
+
+ inline bool is_number(int n)
+ {
+ switch (n) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+
+ // implementation of class Composition
+ template <typename T>
+ inline Composition &Composition::arg(const T &obj)
+ {
+ os << obj;
+
+ std::string rep = os.str();
+
+ if (!rep.empty()) { // manipulators don't produce output
+ for (specification_map::const_iterator i = specs.lower_bound(arg_no),
+ end = specs.upper_bound(arg_no); i != end; ++i) {
+ output_list::iterator pos = i->second;
+ ++pos;
+
+ output.insert(pos, rep);
+ }
+
+ os.str(std::string());
+ //os.clear();
+ ++arg_no;
+ }
+
+ return *this;
+ }
+
+ inline Composition::Composition(std::string fmt)
+ : arg_no(1)
+ {
+ std::string::size_type b = 0, i = 0;
+
+ // fill in output with the strings between the %1 %2 %3 etc. and
+ // fill in specs with the positions
+ while (i < fmt.length()) {
+ if (fmt[i] == '%' && i + 1 < fmt.length()) {
+ if (fmt[i + 1] == '%') { // catch %%
+ fmt.replace(i, 2, "%");
+ ++i;
+ }
+ else if (is_number(fmt[i + 1])) { // aha! a spec!
+ // save string
+ output.push_back(fmt.substr(b, i - b));
+
+ int n = 1; // number of digits
+ int spec_no = 0;
+
+ do {
+ spec_no += char_to_int(fmt[i + n]);
+ spec_no *= 10;
+ ++n;
+ } while (i + n < fmt.length() && is_number(fmt[i + n]));
+
+ spec_no /= 10;
+ output_list::iterator pos = output.end();
+ --pos; // safe since we have just inserted a string>
+
+ specs.insert(specification_map::value_type(spec_no, pos));
+
+ // jump over spec string
+ i += n;
+ b = i;
+ }
+ else
+ ++i;
+ }
+ else
+ ++i;
+ }
+
+ if (i - b > 0) // add the rest of the string
+ output.push_back(fmt.substr(b, i - b));
+ }
+
+ inline std::string Composition::str() const
+ {
+ // assemble string
+ std::string str;
+
+ for (output_list::const_iterator i = output.begin(), end = output.end();
+ i != end; ++i)
+ str += *i;
+
+ return str;
+ }
+}
+
+// now for the real thing(s)
+namespace String
+{
+ // a series of functions which accept a format string on the form "text %1
+ // more %2 less %3" and a number of templated parameters and spits out the
+ // composited string
+ template <typename T1>
+ inline std::string compose(const std::string &fmt, const T1 &o1)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1);
+ return c.str();
+ }
+
+ template <typename T1, typename T2>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13, const T14 &o14)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14,
+ typename T15>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13, const T14 &o14, const T15 &o15)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
+ return c.str();
+ }
+}
+
+
+#endif // STRING_COMPOSE_H
diff --git a/update-menus/exceptions.h b/update-menus/exceptions.h
new file mode 100644
index 0000000..f4c818d
--- /dev/null
+++ b/update-menus/exceptions.h
@@ -0,0 +1,102 @@
+/*
+ * Debian menu system -- update-menus and install-menu
+ * update-menus/exceptions.h
+ *
+ * Copyright (C) 1996-2003 Joost Witteveen,
+ * Copyright (C) 2002-2004 Bill Allombert and Morten Brix Pedersen.
+ *
+ * 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 2 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 with
+ * the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
+ * if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Written by Joost Witteveen.
+ */
+
+#ifndef EXCEPTIONS_H
+#define EXCEPTIONS_H
+
+#include <string>
+#include <iostream>
+#include "common.h"
+#include "compose.hpp"
+
+namespace exceptions {
+ /** Base class for general exceptions */
+ class genexcept {
+ public:
+ virtual void report() {
+ std::cerr << message() << std::endl;
+ }
+ virtual std::string message() const {
+ return _("Unknown error.");
+ }
+ virtual ~genexcept() { }
+ };
+
+ /** Base exception class for classes returning string error messages */
+ class except_string : public genexcept {
+ protected:
+ std::string msg;
+ public:
+ except_string(std::string s) : msg(s) { }
+ std::string message() const {
+ return String::compose(_("Unknown error, message=%1"), msg);
+ }
+ };
+
+ /** Exception class to be thrown when a file fails to open */
+ class ferror_open : public except_string {
+ public:
+ ferror_open(std::string s):except_string(s) { }
+ std::string message() const {
+ return String::compose(_("Unable to open file \"%1\"."), msg);
+ }
+ };
+
+ /** Exception class to be thrown when opening a pipe for reading */
+ class pipeerror_read : public except_string {
+ public:
+ pipeerror_read(std::string s) : except_string(s) { }
+ std::string message() {
+ return String::compose(_("Failed to pipe data through \"%1\" (pipe opened for reading)."), msg);
+ }
+ };
+
+ /** Exception class to be thrown when a fatal error occured */
+ class informed_fatal : public genexcept {
+ public:
+ std::string message() const { return ""; }
+ };
+
+ /** Exception to be thrown when a required tag is missing */
+ class missing_tag : public except_string {
+ std::string file;
+ public:
+ missing_tag(std::string f, std::string s) : except_string(s), file(f) { }
+ std::string message() {
+ return String::compose(_("%1: missing required tag: \"%2\""), file, msg);
+ }
+ };
+
+ /** Exception to be thrown when package isn't installed. */
+ class cond_inst_false : public genexcept { };
+
+ /** Exception to be thrown when opening a directory failed */
+ class dir_error_read : public genexcept { };
+
+}
+
+
+#endif /* EXCEPTIONS_H */
diff --git a/update-menus/parsestream.cc b/update-menus/parsestream.cc
new file mode 100644
index 0000000..8fd5c21
--- /dev/null
+++ b/update-menus/parsestream.cc
@@ -0,0 +1,467 @@
+/*
+ * Debian menu system -- update-menus and install-menu
+ * update-menus/parsestream.cc
+ *
+ * Copyright (C) 1996-2003 Joost Witteveen,
+ * Copyright (C) 2002-2004 Bill Allombert and Morten Brix Pedersen.
+ *
+ * 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 2 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 with
+ * the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
+ * if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Written by Joost Witteveen.
+ */
+
+#include <cstdlib>
+#include <fstream>
+#include <cstring>
+#include "parsestream.h"
+#include "stringtoolbox.h"
+
+using std::string;
+using namespace exceptions;
+
+Regex::Regex(const char *s)
+{
+ patt = new re_pattern_buffer;
+ patt->translate = 0;
+ patt->fastmap = 0;
+ patt->buffer = 0;
+ patt->allocated = 0;
+ re_set_syntax(RE_CHAR_CLASSES);
+ re_compile_pattern(s, std::strlen(s), patt);
+}
+
+parsestream::parsestream(std::istream &in, std::string other)
+ : in_constructor(true), stdin_file(true), otherdir(other), doescaping(true)
+{
+ init(&in,_("(probably) stdin"));
+ in_constructor = false;
+}
+
+parsestream::parsestream(const std::string &name, std::string other)
+ : in_constructor(true), stdin_file(false), otherdir(other), doescaping(true)
+{
+ std::istream *f = new std::ifstream(name.c_str());
+
+ if(!f && (name[0] != '/')) {
+ string add = other + "/" + name;
+ f = new std::ifstream(add.c_str());
+ }
+ if (!*f)
+ throw ferror_open(name);
+
+ init(f, name);
+ in_constructor = false;
+}
+
+void parsestream::preprocess(std::string &s)
+{
+ // Disregards lines that start with a #
+ // Set filename if line starts with "!F"
+
+ string compat;
+ string::size_type i = 0;
+ while ((i < s.length()) && isspace(s[i]))
+ i++;
+ if (i)
+ s = s.substr(i);
+
+ if (s.empty())
+ return;
+
+ switch (s[0])
+ {
+ case '#':
+ s.erase();
+ return;
+ case '!':
+ if (!s.empty()) {
+ switch (s[1])
+ {
+ case 'F':
+ set_linenumber(0);
+ set_filename(s.substr(3));
+ //cerr<<"filename="<<filename()<<", s.substr="<<s.substr(2)<<endl;
+ s.erase();
+ return;
+ case 'L':
+ set_linenumber(stringtoi(s.substr(2))-1);
+ //cerr<<"filename="<<filename()<<", s.substr="<<s.substr(2)<<endl;
+ s.erase();
+ return;
+ case 'C':
+ compat = s.substr(3);
+ if (compat == "menu-1")
+ seteolmode(eol_newline);
+ else if (compat == "menu-2")
+ seteolmode(eol_semicolon);
+ else
+ throw unknown_compat(this, compat);
+ s.erase();
+ return;
+ default:
+ if (contains(s, "!include")) {
+ string t(s.substr(strlen("!include ")));
+ if (t[0] == '/' || filenames.empty()) {
+ new_file(t);
+ } else {
+ string name = string_parent(filename()) + '/' + t;
+ if (std::ifstream(name.c_str()))
+ new_file(name);
+ else
+ new_file(otherdir + '/' + t);
+ }
+ }
+ return;
+ }
+ }
+ return;
+ default:;
+ }
+}
+
+void parsestream::new_file(const std::string &name)
+{
+ std::ifstream *f = new std::ifstream(name.c_str());
+
+ init(f,name);
+}
+
+void parsestream::init(std::istream *in, std::string name)
+{
+ if (!in_constructor)
+ if (!in->good())
+ throw ferror_open(name);
+ pos = 0;
+ eolmode = eol_newline;
+ istreams.push_back(in);
+ linenumbers.push_back(0);
+ filenames.push_back(name);
+ try {
+ new_line();
+ } catch (endoffile) { }
+}
+
+void parsestream::close_file()
+{
+ if (!stdin_file && !istreams.empty())
+ delete istreams.back();
+
+ if (istreams.size() > 1) {
+ istreams.pop_back();
+ linenumbers.pop_back();
+ filenames.pop_back();
+ } else {
+ throw endoffile(this);
+ }
+}
+
+void parsestream::new_line()
+{
+ while (!istreams.empty())
+ {
+ if (!current_istr()->good()) {
+ close_file();
+ continue;
+ }
+ buffer.erase();
+ pos = 0;
+ while (current_istr()->good() && buffer.empty())
+ {
+ getline(*current_istr(), buffer);
+ set_linenumber(linenumber()+1);
+ try {
+ preprocess(buffer);
+ } catch (unknown_compat p) { p.report(); }
+ buffer = rmtrailingspace(buffer);
+ }
+ while (current_istr()->good() &&
+ (((eolmode==eol_newline) && (buffer[buffer.length()-1]=='\\')) ||
+ ((eolmode==eol_semicolon) && (buffer[buffer.length()-1]!=';'))))
+ {
+ string s;
+ getline(*current_istr(), s);
+ set_linenumber(linenumber()+1);
+ switch(eolmode)
+ {
+ case eol_newline:
+ buffer = buffer.substr(0,buffer.length()-1) + ' ' + rmtrailingspace(s);
+ break;
+ case eol_semicolon:
+ buffer = buffer + ' ' + rmtrailingspace(s);
+ break;
+ }
+ }
+ if (buffer.empty()) {
+ close_file();
+ continue;
+ }
+ if (current_istr()->eof() && !buffer.empty() &&
+ (((eolmode==eol_newline)&&(buffer[buffer.length()-1]=='\\'))||
+ ((eolmode==eol_semicolon)&&(buffer[buffer.length()-1]!=';')))){
+ //a "\" at the end of a file: unconditional error (don't unwind etc)
+ //(or no ; at eof, when eolmode=; . Same unconditional error.
+ throw endoffile(this);
+ }
+ if (eolmode == eol_semicolon)
+ if (buffer[buffer.length()-1] == ';')
+ buffer.erase(buffer.length()-1, 1);
+ return;
+ }
+ if (in_constructor)
+ return;
+ else
+ throw endoffile(this);
+}
+
+char parsestream::get_char()
+{
+ if (buffer.empty())
+ throw endoffile(this);
+ if (pos >= buffer.length())
+ throw endofline(this);
+
+ return buffer[pos++];
+}
+
+char parsestream::put_back(char c)
+{
+ if (c) {
+ if (pos) {
+ pos--;
+ buffer[pos] = c;
+ } else {
+ buffer = c + buffer;
+ }
+ }
+ return c;
+}
+
+string parsestream::get_line()
+{
+ string s = buffer.substr(pos);
+ if (s.empty())
+ throw endofline(this);
+ buffer.erase();
+ try {
+ skip_line();
+ } catch (endoffile) { }
+ return s;
+}
+
+string parsestream::get_name()
+{
+ char c;
+ string s;
+ skip_space();
+ try {
+ while((c=get_char()) &&
+ (isalnum(c)||(c=='_')||(c=='-')||(c=='+')||(c=='.')))
+ s+=c;
+ if(c)
+ put_back(c);
+ } catch (endofline d) { }
+ return s;
+}
+
+string parsestream::get_name(const Regex &r)
+{
+ char str[2]={0,0};
+ char &c=str[0];
+ string s;
+ skip_space();
+ try {
+ while ((c = get_char()))
+ {
+ if (re_match(r.pattern(), str, 1, 0, 0) > 0)
+ s += c;
+ else
+ break;
+ }
+ if (c)
+ put_back(c);
+ } catch (endofline) { }
+ return s;
+}
+
+string parsestream::get_stringconst()
+{
+ string s;
+ skip_space();
+ char c = get_char();
+ try {
+ if (c == '\"') { // " found at the beginning
+ while ((c = get_char()) && (c != '\"'))
+ {
+ if (c == '\\') {
+ c = get_char();
+ switch (c)
+ {
+ case 't': c = '\t'; break;
+ case 'b': c = '\b'; break;
+ case 'n': c = '\n'; break;
+ default:
+ if (!doescaping)
+ s += '\\';
+ break;
+ }
+ }
+ s += c;
+ }
+ if (c != '\"')
+ throw char_expected(this, "\"");
+ } else { // no " at beginning
+ s = c;
+ while ((c = get_char()) && !isspace(c))
+ s += c;
+ }
+ } catch (endofline p) { }
+
+ return s;
+}
+
+string parsestream::get_eq_stringconst()
+{
+ skip_space();
+ char c = get_char();
+ if (c != '=')
+ throw char_expected(this, "=");
+ return get_stringconst();
+}
+
+bool parsestream::get_boolean()
+{
+ string s = get_name();
+
+ if (s == "true")
+ return true;
+ else if (s == "false")
+ return false;
+ else
+ throw boolean_expected(this, s);
+}
+
+bool parsestream::get_eq_boolean()
+{
+ skip_space();
+ char c = get_char();
+ if (c != '=')
+ throw char_expected(this,"=");
+ return get_boolean();
+}
+
+int parsestream::get_integer()
+{
+ char c;
+ string s;
+
+ try {
+ skip_space();
+ while((c=get_char())&&((isdigit(c)||(c=='-'))))
+ s += c;
+ } catch (endofline d) { }
+
+ return atoi(s.c_str());
+}
+
+int parsestream::get_eq_integer()
+{
+ skip_space();
+ char c = get_char();
+ if (c != '=')
+ throw char_expected(this, "=");
+ return get_integer();
+}
+
+double parsestream::get_double()
+{
+ char c;
+ string s;
+
+ skip_space();
+ try {
+ while ((c=get_char()) &&
+ (isdigit(c) || (c=='.')||(c=='E')||(c=='e')||(c=='+')||(c=='-')))
+ s += c;
+ } catch (endofline d) { }
+
+ return atof(s.c_str());
+}
+
+double parsestream::get_eq_double()
+{
+ skip_space();
+ char c = get_char();
+ if(c!='=')
+ throw char_expected(this, "=");
+
+ return get_double();
+}
+
+void parsestream::skip_line()
+{
+ buffer.erase();
+ try {
+ new_line();
+ } catch (endoffile d) { }
+}
+
+void parsestream::skip_space()
+{
+ char c;
+ while (isspace(c=get_char()));
+ if(c)
+ put_back(c);
+}
+
+void parsestream::skip_char(char expect)
+{
+ char c = get_char();
+ if(c != expect) {
+ put_back(c);
+ char buf[2] = {c, '\0'};
+ throw char_expected(this, buf);
+ }
+}
+
+void except_pi::report()
+{
+ if (pi) {
+ std::cerr << String::compose(_("In file \"%1\", at (or in the definition that ends at) line %2:\n"),
+ pi->filename(),
+ pi->linenumber());
+
+ string::size_type startpos = 0;
+ if (pi->pos > 50)
+ startpos = pi->pos - 50;
+
+ if (startpos)
+ std::cerr << "[...]";
+
+ std::cerr << pi->buffer.substr(startpos) << std::endl;
+
+ if (startpos)
+ std::cerr << "[...]";
+
+ for (string::size_type i = 1+startpos; i< pi->pos; ++i)
+ std::cerr << ' ';
+
+ std::cerr << '^' << std::endl;
+ } else {
+ std::cerr << _("Somewhere in input file:\n");
+ }
+ std::cerr << message() << std::endl;
+}
diff --git a/update-menus/parsestream.h b/update-menus/parsestream.h
new file mode 100644
index 0000000..9b2e282
--- /dev/null
+++ b/update-menus/parsestream.h
@@ -0,0 +1,176 @@
+/*
+ * Debian menu system -- update-menus and install-menu
+ * update-menus/parsestream.h
+ *
+ * Copyright (C) 1996-2003 Joost Witteveen,
+ * Copyright (C) 2002-2004 Bill Allombert and Morten Brix Pedersen.
+ *
+ * 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 2 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 with
+ * the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
+ * if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Written by Joost Witteveen.
+ */
+
+#ifndef PARSESTREAM_H
+#define PARSESTREAM_H
+
+#include <vector>
+#include <string>
+#include <regex.h>
+#include "exceptions.h"
+
+class Regex {
+ struct re_pattern_buffer* patt;
+public:
+ Regex(const char *s);
+ ~Regex() { delete patt; }
+
+ struct re_pattern_buffer* pattern() const { return patt; }
+};
+
+class parsestream {
+public:
+ enum eol_type { eol_newline, eol_semicolon };
+private:
+ std::vector<int> linenumbers;
+ std::vector<std::string> filenames;
+ std::vector<std::istream *> istreams;
+
+ void set_linenumber(int l) { linenumbers.back() = l; }
+ void set_filename(const std::string &s) { filenames.back() = s; }
+ std::istream *current_istr() const { return istreams.back(); }
+
+ bool in_constructor;
+ void new_line();
+ void preprocess(std::string &s);
+ void new_file(const std::string &name);
+ void init(std::istream *in, std::string name);
+ void close_file();
+ bool stdin_file;
+ eol_type eolmode;
+ std::string otherdir;
+
+public:
+ parsestream(std::istream &in, std::string other = "");
+ parsestream(const std::string &name, std::string other = "");
+
+ bool doescaping;
+ std::string::size_type pos;
+ std::string buffer;
+ std::string filename() const { return filenames.back(); }
+ int linenumber() const { return linenumbers.back(); }
+
+ bool good() const { return istreams.size(); }
+ char get_char();
+ char put_back(char);
+ std::string get_name();
+ std::string get_name(const Regex &);
+ std::string get_stringconst();
+ std::string get_eq_stringconst();
+ bool get_boolean();
+ bool get_eq_boolean();
+ int get_integer();
+ int get_eq_integer();
+ double get_double();
+ double get_eq_double();
+ std::string get_line();
+ void skip_line();
+ void skip_space();
+ void skip_char(char expect);
+ void seteolmode(eol_type mode) { eolmode = mode; }
+};
+
+
+namespace exceptions {
+ /** Exception classes for parsestream class. */
+ class except_pi : public genexcept {
+ public:
+ parsestream *pi;
+ except_pi(parsestream *p) : pi(p) { }
+ void report();
+ };
+
+ /** Exception for parsestream class using string error messages */
+ class except_pi_string : public except_pi {
+ protected:
+ std::string msg;
+ public:
+ except_pi_string(parsestream *p, std::string s) : except_pi(p), msg(s) { }
+ std::string message() const {
+ return String::compose(_("Unknown error, message=%1"), msg);
+ }
+ };
+
+ /** Exception to be thrown when unexpected end of file occurs */
+ class endoffile : public except_pi {
+ public:
+ endoffile(parsestream *p) : except_pi(p) { }
+ std::string message() const { return _("Unexpected end of file."); }
+ };
+
+ /** Exception to be thrown when unexpected end of line occurs */
+ class endofline : public except_pi {
+ public:
+ endofline(parsestream *p) : except_pi(p) { }
+ std::string message() const { return _("Unexpected end of line."); }
+ };
+
+ /** Exception to be thrown when an identifer was expected but wasn't found */
+ class ident_expected : public except_pi {
+ public:
+ ident_expected(parsestream *p) : except_pi(p) { }
+ const char *errormsg() const { return _("Identifier expected."); }
+ };
+
+ /** Exception to be thrown when a character was expected but wasn't found */
+ class char_expected : public except_pi_string {
+ public:
+ char_expected(parsestream *p, std::string s):except_pi_string(p,s) { }
+ std::string message() const {
+ return String::compose(_("Expected: \"%1\""), msg);
+ }
+ };
+
+ /** Exception to be thrown when a character was found but not expected */
+ class char_unexpected : public except_pi_string {
+ public:
+ char_unexpected(parsestream *p, std::string s):except_pi_string(p,s) { }
+ std::string message() const {
+ return String::compose(_("Unexpected character: \"%1\""), msg);
+ }
+ };
+
+ /** Exception to be thrown when an boolean was expected but not found */
+ class boolean_expected : public except_pi_string {
+ public:
+ boolean_expected(parsestream *p, std::string s):except_pi_string(p,s) { }
+ std::string message() const {
+ return String::compose(_("Boolean (either true or false) expected.\n"
+ "Found: \"%1\""), msg);
+ }
+ };
+
+ /** Exception to be thrown when an unknown compatibility mode was chosen */
+ class unknown_compat : public except_pi_string {
+ public:
+ unknown_compat(parsestream *p, std::string s):except_pi_string(p,s) { }
+ std::string message() const {
+ return String::compose(_("Unknown compat mode: \"%1\""), msg);
+ }
+ };
+}
+
+#endif /* PARSESTREAM_H */
diff --git a/update-menus/stringtoolbox.cc b/update-menus/stringtoolbox.cc
new file mode 100644
index 0000000..c00b007
--- /dev/null
+++ b/update-menus/stringtoolbox.cc
@@ -0,0 +1,182 @@
+/*
+ * Debian menu system -- update-menus and install-menu
+ * update-menus/stringtoolbox.cc
+ *
+ * Copyright (C) 1996-2003 Joost Witteveen,
+ * Copyright (C) 2002-2004 Bill Allombert and Morten Brix Pedersen.
+ *
+ * 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 2 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 with
+ * the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
+ * if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Written by Joost Witteveen.
+ */
+
+#include <algorithm>
+#include "exceptions.h"
+#include "stringtoolbox.h"
+
+using std::string;
+
+bool contains(const string& str, const string& sub, string::size_type pos)
+{
+ if (str.length() < (sub.length()+pos))
+ return false;
+
+ return (str.substr(pos, sub.length()) == sub);
+}
+
+bool contains(const string& str, char c)
+{
+ return str.find(c) != string::npos;
+}
+
+string rmtrailingspace(string str)
+{
+ while(!str.empty() && (isspace(str[str.length()-1])))
+ str.erase(str.length()-1);
+ return str;
+}
+
+string escapewith(const string& str, const string& esc, const string& with)
+{
+ string t;
+ for (string::size_type i = 0; i != str.length(); ++i)
+ {
+ if (esc.find(str[i]) != string::npos)
+ t += with;
+ t += str[i];
+ }
+ return t;
+}
+
+string escape(const string &str, const string &esc)
+{
+ return escapewith(str, esc, "\\");
+}
+
+string lowercase(string str)
+{
+ std::transform(str.begin(), str.end(), str.begin(), tolower);
+ return str;
+}
+
+string uppercase(string str)
+{
+ std::transform(str.begin(), str.end(), str.begin(), toupper);
+ return str;
+}
+
+string replacewith(string str, const string &replace, const string &with)
+{
+ if (replace.length() != with.length())
+ throw exceptions::except_string(_("replacewith($string, $replace, $with): $replace and $with must have the same length."));
+
+ for (string::size_type i = 0; i <= replace.length(); ++i)
+ {
+ std::replace(str.begin(), str.end(), replace[i], with[i]);
+ }
+ return str;
+}
+
+string replace(string str, const string& repl, const string& with)
+{
+ string::size_type pos = str.find(repl);
+
+ while (pos != string::npos && !repl.empty())
+ {
+ str = str.replace(pos, repl.length(), with);
+ pos = str.find(repl, pos+with.length());
+ }
+ return str;
+}
+
+/* This is isalnum with C locale enforced */
+static int c_isalnum(char s)
+{
+ if (s>='a' && s<='z') return 1;
+ if (s>='0' && s<='9') return 1;
+ if (s>='A' && s<='Z') return 1;
+ return 0;
+}
+
+string chartohex(unsigned char c)
+{
+ std::ostringstream o;
+ o << std::hex << (int) c;
+ return o.str();
+}
+
+string cppesc(const string &s)
+{
+ string t;
+ for (string::size_type i = 0; i!= s.length(); ++i)
+ {
+ if (!(c_isalnum(s[i]) || (s[i] == '_')))
+ t += '$' + chartohex(s[i]);
+ else
+ t += s[i];
+ }
+ return t;
+}
+
+int stringtoi(const string &str)
+{
+ return atoi(str.c_str());
+}
+
+string itostring(int i)
+{
+ std::ostringstream o;
+ o << i;
+ return o.str();
+}
+
+string string_parent(const string& str)
+{
+ string::size_type pos = str.find_last_of('/');
+ if (pos == string::npos)
+ return "";
+ else
+ return str.substr(0, pos);
+}
+
+string string_basename(const string& str)
+{
+ return string_stripdir(string_parent(str));
+}
+
+string string_stripdir(const string& str)
+{
+ string::size_type pos = str.find_last_of('/');
+ if (pos == string::npos)
+ return str;
+ else
+ return str.substr(pos+1);
+}
+
+void break_char(const string &str, std::vector<string>& container, char breakchar)
+{
+ string::size_type lastPos = str.find_first_not_of(breakchar, 0);
+ string::size_type pos = str.find_first_of(breakchar, lastPos);
+
+ while (string::npos != pos || string::npos != lastPos)
+ {
+ container.push_back(str.substr(lastPos, pos - lastPos));
+
+ lastPos = str.find_first_not_of(breakchar, pos);
+ pos = str.find_first_of(breakchar, lastPos);
+ }
+}
diff --git a/update-menus/stringtoolbox.h b/update-menus/stringtoolbox.h
new file mode 100644
index 0000000..ff934c9
--- /dev/null
+++ b/update-menus/stringtoolbox.h
@@ -0,0 +1,111 @@
+/*
+ * Debian menu system -- update-menus and install-menu
+ * update-menus/stringtoolbox.h
+ *
+ * Copyright (C) 1996-2003 Joost Witteveen,
+ * Copyright (C) 2002-2004 Bill Allombert and Morten Brix Pedersen.
+ *
+ * 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 2 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 with
+ * the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
+ * if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Written by Joost Witteveen.
+ */
+
+#ifndef STRINGTOOLBOX_H
+#define STRINGTOOLBOX_H
+
+#include <vector>
+#include <string>
+
+/** Check whether str contains sub at pos. */
+bool contains(const std::string& str, const std::string &sub, std::string::size_type pos = 0);
+
+/** Check whether str contains c */
+bool contains(const std::string& str, char c);
+
+/** Remove all whitespace at end of str */
+std::string rmtrailingspace(std::string str);
+
+/** Search str and escape all characters in esc with the string 'with'
+ *
+ * Call with: escapewith("hello $world, %dir", "$%", "\\")
+ * Returns: "hello \$world, \%dir"
+ */
+std::string escapewith(const std::string &str, const std::string &esc, const std::string &with);
+
+/** Search str and escape all characters in esc with backspace (\).
+ *
+ * Call with: escape("hello $world, %dir", "$%")
+ * Returns: "hello \$world, \%dir"
+ */
+std::string escape(const std::string &s, const std::string &esc);
+
+/** Return str in lowercase. */
+std::string lowercase(std::string str);
+
+/** Return str in uppercase. */
+std::string uppercase(std::string str);
+
+/** Search str. For each character in replace, substitute it with the
+ * corresponding character in with.
+ *
+ * Call with: replacewith_string("hello $world, %dir", "$% ", "123")
+ * Returns: "hello31world,32dir"
+ */
+std::string replacewith(std::string str, const std::string &replace, const std::string &with);
+
+/** Search str. Replace all occurences of repl with with. */
+std::string replace(std::string str, const std::string& repl, const std::string& with);
+
+/** Escape anything that isn't a letter, number or _ with $<hex-ascii-code>.
+ * So for example '-' is replaced by '$2D'. */
+std::string cppesc(const std::string &s);
+
+/** Return the integer representation of str. If conversion is not possible,
+ * returns 0. */
+int stringtoi(const std::string &str);
+
+/** Returns the string representation of i. */
+std::string itostring(int i);
+
+/** Return the hexadecimal representation of c, as a string. */
+std::string chartohex(unsigned char c);
+
+/** Returns the 'parent' of a string in the form /<foo>/<bar> where '/'
+ * separates parents.
+ *
+ * Call with: string_parent("/Debian/Apps/Editors/Emacs")
+ * Returns: "/Debian/Apps/Editors"
+ */
+std::string string_parent(const std::string& str);
+
+/** Returns the last part of the parent directory. In other words, the same
+ * as string_stripdir(string_parent(str)). */
+std::string string_basename(const std::string& str);
+
+/** Returns the last element of a string in the form /foo/bar where '/'
+ * separates elements.
+ *
+ * Call with: string_stripdir("/Debian/Apps/Editors/Emacs")
+ * Returns: "Emacs"
+ */
+std::string string_stripdir(const std::string& str);
+
+/** Tokenize a string str with a separater breakchar. Insert all tokens into
+ * the vector passed to the function by reference. */
+void break_char(const std::string& str, std::vector<std::string>& container, char breakchar);
+
+#endif /* STRINGTOOLBOX_H */
diff --git a/update-menus/update-menus.cc b/update-menus/update-menus.cc
new file mode 100644
index 0000000..6d7ccc8
--- /dev/null
+++ b/update-menus/update-menus.cc
@@ -0,0 +1,1097 @@
+/*
+ * Debian menu system -- update-menus
+ * update-menus/update-menus.cc
+ *
+ * Copyright (C) 1996-2003 Joost Witteveen,
+ * Copyright (C) 2002-2004 Bill Allombert and Morten Brix Pedersen.
+ *
+ * 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 2 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 with
+ * the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
+ * if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Written by Joost Witteveen;
+ * read_pkginfo function by Tom Lees, run_menumethods by both.
+ */
+
+#include <fstream>
+#include <sstream>
+#include <set>
+#include <cstdio>
+#include <cstdlib>
+#include <unistd.h>
+#include <getopt.h>
+#include <cerrno>
+#include <cctype>
+#include <cstring>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <dirent.h>
+#include <signal.h>
+#include <syslog.h>
+#include <pwd.h>
+#include "config.h"
+#include "update-menus.h"
+#include "stringtoolbox.h"
+
+using std::set;
+using std::vector;
+using std::string;
+using std::cout;
+using std::cerr;
+using std::ostream;
+using namespace exceptions;
+
+static const char * home_dir;
+
+set<string> installed_packages;
+set<string> menufiles_processed;
+int total_menuentries;
+
+translateinfo *transinfo;
+configinfo config;
+bool is_root;
+
+/** Try to open a directory. Throws dir_error_read if failed, and a DIR*
+ * descriptor if succeeded.
+ */
+DIR *open_dir_check(const string& dirname)
+{
+ DIR *dir = opendir(dirname.c_str());
+
+ if (!dir)
+ throw dir_error_read();
+
+ return dir;
+}
+
+/** Checks whether a file is executable */
+bool executable(const string &s)
+{
+ struct stat st;
+ if (stat(s.c_str(), &st)) {
+ return false;
+ } else {
+ return (((st.st_mode & S_IXOTH) ||
+ ((st.st_mode & S_IXGRP) && st.st_gid == getegid ()) ||
+ ((st.st_mode & S_IXUSR) && st.st_uid == geteuid ())) &&
+ (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
+ }
+}
+
+/** Checks whether a package is installed */
+bool is_pkg_installed(const string& filename)
+{
+ if (config.no_dpkg_check || contains(filename, "local."))
+ return true;
+ else
+ return installed_packages.find(filename) != installed_packages.end();
+}
+
+menuentry::menuentry(parsestream &i, const std::string& file, const std::string& shortfile)
+{
+ char c = i.get_char();
+ if (c == '?') {
+ read_menuentry(i);
+ } else {
+ // old format menuentry
+ if (!is_pkg_installed(shortfile)) {
+ i.skip_line();
+ throw cond_inst_false();
+ }
+ data[PACKAGE_VAR] = shortfile;
+ i.put_back(c);
+ data[NEEDS_VAR] = i.get_stringconst();
+ data[SECTION_VAR] = i.get_stringconst();
+ i.get_stringconst(); //id is unused
+ data[ICON_VAR] = i.get_stringconst();
+ data[TITLE_VAR] = i.get_stringconst();
+ i.skip_space();
+ data[COMMAND_VAR] = i.get_line();
+ }
+ check_req_tags(file);
+}
+
+/** This function checks the package name to see if it's valid and installed.
+ * Multiple package names can exist, seperated by comma. */
+void menuentry::check_pkg_validity(parsestream &i, std::string &name)
+{
+ string function = i.get_name();
+ if (function != COND_PACKAGE)
+ throw unknown_cond_package(&i, function);
+
+ // Read an entry, such as (foo) or (foo, bar)
+ char c;
+ while ((c = i.get_char()))
+ {
+ if (c == ',' || c == '(') {
+ // A package name follows.
+ string pkgname = i.get_name();
+
+ if (!name.empty())
+ name += ", ";
+
+ name += pkgname;
+ if (!is_pkg_installed(pkgname)) {
+ i.skip_line();
+ config.report(String::compose(
+ _("file %1 line %2:\nDiscarding entry requiring missing package %3.")
+ , i.filename(), i.linenumber(), pkgname)
+ , configinfo::report_verbose);
+ throw cond_inst_false();
+ }
+ } else if (c == ')') {
+ // We are finished with the package requirements.
+ return;
+ }
+ }
+ i.skip_char(')');
+}
+
+/** Checks whether we have all the tags we need. */
+void menuentry::check_req_tags(const std::string& filename)
+{
+ vector<string> need;
+
+ need.push_back(SECTION_VAR);
+ need.push_back(TITLE_VAR);
+ need.push_back(NEEDS_VAR);
+
+ for(vector<string>::iterator i = need.begin(); i != need.end(); ++i)
+ {
+ std::map<string, string>::const_iterator j = data.find(*i);
+ if ((j == data.end()) || j->second.empty())
+ throw missing_tag(filename, *i);
+ }
+
+}
+
+/** Parse a menuentry from a parsestream */
+void menuentry::read_menuentry(parsestream &i)
+{
+ // a new format menuentry
+ string name;
+
+ // read available info
+ check_pkg_validity(i, name);
+ data[PACKAGE_VAR] = name;
+ i.skip_space();
+ i.skip_char(':');
+ i.doescaping = false;
+
+ // read menuentry:
+ try {
+ char c;
+ do {
+ string key = i.get_name();
+ string value = i.get_eq_stringconst();
+ data[key] = value;
+ c = i.get_char();
+ i.put_back(c);
+ } while(c);
+ }
+ catch (endofline) { }
+ i.skip_line();
+}
+
+void menuentry::output(std::vector<std::string> &s)
+{
+ string t;
+ std::map<string, string>::const_iterator i = data.begin();
+ if (i != data.end()) {
+ while (true)
+ {
+ t += i->first + "=\"" + i->second + '"';
+ i++;
+ if (i == data.end())
+ break;
+ else
+ t += ' ';
+ }
+ }
+ t += '\n';
+ s.push_back(t);
+}
+
+/////////////////////////////////////////////////////
+// configinfo
+//
+
+/** Handle key/value configuration pair */
+void configinfo::parse_config(const std::string &key, const std::string& value)
+{
+ if (key=="compat") {
+ if (value=="menu-1") compat = parsestream::eol_newline;
+ else if(value=="menu-2") compat = parsestream::eol_semicolon;
+ } else if(key=="verbosity") {
+ if (value=="quiet") verbosity=report_quiet;
+ else if(value=="normal") verbosity=report_normal;
+ else if(value=="verbose") verbosity=report_verbose;
+ else if(value=="debug") verbosity=report_debug;
+ } else if(key=="method") {
+ if (value=="stdout") method=method_stdout;
+ else if(value=="stderr") method=method_stderr;
+ else if(value=="syslog") {
+
+ method = method_syslog;
+ string facility;
+ string priority;
+
+ std::istringstream ss(value);
+ ss >> facility;
+ ss >> priority;
+
+ if(facility=="auth") syslog_facility=LOG_AUTH;
+ else if(facility=="authpriv") syslog_facility=LOG_AUTHPRIV;
+ else if(facility=="authcron") syslog_facility=LOG_CRON;
+ else if(facility=="authdaemon") syslog_facility=LOG_AUTHPRIV;
+ else if(facility=="authkern") syslog_facility=LOG_KERN;
+ else if(facility=="authlocal0") syslog_facility=LOG_LOCAL0;
+ else if(facility=="authlocal1") syslog_facility=LOG_LOCAL1;
+ else if(facility=="authlocal2") syslog_facility=LOG_LOCAL2;
+ else if(facility=="authlocal3") syslog_facility=LOG_LOCAL3;
+ else if(facility=="authlocal4") syslog_facility=LOG_LOCAL4;
+ else if(facility=="authlocal5") syslog_facility=LOG_LOCAL5;
+ else if(facility=="authlocal6") syslog_facility=LOG_LOCAL6;
+ else if(facility=="authlocal7") syslog_facility=LOG_LOCAL7;
+ else if(facility=="authlpr") syslog_facility=LOG_LPR;
+ else if(facility=="authmail") syslog_facility=LOG_MAIL;
+ else if(facility=="authnews") syslog_facility=LOG_NEWS;
+ else if(facility=="authsyslog") syslog_facility=LOG_SYSLOG;
+ else if(facility=="authuser") syslog_facility=LOG_USER;
+ else if(facility=="authuucp") syslog_facility=LOG_UUCP;
+
+ if(priority=="emerg") syslog_priority=LOG_EMERG;
+ else if(priority=="alert") syslog_priority=LOG_ALERT;
+ else if(priority=="crit") syslog_priority=LOG_CRIT;
+ else if(priority=="err") syslog_priority=LOG_ERR;
+ else if(priority=="warning") syslog_priority=LOG_WARNING;
+ else if(priority=="notice") syslog_priority=LOG_NOTICE;
+ else if(priority=="info") syslog_priority=LOG_INFO;
+ else if(priority=="debug") syslog_priority=LOG_DEBUG;
+ }
+ }
+}
+
+void configinfo::read_file(const std::string& filename)
+{
+ std::ifstream config_file(filename.c_str());
+
+ if (!config_file)
+ return;
+
+ string str;
+ while (getline(config_file, str))
+ {
+ // read a key and a value, seperated by '='.
+ string::size_type pos = str.find("=");
+ if (pos == string::npos)
+ return;
+
+ // parse key and value
+ parse_config(str.substr(0, pos), str.substr(pos + 1));
+ }
+
+}
+
+void configinfo::report(const std::string &message, verbosity_type v)
+{
+ if(v <= verbosity) {
+ switch(method) {
+ case method_stdout:
+ cout << String::compose("update-menus[%1]: %2\n",getpid(),message);
+ break;
+ case method_stderr:
+ cerr << String::compose("update-menus[%1]: %2\n",getpid(),message);
+ break;
+ case method_syslog:
+ openlog("update-menus",LOG_PID,syslog_facility);
+ syslog(syslog_priority,message.c_str());
+ closelog();
+ }
+ }
+}
+
+/////////////////////////////////////////////////////
+// translate stuff
+//
+
+void translate::process(menuentry &m, const std::string &search)
+{
+ if (search == match)
+ m.data[replace_var] = replace;
+}
+
+void subtranslate::process(menuentry &m, const std::string &search)
+{
+ if (contains(search, match))
+ m.data[replace_var] = replace;
+}
+
+void substitute::process(menuentry &m, const std::string &search)
+{
+ if (contains(search, match)) {
+ string *current = &(m.data[replace_var]);
+ if (current->length() >= match.length())
+ *current = replace + current->substr(match.length());
+ }
+}
+
+translateinfo::translateinfo(const std::string &filename)
+{
+ parsestream *i = 0;
+ try {
+ i = new parsestream(filename);
+
+ Regex ident("[[:alpha:]][[:alnum:]_]*");
+ /*Translation here and below refer to the file
+ /etc/menu-methods/translate_menus that allow to rename and reorganize
+ menu entries automatically. It does not refer to the localisation
+ (translation to other languages).
+ */
+ config.report(String::compose(_("Reading translation rules in %1."), i->filename()),
+ configinfo::report_verbose);
+ while (true)
+ {
+ string name = i->get_name(ident);
+ i->skip_space();
+ string match_var = i->get_name(ident);
+ i->skip_space();
+ i->skip_char('-');
+ i->skip_char('>');
+ i->skip_space();
+ string replace_var = i->get_name(ident);
+
+ i->skip_line();
+ while (true)
+ {
+ i->skip_space();
+ string match = i->get_stringconst();
+ if (match == ENDTRANSLATE_TRANS) {
+ i->skip_line();
+ break;
+ }
+ if (match[0]=='#') {
+ i->skip_line();
+ continue;
+ }
+ i->skip_space();
+ string replace = i->get_stringconst();
+ trans_class *trcl;
+ if (name == TRANSLATE_TRANS) {
+ trcl = new translate(match,replace,replace_var);
+ } else if (name == SUBTRANSLATE_TRANS) {
+ trcl = new subtranslate(match,replace,replace_var);
+ } else if (name == SUBSTITUTE_TRANS) {
+ trcl = new substitute(match,replace,replace_var);
+ } else {
+ i->skip_line();
+ continue;
+ }
+
+ std::pair<const string, trans_class *> p(match, trcl);
+
+ trans[match_var].push_back(p);
+ i->skip_line();
+ }
+ }
+ } catch(endoffile p) { }
+ delete i;
+}
+
+void translateinfo::process(menuentry &m)
+{
+ std::map<string, std::vector<trans_pair> >::const_iterator i;
+ std::vector<trans_pair>::const_iterator j;
+ string *search;
+ for (i = trans.begin(); i != trans.end(); ++i)
+ {
+ search = &m.data[i->first];
+ for (j = i->second.begin(); j != i->second.end(); ++j)
+ {
+ j->second->process(m, *search);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////
+// Installed Package Status:
+//
+
+/** Read in list of installed packages */
+void read_pkginfo()
+{
+ // Here we get the list of *installed* packages from dpkg, using sed to
+ // retrieve the package name.
+ string inst_states="installed\\|triggers-awaited\\|triggers-pending";
+ string pkgs = "dpkg-query --show --showformat=\"\\${status} \\${provides} \\${package}\\n\" | sed -n -e \"/" + inst_states
+ + " /{s/^.*\\("+inst_states+"\\) *//; s/[, ][, ]*/\\n/g; p}\"";
+ pkgs = "exec /bin/bash -o pipefail -c '" + pkgs + "'";
+ FILE *status = popen(pkgs.c_str(), "r");
+
+ if (!status)
+ throw pipeerror_read(pkgs.c_str());
+
+ config.report(_("Reading installed packages list..."),
+ configinfo::report_verbose);
+
+ while (!feof(status))
+ {
+ char tmp[MAX_LINE];
+ if (fgets(tmp, MAX_LINE, status) != NULL) {
+ if (tmp[strlen(tmp)-1] == '\n')
+ tmp[strlen(tmp)-1] = 0;
+
+ installed_packages.insert(tmp);
+ }
+ }
+ if (pclose(status))
+ throw pipeerror_read(pkgs.c_str());
+}
+
+/** Read a menufile and create one (or more) menu entries for it.
+ *
+ * Returns the number of menu entries read. */
+int read_menufile(const string &filename, const string &shortfilename,
+ vector<string> &menudata)
+{
+ int menuentries = 0;
+ parsestream *ps = 0;
+ std::stringstream *sstream = 0;
+
+ try {
+ // Whenever we encounter a file which has the executable bit set, we
+ // need to execute it and read its output from stdout.
+
+ if (executable(filename)) {
+ FILE *exec_file = popen(filename.c_str(), "r");
+
+ sstream = new std::stringstream;
+
+ if (!exec_file)
+ throw pipeerror_read(filename.c_str());
+
+ while (!feof(exec_file))
+ {
+ char tmp[MAX_LINE];
+ if (fgets(tmp, MAX_LINE, exec_file) != NULL)
+ *sstream << tmp;
+ }
+ int status = pclose(exec_file);
+
+ try {
+ ps = new parsestream(*sstream);
+ } catch (endoffile d) {
+ if (status || sstream->str().size())
+ cerr << String::compose(_("Execution of %1 generated no output or returned an error.\n"), filename);
+ throw endoffile(d);
+ }
+ } else {
+ ps = new parsestream(filename);
+ }
+ } catch (endoffile p) {
+ delete sstream;
+ return menuentries;
+ }
+
+ try {
+ ps->seteolmode(config.compat);
+
+ bool wrote_filename = false;
+ int linenr = 1;
+ while (true)
+ {
+ try {
+ menuentry m(*ps, filename, shortfilename);
+ linenr++;
+ if (transinfo)
+ transinfo->process(m);
+ if (!wrote_filename) {
+ menudata.push_back(string("!F ") + filename + '\n');
+ wrote_filename = true;
+ }
+ m.output(menudata);
+ menuentries++;
+ if (ps->linenumber() != linenr) {
+ menudata.push_back(string("!L ") + itostring(ps->linenumber())+ '\n');
+ linenr = ps->linenumber();
+ }
+ }
+ catch (cond_inst_false) { }
+ }
+ }
+ catch (endoffile p) { }
+ catch (missing_tag& exc) {
+ std::cerr << exc.message() << std::endl;
+ std::cerr << _("Skipping file because of errors...\n");
+ }
+ catch (except_pi& exc) {
+ exc.report();
+ std::cerr << _("Skipping file because of errors...\n");
+ }
+
+ delete ps;
+ delete sstream;
+ return menuentries;
+}
+
+/** Read a directory full of menu files */
+void read_menufilesdir(vector<string> &menudata)
+{
+ for(vector<string>::const_iterator method_i = config.menufilesdir.begin();
+ method_i != config.menufilesdir.end();
+ ++method_i)
+ {
+ int menuentries = 0;
+ string dirname = *method_i;
+ config.report(String::compose(_("Reading menu-entry files in %1."), dirname),
+ configinfo::report_verbose);
+ try {
+ struct dirent *entry;
+ DIR *dir = open_dir_check(dirname);
+ while((entry = readdir(dir)))
+ {
+ string name = entry->d_name;
+ if ((name != "README") && (name != "core") && (name[0] != '.') &&
+ (name.find(".bak") == string::npos) &&
+ (!contains(name, "menu.config")) &&
+ (name[name.length()-1] != '~'))
+
+ if (menufiles_processed.find(name) == menufiles_processed.end()) {
+ menufiles_processed.insert(name);
+ name = dirname + "/" + name;
+ struct stat st;
+ int r = stat(name.c_str(),&st);
+ try {
+ if ((!r) && (S_ISREG(st.st_mode)||S_ISLNK(st.st_mode)))
+ menuentries += read_menufile(name,entry->d_name, menudata);
+ }
+ catch (endofline p) {
+ cerr << String::compose(_("Error reading %1.\n"), name);
+ }
+ }
+ }
+ } catch (dir_error_read p) { }
+ total_menuentries += menuentries;
+ config.report(String::compose(_("%1 menu entries found (%2 total)."), menuentries, total_menuentries), configinfo::report_verbose);
+ }
+}
+
+/** Run a menu-method with --remove*/
+void run_menumethod_remove(string methodname)
+{
+ const char *args[] = { methodname.c_str(), "--remove", NULL };
+ pid_t child, r;
+ int status;
+
+ config.report(String::compose(_("Running method: %1 --remove"), methodname),
+ configinfo::report_verbose);
+ if (!(child=fork())) {
+ // child:
+ execv(args[0],(char **)args);
+ exit(1);
+ }
+ // parent:
+ signal(SIGPIPE,SIG_IGN);
+ r = wait4(child, &status, 0, NULL);
+ signal(SIGPIPE,SIG_DFL);
+ if (r == -1)
+ config.report(String::compose(_("Script %1 could not be executed."), methodname),
+ configinfo::report_quiet);
+ if (WEXITSTATUS(status))
+ config.report(String::compose(_("Script %1 returned error status %2."), methodname, WEXITSTATUS(status)),
+ configinfo::report_quiet);
+ else if (WIFSIGNALED(status))
+ config.report(String::compose(_("Script %1 received signal %2."), methodname, WTERMSIG(status)),
+ configinfo::report_quiet);
+}
+
+/** Run a menu method */
+void run_menumethod(string methodname, const vector<string> &menudata)
+{
+ if (config.remove_menu)
+ {
+ run_menumethod_remove(methodname);
+ return;
+ }
+ int fds[2];
+ const char *args[] = { methodname.c_str(), NULL };
+ pid_t child, r;
+ int status;
+
+ config.report(String::compose(_("Running method: %1"), methodname), configinfo::report_verbose);
+
+ if (pipe(fds) == -1) {
+ config.report(_("Cannot create pipe."), configinfo::report_quiet);
+ exit(1);
+ }
+
+ if (!(child=fork())) {
+ // child:
+ close(fds[1]);
+ close(0);
+ dup(fds[0]);
+
+ //???
+ // The next 2 lines seem strange! But if I leave it out,
+ // pipes (in commands executed in the /etc/menu-method/* scripts as
+ // postrun etc) don't work when ran from dpkg. (When run from dpkg,
+ // we elsewhere close all filedescriptors. And apparently
+ // we need STDOUT open somehow in order to make the pipes work.
+ // (changed by suggestion of Fabian Sturm <f@rtfs.org>, for GNU Hurd,
+ // bug #105674
+
+ close(1);
+ open("/dev/null", O_RDWR);
+ execv(args[0],(char **)args);
+ exit(1);
+ } else {
+ // parent:
+ signal(SIGPIPE,SIG_IGN);
+ close(fds[0]);
+
+ for(vector<string>::const_iterator i = menudata.begin(); i != menudata.end(); ++i)
+ write(fds[1], i->c_str(), i->length());
+
+ close(fds[1]);
+ r = wait4(child, &status, 0, NULL);
+ signal(SIGPIPE,SIG_DFL);
+ }
+ if (r == -1)
+ config.report(String::compose(_("Script %1 could not be executed."), methodname),
+ configinfo::report_quiet);
+ if (WEXITSTATUS(status))
+ config.report(String::compose(_("Script %1 returned error status %2."), methodname, WEXITSTATUS(status)),
+ configinfo::report_quiet);
+ else if (WIFSIGNALED(status))
+ config.report(String::compose(_("Script %1 received signal %2."), methodname, WTERMSIG(status)),
+ configinfo::report_quiet);
+}
+
+/** Run a directory full of menu methods */
+void run_menumethoddir(const string &dirname, const vector<string> &menudata)
+{
+ struct dirent *entry;
+ char *s;
+
+ config.report(String::compose(_("Running menu-methods in %1."), dirname), configinfo::report_verbose);
+ DIR *dir = open_dir_check(dirname);
+ while ((entry = readdir (dir)) != NULL) {
+ if (!strcmp(entry->d_name, "README") || !strcmp(entry->d_name, "core"))
+ continue;
+ for (s = entry->d_name; *s != '\0'; s++)
+ {
+ if (!(isalnum (*s) || (*s == '_') || (*s == '-')))
+ break;
+ }
+ if (*s != '\0')
+ continue;
+
+ string method = dirname + "/" + entry->d_name;
+
+ if (executable(method))
+ run_menumethod(method, menudata);
+ }
+ closedir (dir);
+}
+
+/** Try to create a lock file for update-menus */
+int create_lock()
+{
+ // return lock fd if succesful, false if unsuccesfull.
+ int fd = true;
+
+ if (is_root) {
+ fd = open(UPMEN_LOCKFILE,O_WRONLY|O_CREAT,00644);
+
+ if (flock(fd,LOCK_EX|LOCK_NB)) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ config.report(String::compose(_("Other update-menus processes are already locking %1, quitting."), UPMEN_LOCKFILE),
+ configinfo::report_verbose);
+ } else {
+ config.report(String::compose(_("Cannot lock %1: %2 - Aborting."), UPMEN_LOCKFILE, strerror(errno)),
+ configinfo::report_quiet);
+ }
+ return false;
+
+ }
+
+ std::ostringstream ss;
+ ss << getpid() << "\n";
+ if (write(fd, ss.str().c_str(), ss.str().length()) < 1) {
+ config.report(String::compose(_("Cannot write to lockfile %1 - Aborting."), UPMEN_LOCKFILE),
+ configinfo::report_quiet);
+ return false;
+ }
+ }
+ return fd;
+}
+
+/** Try to remove update-menus lock */
+void remove_lock()
+{
+ if (is_root) {
+ if (unlink(UPMEN_LOCKFILE))
+ config.report(String::compose(_("Cannot remove lockfile %1."), UPMEN_LOCKFILE),
+ configinfo::report_normal);
+ }
+}
+
+/** Check whether dpkg is locked
+ * return 1 if DPKG_LOCKFILE is locked and we should wait
+ * return 0 if we don't need to wait (not root, or no dpkg lock)
+ * when in doubt return 0 to avoid deadlocks.
+ */
+int check_dpkglock()
+{
+ int fd;
+ struct flock fl;
+ if (!is_root)
+ {
+ config.report(_("Update-menus is run by user."), configinfo::report_verbose);
+ return 0;
+ }
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fd = open(DPKG_LOCKFILE, O_RDWR|O_CREAT|O_TRUNC, 0660);
+ if (fd == -1)
+ /* Probably /var/lib/dpkg does not exist.
+ * Most probably dpkg is not running.
+ */
+ return 0;
+ if (fcntl(fd,F_GETLK,&fl) == -1)
+ {
+ /* Probably /var/lib/dpkg filesystem does not support
+ * locks.
+ */
+ close(fd);
+ return 0;
+ }
+ close(fd);
+ return fl.l_type!=F_UNLCK;
+}
+
+void exit_on_signal(int signr)
+{
+ exit(0);
+}
+
+/** Check whether dpkg is running, and fork into background waiting if it is
+ * Or, if this dpkg supports triggers, do nothing unless run with a --trigger
+ * flag.
+ */
+void wait_dpkg(string &stdoutfile)
+{
+ int child;
+ int parentpid;
+ int i,r;
+ // This function checks to see if dpkg is running, and if
+ // so, forks into the background, to let dpkg go on installing
+ // packages. After dpkg finished (and wrote the new packages file),
+ // this function wakes up, and returns.
+
+
+ // Check if the dpkg lock is taken
+ if (check_dpkglock())
+ {
+ if (! config.trigger)
+ {
+ // Try to use dpkg-trigger to trigger the menu update later.
+ if (system("dpkg-trigger --by-package=menu /usr/share/menu >/dev/null 2>&1") == 0)
+ {
+ // Success, so no need to do anything now.
+ exit(0);
+ }
+ }
+
+ // If we can't get the u-m lock, probably another process is waiting
+ // for dpkg. We can safely exit.
+ r = create_lock();
+ if (!r)
+ exit(0);
+
+ if (config.trigger)
+ {
+ // This is the real update-menus run, caused by the trigger.
+ // So ignore the dpkg lock, and continue with updating in
+ // the foreground.
+ return;
+ }
+
+ // OK, we need to fork, create log file name and tell user about it.
+ stdoutfile = string("/tmp/update-menus.")+itostring(getpid());
+ config.report(String::compose(_("Waiting for dpkg to finish (forking to background).\n"
+ "(checking %1)"), DPKG_LOCKFILE),
+ configinfo::report_normal);
+ config.report(String::compose(_("Further output (if any) will appear in %1."), stdoutfile),
+ configinfo::report_normal);
+
+ // Now do the fork
+ parentpid=getpid();
+ if ((child=fork())) {
+ if (child==-1) {
+ perror("update-menus: fork");
+ exit(1);
+ }
+ exit(0);
+ } else {
+ // Close all fd's except the lock fd, for daemon mode.
+ for (i=0;i<32;i++) {
+ if (i != r)
+ close(i);
+ }
+ setsid();
+ // Sit and wait until dpkg is finished ...
+ while(check_dpkglock())
+ sleep(2);
+ }
+ } else {
+ // Dpkg is not locked. Try to get the u-m lock. If we can't get it,
+ // there may be a real problem.
+ r = create_lock();
+ if (!r)
+ exit(1);
+
+ config.report(_("Dpkg is not locking dpkg status area, good."),
+ configinfo::report_verbose);
+ }
+}
+
+/** Print usage information */
+void usage(ostream &c)
+{
+ c <<
+ _( /* This is the update-menus --help message*/
+ "Usage: update-menus [options] \n"
+ "Gather packages data from the menu database and generate menus for\n"
+ "all programs providing menu-methods, usually window-managers.\n"
+ " -d Output debugging messages.\n"
+ " -v Be verbose about what is going on.\n"
+ " -h, --help This message.\n"
+ " --menufilesdir=<dir> Add <dir> to the lists of menu directories to search.\n"
+ " --menumethod=<method> Run only the menu method <method>.\n"
+ " --nodefaultdirs Disable the use of all the standard menu directories.\n"
+ " --nodpkgcheck Do not check if packages are installed.\n"
+ " --remove Remove generated menus instead.\n"
+ " --stdout Output menu list in format suitable for piping to\n"
+ " install-menu.\n"
+ " --version Output version information and exit.\n");
+}
+
+struct option long_options[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { "menufilesdir", required_argument, NULL, 'f'},
+ { "menumethod", required_argument, NULL, 'm'},
+ { "nodefaultdirs", no_argument, NULL, 'n'},
+ { "nodpkgcheck", no_argument, NULL, 'c'},
+ { "stdout", no_argument, NULL, 's'},
+ { "version", no_argument, NULL, 'V'},
+ { "remove", no_argument, NULL, 'r'},
+ { "trigger", no_argument, NULL, 'T'},
+ { NULL, 0, NULL, 0 } };
+
+
+/** Parse commandline parameters */
+void parse_params(int argc, char **argv)
+{
+ while(1)
+ {
+ int c = getopt_long (argc, argv, "hvd", long_options, NULL);
+ if (c == -1)
+ break;
+ switch(c)
+ {
+ case 'v':
+ config.set_verbosity(configinfo::report_verbose);
+ break;
+ case 'c':
+ config.no_dpkg_check = true;
+ break;
+ case 'd':
+ config.set_verbosity(configinfo::report_verbose);
+ break;
+ case 'n':
+ config.usedefaultmenufilesdirs = false;
+ break;
+ case 's':
+ config.onlyoutput_to_stdout = true;
+ break;
+ case 'f':
+ config.menufilesdir.push_back(optarg);
+ break;
+ case 'm':
+ config.menumethod = optarg;
+ break;
+ case 'r':
+ config.remove_menu = true;
+ break;
+ case 'T':
+ config.trigger = true;
+ break;
+ case 'V':
+ cout << "update-menus "VERSION << std::endl;
+ exit(0);
+ case 'h':
+ usage(cout);
+ exit(0);
+ default:
+ usage(cerr);
+ exit(1);
+ }
+ }
+}
+
+/** Read users configuration file */
+void read_userconfiginfo()
+{
+ if (!is_root) {
+ try {
+ config.read_file(string(home_dir)+"/"+USERCONFIG);
+ } catch(ferror_open d) { };
+ }
+}
+
+/** Read roots configuration file */
+void read_rootconfiginfo()
+{
+ if(!transinfo){
+ try {
+ config.read_file(CONFIG_FILE);
+ } catch (ferror_open d){};
+ }
+}
+
+/** Read users translate information */
+void read_usertranslateinfo()
+{
+ if (!is_root) {
+ try {
+ transinfo = new translateinfo(string(home_dir)+"/"+USERTRANSLATE);
+ } catch(ferror_open d) { };
+ }
+}
+
+/** Read roots translate information */
+void read_roottranslateinfo()
+{
+ if (!transinfo) {
+ try {
+ transinfo = new translateinfo(TRANSLATE_FILE);
+ } catch (ferror_open d) { };
+ }
+}
+
+/** Find our home directory */
+void read_homedirectory()
+{
+ struct passwd *pwentry = getpwuid(getuid());
+
+ if (pwentry != NULL)
+ home_dir = pwentry->pw_dir;
+ else
+ home_dir = getenv("HOME");
+}
+
+void run_methods(vector<string> &menudata)
+{
+ if (!config.menumethod.empty())
+ {
+ if (executable(config.menumethod))
+ run_menumethod(config.menumethod, menudata);
+ else
+ config.report(String::compose(_("Script %1 could not be executed."),
+ config.menumethod), configinfo::report_quiet);
+ }
+ else if (!is_root) {
+ try {
+ run_menumethoddir(string(home_dir)+"/"+USERMETHODS, menudata);
+ }
+ catch(dir_error_read d) {
+ run_menumethoddir(MENUMETHODS, menudata);
+ }
+ } else
+ run_menumethoddir(MENUMETHODS, menudata);
+}
+
+int main (int argc, char **argv)
+{
+ is_root = (getuid() == 0);
+ read_homedirectory();
+
+ vector<string> menudata;
+ string stdoutfile;
+ struct stat st;
+
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ try {
+ read_rootconfiginfo();
+ parse_params(argc, argv);
+ wait_dpkg(stdoutfile);
+ if(!stdoutfile.empty()) {
+ close(1);
+ close(2);
+ int i = open(stdoutfile.c_str(), O_WRONLY|O_CREAT|O_SYNC|O_EXCL, 0666);
+ if (i != 2) {
+ dup2(i,2);
+ close(i);
+ }
+ }
+ if (config.remove_menu)
+ {
+ run_methods(menudata);
+ remove_lock();
+ return 0;
+ }
+ read_pkginfo();
+ transinfo = 0;
+
+ read_usertranslateinfo();
+ read_roottranslateinfo();
+
+ if (config.usedefaultmenufilesdirs) {
+ if (!is_root)
+ config.menufilesdir.push_back(string(home_dir)+"/"+USERMENUS);
+ config.menufilesdir.push_back(CONFIGMENUS);
+ config.menufilesdir.push_back(PACKAGEMENUSLIB);
+ config.menufilesdir.push_back(PACKAGEMENUS);
+ config.menufilesdir.push_back(MENUMENUS);
+ }
+
+ read_menufilesdir(menudata);
+
+ if (config.onlyoutput_to_stdout) {
+ for(vector<string>::const_iterator i = menudata.begin(); i != menudata.end(); ++i)
+ cout << *i;
+
+ } else
+ run_methods(menudata);
+ }
+ catch(genexcept& p) { p.report(); }
+
+ remove_lock();
+
+ if(!stdoutfile.empty())
+ if (!stat(stdoutfile.c_str(),&st))
+ if (!st.st_size)
+ unlink(stdoutfile.c_str());
+}
diff --git a/update-menus/update-menus.h b/update-menus/update-menus.h
new file mode 100644
index 0000000..f00d72e
--- /dev/null
+++ b/update-menus/update-menus.h
@@ -0,0 +1,157 @@
+/*
+ * Debian menu system -- update-menus
+ * update-menus/update-menus.h
+ *
+ * Copyright (C) 1996-2003 Joost Witteveen,
+ * Copyright (C) 2002-2004 Bill Allombert and Morten Brix Pedersen.
+ *
+ * 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 2 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 with
+ * the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
+ * if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Written by Joost Witteveen.
+ */
+
+#ifndef UPDATE_MENUS_H
+#define UPDATE_MENUS_H
+
+#include <map>
+#include <vector>
+#include "compose.hpp"
+#include "common.h"
+#include "exceptions.h"
+#include "parsestream.h"
+
+/** Internal representation of a menu entry */
+class menuentry {
+ void check_req_tags(const std::string& filename);
+ void check_pkg_validity(parsestream &i, std::string &);
+ void read_menuentry(parsestream &i);
+
+public:
+ menuentry(parsestream &i, const std::string& file, const std::string& shortfile);
+
+ /** Map containing the data of the menuentry. The key is the tag and the
+ * value is the value of the tag.
+ */
+ std::map<std::string, std::string> data;
+ /** Output full menu entry in menu format, and put it in vec */
+ void output(std::vector<std::string>& vec);
+};
+
+/** Base class containing general methods for translation/substitution */
+class trans_class {
+protected:
+ std::string match, replace, replace_var;
+
+public:
+ trans_class(const std::string &m, const std::string &r, const std::string &rv)
+ : match(m), replace(r), replace_var(rv) { }
+
+ /** Process the menuentry using a search string */
+ virtual void process(menuentry &m, const std::string& search) = 0;
+ virtual ~trans_class() { }
+};
+
+/** Class for menu's "translation" feature */
+class translate : public trans_class {
+public:
+ translate(const std::string &m, const std::string &r, const std::string &rv)
+ : trans_class(m,r,rv) { }
+
+ void process(menuentry &m, const std::string& search);
+};
+
+/** Class for menu's "substitute" feature */
+class substitute : public trans_class {
+public:
+ substitute(const std::string &m, const std::string &r, const std::string &rv)
+ : trans_class(m,r,rv) { }
+
+ void process(menuentry &m, const std::string& search);
+};
+
+/** Helper class for menu's "substitute" feature */
+class subtranslate : public trans_class {
+public:
+ subtranslate(std::string &m, const std::string &r, const std::string &rv)
+ : trans_class(m,r,rv) { }
+
+ void process(menuentry &m, const std::string& search);
+};
+
+/** Class container for translation and subtitution classes */
+class translateinfo {
+ typedef std::pair<std::string, trans_class*> trans_pair;
+ std::map<std::string, std::vector<trans_pair> > trans;
+
+public:
+ translateinfo(const std::string &filename);
+
+ /** Process all translation classes */
+ void process(menuentry &m);
+};
+
+/** Representation of configurable variables in update-menus. */
+class configinfo {
+ typedef enum { method_stdout, method_stderr, method_syslog} method_type;
+ method_type method;
+ int syslog_facility, syslog_priority;
+
+ void parse_config(const std::string &var, const std::string& value);
+
+public:
+ configinfo()
+ : method(method_stderr), compat(parsestream::eol_newline),
+ usedefaultmenufilesdirs(true), onlyoutput_to_stdout(false),
+ remove_menu(false), trigger(false), verbosity(report_quiet)
+ { }
+
+ typedef enum { report_quiet, report_normal, report_verbose, report_debug} verbosity_type;
+ parsestream::eol_type compat;
+ std::vector<std::string> menufilesdir;
+ std::string menumethod;
+ bool usedefaultmenufilesdirs;
+ bool onlyoutput_to_stdout;
+ bool no_dpkg_check;
+ bool remove_menu;
+ bool trigger;
+
+
+ /** Read configuration file from filename */
+ void read_file(const std::string& filename);
+
+ /** Print message according to verbosity_type */
+ void report(const std::string &message, verbosity_type v);
+
+ /** Mutator method to verbosity */
+ void set_verbosity(verbosity_type v) { verbosity = v; }
+
+private:
+ verbosity_type verbosity;
+};
+
+namespace exceptions {
+ /** Exception to be thrown when an unknown install condition is found */
+ class unknown_cond_package : public except_pi_string {
+ public:
+ unknown_cond_package(parsestream *p, std::string s) : except_pi_string(p,s) { }
+ std::string message() {
+ return String::compose(_("Unknown install condition \"%1\" (currently, only \"package\" is supported)."), msg);
+ }
+ };
+}
+
+#endif