| // Copyright (C) 2006-2010 David Sugar, Tycho Softworks. |
| // |
| // This file is part of GNU uCommon C++. |
| // |
| // GNU uCommon C++ 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 3 of the License, or |
| // (at your option) any later version. |
| // |
| // GNU uCommon C++ 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 GNU uCommon C++. If not, see <http://www.gnu.org/licenses/>. |
| |
| /** |
| * Generic shell parsing and application services. |
| * @file ucommon/shell.h |
| */ |
| |
| /** |
| * Example of shell parsing. |
| * @example shell.cpp |
| */ |
| |
| #ifndef _UCOMMON_STRING_H_ |
| #include <ucommon/string.h> |
| #endif |
| |
| #ifndef _UCOMMON_MEMORY_H_ |
| #include <ucommon/memory.h> |
| #endif |
| |
| #ifndef _UCOMMON_BUFFER_H_ |
| #include <ucommon/buffer.h> |
| #endif |
| |
| #ifndef _UCOMMON_SHELL_H_ |
| #define _UCOMMON_SHELL_H_ |
| |
| #ifdef _MSWINDOWS_ |
| #define INVALID_PID_VALUE INVALID_HANDLE_VALUE |
| #else |
| #define INVALID_PID_VALUE -1 |
| #endif |
| |
| #ifdef ERR |
| #undef ERR |
| #endif |
| |
| NAMESPACE_UCOMMON |
| |
| /** |
| * A utility class for generic shell operations. This includes utilities |
| * to parse and expand arguments, and to call system shell services. This |
| * also includes a common shell class to parse and process command line |
| * arguments which are managed through a local heap. |
| * @author David Sugar <dyfet@gnutelephony.org> |
| */ |
| class __EXPORT shell : public mempager |
| { |
| private: |
| char **_argv; |
| unsigned _argc; |
| char *_argv0; |
| char *_exedir; |
| LinkedObject *_syms; |
| |
| class __LOCAL args : public OrderedObject |
| { |
| public: |
| char *item; |
| }; |
| |
| class __LOCAL syms : public LinkedObject |
| { |
| public: |
| const char *name; |
| const char *value; |
| }; |
| |
| /** |
| * Collapse argument list. This is used internally to collapse args |
| * that are created in a pool heap when they need to be turned into |
| * an argv style array. |
| */ |
| void collapse(LinkedObject *first); |
| |
| /** |
| * Set argv0. This gets the simple filename of argv[0]. |
| */ |
| void set0(char *argv0); |
| |
| public: |
| /** |
| * Error table index. |
| */ |
| typedef enum {NOARGS = 0, NOARGUMENT, INVARGUMENT, BADOPTION, OPTION_USED, BAD_VALUE, NUMERIC_SET} errmsg_t; |
| |
| /** |
| * Type of error logging we are using. |
| */ |
| typedef enum {NONE = 0, CONSOLE_LOG, USER_LOG, SYSTEM_LOG, SECURITY_LOG} logmode_t; |
| |
| /** |
| * Level of error logging. |
| */ |
| typedef enum {FAIL = 0, ERR, WARN, NOTIFY, INFO, DEBUG0} loglevel_t; |
| |
| /** |
| * Numeric mode of parser. |
| */ |
| typedef enum {NO_NUMERIC, NUMERIC_PLUS, NUMERIC_DASH, NUMERIC_ALL} numeric_t; |
| |
| /** |
| * Path types to retrieve. |
| */ |
| typedef enum { |
| PROGRAM_CONFIG, SERVICE_CONFIG, USER_DEFAULTS, SERVICE_CONTROL, |
| USER_HOME = USER_DEFAULTS + 3, SERVICE_DATA, SYSTEM_TEMP, USER_CACHE, |
| SERVICE_CACHE, USER_DATA, USER_CONFIG, SYSTEM_CFG, SYSTEM_ETC, |
| SYSTEM_VAR, SYSTEM_PREFIX, SYSTEM_SHARE, PROGRAM_PLUGINS, |
| PROGRAM_TEMP} path_t; |
| |
| /** |
| * Log process handler. |
| */ |
| typedef bool (*logproc_t)(loglevel_t level, const char *text); |
| |
| /** |
| * Main handler. |
| */ |
| typedef cpr_service_t mainproc_t; |
| |
| /** |
| * Exit handler. |
| */ |
| typedef void (*exitproc_t)(void); |
| |
| #ifdef _MSWINDOWS_ |
| typedef HANDLE pid_t; |
| #else |
| /** |
| * Standard type of process id for shell class. |
| */ |
| typedef int pid_t; |
| #endif |
| |
| /** |
| * This can be used to get internationalized error messages. The internal |
| * text for shell parser errors are passed through here. |
| * @param id of error message to use. |
| * @return published text of error message. |
| */ |
| static const char *errmsg(errmsg_t id); |
| |
| /** |
| * This is used to set internationalized error messages for the shell |
| * parser. |
| * @param id of message to set. |
| * @param text for error message. |
| */ |
| static void errmsg(errmsg_t id, const char *text); |
| |
| /** |
| * A class to redefine error messages. This can be used as a statically |
| * initialized object to remap error messages for easier |
| * internationalization. |
| * @author David Sugar <dyfet@gnutelephony.org> |
| */ |
| class __EXPORT errormap |
| { |
| public: |
| inline errormap(errmsg_t id, const char *text) |
| {shell::errmsg(id, text);}; |
| }; |
| |
| /** |
| * A base class used to create parsable shell options. The virtual |
| * is invoked when the shell option is detected. Both short and long |
| * forms of argument parsing are supported. An instance of a derived |
| * class is created to perform the argument parsing. |
| * @author David Sugar <dyfet@gnutelephony.org> |
| */ |
| class __EXPORT Option : public LinkedObject |
| { |
| public: |
| char short_option; |
| const char *long_option; |
| const char *uses_option; |
| const char *help_string; |
| bool trigger_option; |
| |
| /** |
| * Construct a shell parser option. |
| * @param short_option for single character code. |
| * @param long_option for extended string. |
| * @param value_type if -x value or -long=yyy. |
| * @param help string, future use. |
| */ |
| Option(char short_option = 0, const char *long_option = NULL, const char *value_type = NULL, const char *help = NULL); |
| |
| virtual ~Option(); |
| |
| static LinkedObject *first(void); |
| |
| /** |
| * Disable a option. Might happen if argv0 name suggests an |
| * option is no longer actively needed. |
| */ |
| void disable(void); |
| |
| /** |
| * Used to send option into derived receiver. |
| * @param value option that was received. |
| * @return NULL or error string to use. |
| */ |
| virtual const char *assign(const char *value) = 0; |
| |
| static void reset(void); |
| }; |
| |
| /** |
| * Flag option for shell parsing. This offers a quick-use class |
| * to parse a shell flag, along with a counter for how many times |
| * the flag was selected. The counter might be used for -vvvv style |
| * verbose options, for example. |
| * @author David Sugar <dyfet@gnutelephony.org> |
| */ |
| class __EXPORT flagopt : public Option |
| { |
| private: |
| unsigned counter; |
| bool single; |
| |
| virtual const char *assign(const char *value); |
| |
| public: |
| flagopt(char short_option, const char *long_option = NULL, const char *help = NULL, bool single_use = true); |
| |
| inline operator bool() |
| {return counter > 0;}; |
| |
| inline bool operator!() |
| {return counter == 0;}; |
| |
| inline operator unsigned() |
| {return counter;}; |
| |
| inline unsigned operator*() |
| {return counter;}; |
| |
| inline void set(unsigned value = 1) |
| {counter = value;}; |
| }; |
| |
| /** |
| * Grouping option. This is used to create a grouping entry in |
| * the shell::help() listing. |
| * @author David Sugar <dyfet@gnutelephony.org> |
| */ |
| class __EXPORT groupopt : public Option |
| { |
| private: |
| virtual const char *assign(const char *value); |
| |
| public: |
| groupopt(const char *help); |
| }; |
| |
| /** |
| * Text option for shell parsing. This offers a quick-use class |
| * to parse a shell flag, along with a numeric text that may be |
| * saved and a use counter, as multiple invocations is an error. |
| * @author David Sugar <dyfet@gnutelephony.org> |
| */ |
| class __EXPORT stringopt : public Option |
| { |
| private: |
| bool used; |
| |
| protected: |
| const char *text; |
| |
| virtual const char *assign(const char *value); |
| |
| public: |
| stringopt(char short_option, const char *long_option = NULL, const char *help = NULL, const char *type = "text", const char *def_text = NULL); |
| |
| inline void set(const char *string) |
| {text = string;}; |
| |
| inline operator bool() |
| {return used;}; |
| |
| inline bool operator!() |
| {return !used;}; |
| |
| inline operator const char *() |
| {return text;}; |
| |
| inline const char *operator*() |
| {return text;}; |
| }; |
| |
| /** |
| * Character option for shell parsing. This offers a quick-use class |
| * to parse a shell flag, along with a character code that may be |
| * saved. Multiple invocations is an error. |
| * @author David Sugar <dyfet@gnutelephony.org> |
| */ |
| class __EXPORT charopt : public Option |
| { |
| private: |
| bool used; |
| |
| protected: |
| char code; |
| |
| virtual const char *assign(const char *value); |
| |
| public: |
| charopt(char short_option, const char *long_option = NULL, const char *help = NULL, const char *type = "char", char default_code = ' '); |
| |
| inline void set(char value) |
| {code = value;}; |
| |
| inline operator bool() |
| {return used;}; |
| |
| inline bool operator!() |
| {return !used;}; |
| |
| inline operator char() |
| {return code;}; |
| |
| inline char operator*() |
| {return code;}; |
| }; |
| |
| /** |
| * Numeric option for shell parsing. This offers a quick-use class |
| * to parse a shell flag, along with a numeric value that may be |
| * saved and a use counter, as multiple invocations is an error. |
| * @author David Sugar <dyfet@gnutelephony.org> |
| */ |
| class __EXPORT numericopt : public Option |
| { |
| private: |
| bool used; |
| |
| protected: |
| long number; |
| |
| virtual const char *assign(const char *value); |
| |
| public: |
| numericopt(char short_option, const char *long_option = NULL, const char *help = NULL, const char *type = "numeric", long def_value = 0); |
| |
| inline void set(long value) |
| {number = value;}; |
| |
| inline operator bool() |
| {return used;}; |
| |
| inline bool operator!() |
| {return !used;}; |
| |
| inline operator long() |
| {return number;}; |
| |
| inline long operator*() |
| {return number;}; |
| }; |
| |
| /** |
| * Counter option for shell parsing. This offers a quick-use class |
| * to parse a shell flag, along with a numeric value that may be |
| * saved and a use counter, as multiple invocations is an error. Unlike |
| * numeric options, the short mode flag is a trigger option, and each |
| * use of the short flag is considered a counter increment. |
| * @author David Sugar <dyfet@gnutelephony.org> |
| */ |
| class __EXPORT counteropt : public Option |
| { |
| private: |
| bool used; |
| |
| protected: |
| long number; |
| |
| virtual const char *assign(const char *value); |
| |
| public: |
| counteropt(char short_option, const char *long_option = NULL, const char *help = NULL, const char *type = "numeric", long def_value = 0); |
| |
| inline void set(long value) |
| {number = value;}; |
| |
| inline operator bool() |
| {return used;}; |
| |
| inline bool operator!() |
| {return !used;}; |
| |
| inline operator long() |
| {return number;}; |
| |
| inline long operator*() |
| {return number;}; |
| }; |
| |
| /** |
| * Construct a shell argument list by parsing a simple command string. |
| * This separates a string into a list of command line arguments which |
| * can be used with exec functions. |
| * @param string to parse. |
| * @param pagesize for local heap. |
| */ |
| shell(const char *string, size_t pagesize = 0); |
| |
| /** |
| * Construct a shell argument list from existing arguments. This |
| * copies and on some platforms expands the argument list originally |
| * passed to main. |
| * @param argc from main. |
| * @param argv from main. |
| * @param pagesize for local heap. |
| */ |
| shell(int argc, char **argv, size_t pagesize = 0); |
| |
| /** |
| * Construct an empty shell parser argument list. |
| * @param pagesize for local heap. |
| */ |
| shell(size_t pagesize = 0); |
| |
| static void setNumeric(numeric_t); |
| |
| static long getNumeric(void); |
| |
| /** |
| * Display shell options. |
| */ |
| static void help(void); |
| |
| /** |
| * A shell system call. This uses the native system shell to invoke the |
| * command. |
| * @param command string.. |
| * @param env array to optionally use. |
| * @return error code of child process. |
| */ |
| static int system(const char *command, const char **env = NULL); |
| |
| /** |
| * A shell system call that can be issued using a formatted string. This |
| * uses the native system shell to invoke the command. |
| * @param format of/command string. |
| * @return error code of child process. |
| */ |
| static int systemf(const char *format, ...) __PRINTF(1,2); |
| |
| /** |
| * Set relative prefix. Used for OS/X relocatable applications. |
| * @param argv0 path of executable. |
| */ |
| static void relocate(const char *argv0); |
| |
| /** |
| * Get a system path. This is used to get directories for application |
| * specific data stores and default paths for config keys. |
| * @param id of path to return. |
| * @return path string or emptry string if not supported. |
| */ |
| static String path(path_t id); |
| |
| /** |
| * Get the system login id. |
| * @return login id. |
| */ |
| static String userid(void); |
| |
| /** |
| * Get a merged path. If the path requested is a full path, then |
| * the prefix is ignored. |
| * @param id of prefix. |
| * @param directory path to merge with prefix. |
| */ |
| static String path(path_t id, const char *directory); |
| |
| /** |
| * Join a prefix with a path. |
| * @param prefix to merge with. |
| * @param directory or file path to merge. |
| */ |
| static String path(String& prefix, const char *directory); |
| |
| /** |
| * Bind application to text domain for internationalization. This |
| * is useful if the argv0 argument can vary because of a symlinked |
| * executable. This is the name of the .po/.mo message files for |
| * your application. If bind is not called before shell processing, |
| * then the argv0 is used as the bind name. Bind can be called |
| * multiple times to change the default message catalog name of the |
| * application, and this usage may be needed for plugins, though it's |
| * generally recommended to use only once, and at the start of main(). |
| * @param name of text domain for the application. |
| */ |
| static void bind(const char *name); |
| |
| /** |
| * Rebind is used to change the text domain. This may be needed in |
| * applications which have separately built plugins that have thier |
| * own message catalogs. Normally the plugin would call bind itself |
| * at initialization, and then use rebind to select either the |
| * application's domain, or the plugins. On systems without |
| * internationalization, this has no effect. |
| * @param name of text domain of plugin or NULL to restore application. |
| */ |
| static void rebind(const char *name = NULL); |
| |
| /** |
| * Parse a string as a series of arguments for use in exec calls. |
| * @param string to parse. |
| * @return argument array. |
| */ |
| char **parse(const char *string); |
| |
| /** |
| * Parse the command line arguments using the option table. File |
| * arguments will be expanded for wildcards on some platforms. |
| * The argv will be set to the first file argument after all options |
| * are parsed. |
| * @param argc from main. |
| * @param argv from main. |
| */ |
| void parse(int argc, char **argv); |
| |
| /** |
| * Get an environment variable. This creates a local copy of the |
| * variable in pager memory. |
| * @param name of symbol. |
| * @param value of symbol if not found. |
| * @return value of symbol. |
| */ |
| const char *env(const char *name, const char *value = NULL); |
| |
| inline const char *getenv(const char *name, const char *value = NULL) |
| {return env(name, value);} |
| |
| /** |
| * Get a local symbol. This uses getenv if no local symbol is found. |
| * @param name of symbol. |
| * @param value of symbol if not found. |
| * @return value of symbol. |
| */ |
| const char *get(const char *name, const char *value = NULL); |
| |
| inline const char *getsym(const char *name, const char *value = NULL) |
| {return get(name, value);} |
| |
| /** |
| * Set a local symbol. |
| * @param name of symbol to set. |
| * @param value of symbol to set. |
| */ |
| void set(const char *name, const char *value); |
| |
| inline void setsym(const char *name, const char *value) |
| {return set(name, value);} |
| |
| /** |
| * Test if symbol exists. |
| * @param name of symbol. |
| * @return true if found. |
| */ |
| bool is_sym(const char *name); |
| |
| /** |
| * Parse and extract the argv0 filename alone. |
| * @param argv from main. |
| * @return argv0 simple path name. |
| */ |
| char *getargv0(char **argv); |
| |
| /** |
| * Get the argument list by parsing options, and return the remaining |
| * file arguments. This is used by parse, and can be fed by main by |
| * posting ++argv. |
| * @param argv of first option. |
| * @return argv of non-option file list. |
| */ |
| char **getargv(char **argv); |
| |
| /** |
| * Execute front-end like gdb based on stripped first argument. |
| * @param argv0 of our executable. |
| * @param argv to pass to child. |
| * @param list of arguments to execute in front of argv. |
| */ |
| void restart(char *argv0, char **argv, char **list); |
| |
| /** |
| * Get program name (argv0). |
| */ |
| inline const char *argv0() const |
| {return _argv0;} |
| |
| /** |
| * Get the exec directory. |
| */ |
| inline const char *execdir() const |
| {return _exedir;} |
| |
| /** |
| * Print error message and continue. |
| * @param format string to use. |
| */ |
| static void error(const char *format, ...) __PRINTF(1, 2); |
| |
| /** |
| * Print error message and exit. Ignored if exitcode == 0. |
| * @param exitcode to return to parent process. |
| * @param format string to use. |
| */ |
| static void errexit(int exitcode, const char *format = NULL, ...) __PRINTF(2, 3); |
| |
| |
| /** |
| * Convert condition to exit code if true. |
| * @param test condition. |
| * @param exitcode to use if true. |
| */ |
| static inline int condition(bool test, int exitcode) |
| { return (test) ? exitcode : 0;}; |
| |
| /** |
| * Print a debug message by debug level. |
| * @param level of debug message. |
| * @param format string to use. |
| */ |
| static void debug(unsigned level, const char *format, ...) __PRINTF(2, 3); |
| |
| /** |
| * Print generic error message at specific error level. |
| * @param level of error condition. |
| * @param format string to use. |
| */ |
| static void log(loglevel_t level, const char *format, ...) __PRINTF(2, 3); |
| |
| /** |
| * Print security error message at specific error level. |
| * @param level of error condition. |
| * @param format string to use. |
| */ |
| static void security(loglevel_t level, const char *format, ...) __PRINTF(2, 3); |
| |
| /** |
| * Set logging level and state. |
| * @param name of logging entity. |
| * @param level of error conditions to log. |
| * @param mode of logging. |
| * @param handler for log messages. |
| */ |
| static void log(const char *name, loglevel_t level = ERR, logmode_t mode = USER_LOG, logproc_t handler = (logproc_t)NULL); |
| |
| /** |
| * Print to standard output. |
| * @param format string to use. |
| */ |
| static size_t printf(const char *format, ...) __PRINTF(1, 2); |
| |
| static size_t readln(char *address, size_t size); |
| |
| static size_t writes(const char *string); |
| |
| static size_t read(String& string); |
| |
| inline static size_t write(String& string) |
| {return writes(string.c_str());}; |
| |
| /** |
| * Get saved internal argc count for items. This may be items that |
| * remain after shell expansion and options have been parsed. |
| * @return count of remaining arguments. |
| */ |
| inline unsigned argc(void) const |
| {return _argc;}; |
| |
| /** |
| * Get saved internal argv count for items in this shell object. This |
| * may be filename items only that remain after shell expansion and |
| * options that have been parsed. |
| * @return list of remaining arguments. |
| */ |
| inline char **argv(void) const |
| {return _argv;}; |
| |
| /** |
| * Return parser argv element. |
| * @param offset into array. |
| * @return argument string. |
| */ |
| inline const char *operator[](unsigned offset) |
| {return _argv[offset];}; |
| |
| static void exiting(exitproc_t); |
| |
| /** |
| * Detach current process to daemon for service entry. |
| */ |
| void detach(mainproc_t mainentry = (mainproc_t)NULL); |
| |
| /** |
| * Make current process restartable. |
| */ |
| void restart(void); |
| |
| /** |
| * Spawn a child process. This creates a new child process. If |
| * the executable path is a pure filename, then the $PATH will be |
| * used to find it. The argv array may be created from a string |
| * with the shell string parser. |
| * @param path to executable. |
| * @param argv list of command arguments for the child process. |
| * @param env of child process can be explicitly set. |
| * @param stdio handles for stdin, stdout, and stderr. |
| * @return process id of child or INVALID_PID_VALUE if fails. |
| */ |
| static shell::pid_t spawn(const char *path, char **argv, char **env = NULL, fd_t *stdio = NULL); |
| |
| /** |
| * Set priority level and enable priority scheduler. This activates the |
| * realtime priority scheduler when a priority > 0 is requested for the |
| * process, assuming scheduler support exists and the process is |
| * sufficiently privileged. Negative priorities are essentially the |
| * same as nice. |
| * @param pri level for process. |
| */ |
| static void priority(int pri = 1); |
| |
| /** |
| * Create a detached process. This creates a new child process that |
| * is completely detached from the current process. |
| * @param path to executable. |
| * @param argv list of command arguments for the child process. |
| * @param env of child process can be explicitly set. |
| * @param stdio handles for stdin, stdout, and stderr. |
| * @return 0 if success, -1 on error. |
| */ |
| static int detach(const char *path, char **argv, char **env = NULL, fd_t *stdio = NULL); |
| |
| /** |
| * Detach and release from parent process with exit code. |
| * @param exit_code to send to parent process. |
| */ |
| static void release(int exit_code = 0); |
| |
| /** |
| * Wait for a child process to terminate. This operation blocks. |
| * @param pid of process to wait for. |
| * @return exit code of process, -1 if fails or pid is invalid. |
| */ |
| static int wait(shell::pid_t pid); |
| |
| /** |
| * Cancel a child process. |
| * @param pid of child process to cancel. |
| * @return exit code of process, -1 if fails or pid is invalid. |
| */ |
| static int cancel(shell::pid_t pid); |
| |
| /** |
| * Return argc count. |
| * @return argc count. |
| */ |
| inline unsigned operator()(void) |
| {return _argc;}; |
| |
| /** |
| * Text translation and localization. This function does nothing but |
| * return the original string if no internationalization support is |
| * available. If internationalization support exists, then it may |
| * return a substituted translation based on the current locale. This |
| * offers a single function that can be safely used either when |
| * internationalization support is present, or it is absent, eliminating |
| * the need for the application to be coded differently based on |
| * awareness of support. |
| * @param string to translate. |
| * @return translation if found or original text. |
| */ |
| static const char *text(const char *string); |
| |
| /** |
| * Plural text translation and localization. This does nothing but |
| * return single or plural forms if no internationalization is |
| * enabled. Else it uses ngettext(). |
| * @param singular string to translate. |
| * @param plural string to translate. |
| * @param count of objects. |
| * @return string to use. |
| */ |
| static const char *texts(const char *singular, const char *plural, unsigned long count); |
| |
| /** |
| * Get argc count for an existing array. |
| * @param argv to count items in. |
| * @return argc count of array. |
| */ |
| static unsigned count(char **argv); |
| |
| #ifdef _MSWINDOWS_ |
| |
| static inline fd_t input(void) |
| {return GetStdHandle(STD_INPUT_HANDLE);}; |
| |
| static inline fd_t output(void) |
| {return GetStdHandle(STD_OUTPUT_HANDLE);}; |
| |
| static inline fd_t error(void) |
| {return GetStdHandle(STD_ERROR_HANDLE);}; |
| |
| #else |
| static inline fd_t input(void) |
| {return 0;}; |
| |
| static inline fd_t output(void) |
| {return 1;}; |
| |
| static inline fd_t error(void) |
| {return 2;}; |
| #endif |
| |
| static int inkey(const char *prompt = NULL); |
| |
| static char *getpass(const char *prompt, char *buffer, size_t size); |
| |
| static char *getline(const char *prompt, char *buffer, size_t size); |
| |
| }; |
| |
| /** |
| * Convenience type to manage and pass shell objects. |
| */ |
| typedef shell shell_t; |
| |
| /** |
| * Abusive compilers... |
| */ |
| #undef _TEXT |
| #undef _STR |
| #define _STR(x) (const char *)(x) |
| |
| /** |
| * Invoke translation lookup if available. This can also be used to |
| * mark text constants that need to be translated. It should not be |
| * used with pointer variables, which should instead call shell::text |
| * directly. The primary purpose is to allow extraction of text to |
| * be internationalized with xgettext "--keyword=_TEXT:1". |
| */ |
| inline const char *_TEXT(const char *s) |
| {return shell::text(s);} |
| |
| END_NAMESPACE |
| |
| #endif |