/*------------------------------------------------------------------------- * C-Pluff, a plug-in framework for C * Copyright 2007 Johannes Lehtinen * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *-----------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <unistd.h> #ifdef HAVE_GETTEXT #include <libintl.h> #include <locale.h> #endif #include <cpluff.h> /* ----------------------------------------------------------------------- * Defines * ---------------------------------------------------------------------*/ // Gettext defines #ifdef HAVE_GETTEXT #define _(String) gettext(String) #define gettext_noop(String) String #define N_(String) gettext_noop(String) #else #define _(String) (String) #define N_(String) String #define textdomain(Domain) #define bindtextdomain(Package, Directory) #endif // GNU C attribute defines #ifndef CP_GCC_NORETURN #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5) #define CP_GCC_NORETURN __attribute__((noreturn)) #else #define CP_GCC_NORETURN #endif #endif // Initializer for empty list #define STR_LIST_INITIALIZER { NULL, NULL } /* ----------------------------------------------------------------------- * Data types * ---------------------------------------------------------------------*/ /// A type for str_list_t structure typedef struct str_list_t str_list_t; /// A type for str_list_entry_t structure typedef struct str_list_entry_t str_list_entry_t; /// A string list container struct str_list_t { /// The first entry or NULL if empty str_list_entry_t *first; /// The last entry or NULL if empty str_list_entry_t *last; }; /// A holder for a string list entry struct str_list_entry_t { /// The string const char *str; /// Next entry str_list_entry_t *next; }; /* ----------------------------------------------------------------------- * Variables * ---------------------------------------------------------------------*/ /// The level of verbosity static int verbosity = 1; /* ----------------------------------------------------------------------- * Functions * ---------------------------------------------------------------------*/ /** * Prints an error message and exits. In quiet mode the error message is * not printed. * * @param msg the error message */ CP_GCC_NORETURN static void error(const char *msg) { if (verbosity >= 1) { /* TRANSLATORS: A formatting string for loader error messages. */ fprintf(stderr, _("C-Pluff Loader: ERROR: %s\n"), msg); } exit(1); } /** * Formats and prints an error message and exits. In quiet mode the error * message is not printed. * * @param msg the error message */ CP_GCC_NORETURN static void errorf(const char *msg, ...) { char buffer[256]; va_list va; va_start(va, msg); vsnprintf(buffer, sizeof(buffer), _(msg), va); va_end(va); strcpy(buffer + sizeof(buffer)/sizeof(char) - 4, "..."); error(buffer); } /** * Allocates memory using malloc and checks for failures. * * @param size the amount of memory to allocate * @return the allocated memory (always non-NULL) */ static void *chk_malloc(size_t size) { void *ptr = malloc(size); if (ptr == NULL) { error(_("Memory allocation failed.")); } else { return ptr; } } /** * Appends a new string to a string list. Copies strings by pointers. */ static void str_list_append(str_list_t *list, const char *str) { str_list_entry_t *entry = chk_malloc(sizeof(str_list_entry_t)); entry->str = str; entry->next = NULL; if (list->last != NULL) { list->last->next = entry; } if (list->first == NULL) { list->first = entry; } list->last = entry; } /** * Removes all entries from a string list. Does not free contained strings. */ static void str_list_clear(str_list_t *list) { str_list_entry_t *entry = list->first; while (entry != NULL) { str_list_entry_t *n = entry->next; free(entry); entry = n; } list->first = NULL; list->last = NULL; } /** * Prints the help text. */ static void print_help(void) { printf(_("C-Pluff Loader, version %s\n"), PACKAGE_VERSION); putchar('\n'); fputs(_("usage: cpluff-loader <option>... [--] <arguments passed to plug-ins>\n" "options:\n" " -h print this help text\n" " -c DIR add plug-in collection in directory DIR\n" " -p DIR add plug-in in directory DIR\n" " -s PID start plug-in PID\n" " -v be more verbose (repeat for increased verbosity)\n" " -q be quiet\n" " -V print C-Pluff version number and exit\n" ), stdout); } static void logger(cp_log_severity_t severity, const char *msg, const char *apid, void *dummy) { const char *level; int minv; switch (severity) { case CP_LOG_DEBUG: /* TRANSLATORS: A tag for debug level log entries. */ level = _("DEBUG"); minv = 4; break; case CP_LOG_INFO: /* TRANSLATORS: A tag for info level log entries. */ level = _("INFO"); minv = 3; break; case CP_LOG_WARNING: /* TRANSLATORS: A tag for warning level log entries. */ level = _("WARNING"); minv = 2; break; case CP_LOG_ERROR: /* TRANSLATORS: A tag for error level log entries. */ level = _("ERROR"); minv = 1; break; default: /* TRANSLATORS: A tag for unknown severity level. */ level = _("UNKNOWN"); minv = 1; break; } if (verbosity >= minv) { if (apid != NULL) { /* TRANSLATORS: A formatting string for log messages caused by plug-in activity. */ fprintf(stderr, _("C-Pluff: %s: [%s] %s\n"), level, apid, msg); } else { /* TRANSLATORS: A formatting string for log messages caused by loader activity. */ fprintf(stderr, _("C-Pluff: %s: [loader] %s\n"), level, msg); } } } /// The main function int main(int argc, char *argv[]) { int i; str_list_t lst_plugin_collections = STR_LIST_INITIALIZER; str_list_t lst_plugin_dirs = STR_LIST_INITIALIZER; str_list_t lst_start = STR_LIST_INITIALIZER; cp_context_t *context; char **ctx_argv; str_list_entry_t *entry; // Set locale #ifdef HAVE_GETTEXT setlocale(LC_ALL, ""); #endif // Initialize the framework if (cp_init() != CP_OK) { error(_("The C-Pluff initialization failed.")); } // Set gettext domain #ifdef HAVE_GETTEXT textdomain(PACKAGE); #endif // Parse arguments while ((i = getopt(argc, argv, "hc:p:s:vqV")) != -1) { switch (i) { // Display help and exit case 'h': print_help(); exit(0); // Add a plug-in collection case 'c': str_list_append(&lst_plugin_collections, optarg); break; // Add a single plug-in case 'p': str_list_append(&lst_plugin_dirs, optarg); break; // Add a plug-in to be started case 's': str_list_append(&lst_start, optarg); break; // Be more verbose case 'v': if (verbosity < 1) { error(_("Quiet and verbose modes are mutually exclusive.")); } verbosity++; break; // Quiet mode case 'q': if (verbosity > 1) { error(_("Quiet and verbose modes are mutually exclusive.")); } verbosity--; break; // Display release version and exit case 'V': fputs(cp_get_version(), stdout); putchar('\n'); exit(0); // Unrecognized option default: error(_("Unrecognized option or argument. Try option -h for help.")); } } // Display startup information if (verbosity >= 1) { /* TRANSLATORS: This is a version string displayed on startup. */ fprintf(stderr, _("C-Pluff Loader, version %s\n"), PACKAGE_VERSION); /* TRANSLATORS: This is a version string displayed on startup. The first %s is version and the second %s is platform type. */ fprintf(stderr, _("C-Pluff Library, version %s for %s\n"), cp_get_version(), cp_get_host_type()); } // Check arguments if (lst_plugin_dirs.first == NULL && lst_plugin_collections.first == NULL) { error(_("No plug-ins to load. Try option -h for help.")); } // Create the context if ((context = cp_create_context(NULL)) == NULL) { error(_("Plug-in context creation failed.")); } // Register logger if (verbosity >= 1) { cp_log_severity_t mv = CP_LOG_DEBUG; switch (verbosity) { case 1: mv = CP_LOG_ERROR; break; case 2: mv = CP_LOG_WARNING; break; case 3: mv = CP_LOG_INFO; break; } cp_register_logger(context, logger, NULL, mv); } // Set context arguments ctx_argv = chk_malloc((argc - optind + 2) * sizeof(char *)); ctx_argv[0] = ""; for (i = optind; i < argc; i++) { ctx_argv[i - optind + 1] = argv[i]; } ctx_argv[argc - optind + 1] = NULL; cp_set_context_args(context, ctx_argv); // Load individual plug-ins for (entry = lst_plugin_dirs.first; entry != NULL; entry = entry->next) { cp_plugin_info_t *pi = cp_load_plugin_descriptor(context, entry->str, NULL); if (pi == NULL) { errorf(_("Failed to load a plug-in from path %s."), entry->str); } if (cp_install_plugin(context, pi) != CP_OK) { errorf(_("Failed to install plug-in %s."), pi->identifier); } cp_release_info(context, pi); } str_list_clear(&lst_plugin_dirs); // Load plug-in collections for (entry = lst_plugin_collections.first; entry != NULL; entry = entry->next) { if (cp_register_pcollection(context, entry->str) != CP_OK) { errorf(_("Failed to register a plug-in collection at path %s."), entry->str); } } if (lst_plugin_collections.first != NULL && cp_scan_plugins(context, 0) != CP_OK) { error(_("Failed to load and install plug-ins from plug-in collections.")); } str_list_clear(&lst_plugin_collections); // Start plug-ins for (entry = lst_start.first; entry != NULL; entry = entry->next) { if (cp_start_plugin(context, entry->str) != CP_OK) { errorf(_("Failed to start plug-in %s."), entry->str); } } str_list_clear(&lst_start); // Run plug-ins cp_run_plugins(context); // Destroy framework cp_destroy(); // Release context argument data free(ctx_argv); // Return from the main program return 0; }