diff options
author | Bill Allombert <ballombe@debian.org> | 2011-11-16 12:33:27 +0100 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-03-05 16:39:28 +0000 |
commit | dd7279795c97aa356727d366a2149c9bc838b496 (patch) | |
tree | a87d19d2e5220f5b38aec3b5b036235a59f971c9 /update-menus | |
download | menu-dd7279795c97aa356727d366a2149c9bc838b496.tar.gz |
Imported Debian version 2.1.46debian/2.1.46
Diffstat (limited to 'update-menus')
-rw-r--r-- | update-menus/Makefile.am | 8 | ||||
-rw-r--r-- | update-menus/Makefile.in | 425 | ||||
-rw-r--r-- | update-menus/common.h | 52 | ||||
-rw-r--r-- | update-menus/compose.hpp | 393 | ||||
-rw-r--r-- | update-menus/exceptions.h | 102 | ||||
-rw-r--r-- | update-menus/parsestream.cc | 467 | ||||
-rw-r--r-- | update-menus/parsestream.h | 176 | ||||
-rw-r--r-- | update-menus/stringtoolbox.cc | 182 | ||||
-rw-r--r-- | update-menus/stringtoolbox.h | 111 | ||||
-rw-r--r-- | update-menus/update-menus.cc | 1097 | ||||
-rw-r--r-- | update-menus/update-menus.h | 157 |
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 |