Hi, I wrote a gimptool replacement in C for Windows users, who might not have a Bourne shell to run the gimtool script with. "Where is gimptool" is a FAQ on the gimpwin-dev list. Also, the gimptool script's use of configure-time @substitutions@ doesn't work on Windows, where the installation directory isn't fixed. (Yes, pkg-config on Windows also handles this nicely, it replaces the configure-time prefix in a .pc file at run-time with the actual installation directory.) Should I call the file gimptool.c.in, or gimptool-win32.c.in? I.e., will automake etc get confused if there are both a gimptool script *and* a program? Even if automake conditionals decide which one to build? --tml /* gimptool in C * Copyright (C) 2001 Tor Lillqvist * * 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 * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * Gimptool rewritten in C, primarily for Win32, where end-users who might * want to build and install a plug-in from source don't necessarily have * any Bourne-compatible shell to run the gimptool script in. * * TODO: MSVC support: cl instead of gcc, pass --msvc-syntax to pkg-config. */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <glib.h> static gboolean silent = FALSE; static gboolean dry_run = FALSE; static gchar *prefix; static gchar *env_cflags; static gchar *env_ldflags; static gchar *env_libs; #ifdef G_OS_WIN32 #define EXEEXT ".exe" #else #define EXEEXT "" #endif #ifdef G_OS_WIN32 #define IS_EXECUTABLE(mode) 1 #else #define IS_EXECUTABLE(mode) (S_IXUSR & st.st_mode) #endif #ifdef G_OS_WIN32 #define USE_PKG_CONFIG yep #else #if defined (GLIB_CHECK_VERSION) && GLIB_CHECK_VERSION (1, 3, 10) #define USE_PKG_CONFIG also yep #endif #endif #ifdef G_OS_WIN32 #define COPY "copy" #define REMOVE "del " #else #define COPY "cp" #define REMOVE "rm -f " #endif static gchar * get_prefix (gchar slash) { #ifdef G_OS_WIN32 /* Don't use the build-time @prefix@, but deduce the * installation-time prefix from where gimp.exe can be found. */ char *path; char *p; char *q, *r; struct stat st; if (prefix != NULL) return prefix; p = path = g_strdup (g_getenv ("PATH")); while ((q = strchr (p, G_SEARCHPATH_SEPARATOR)) != NULL) { gchar *test; *q = '\0'; test = g_strconcat (p, G_DIR_SEPARATOR_S, "gimp", EXEEXT, NULL); if (stat (test, &st) == 0 && IS_EXECUTABLE (st.st_mode)) { r = strrchr (test, G_DIR_SEPARATOR); if (r != NULL) { *r = '\0'; if (strlen (test) >= 4 && g_strcasecmp (r - 4, G_DIR_SEPARATOR_S "bin") == 0) { r[-4] = '\0'; prefix = test; #ifdef G_OS_WIN32 if (slash == '/') { /* Use forward slashes, less quoting trouble in Makefiles */ while ((p = strchr (prefix, '\\')) != NULL) *p = '/'; } #endif return prefix; } } } p = q + 1; } fprintf (stderr, "Cannot find any suitable GIMP executable in PATH\n"); exit (1); #else return "@prefix@"; #endif } static void find_out_env_flags (void) { gchar *p; if ((p = getenv ("CFLAGS")) != NULL && *p != '\0') env_cflags = p; else env_cflags = "@CFLAGS@"; if ((p = getenv ("LDFLAGS")) != NULL && *p != '\0') env_ldflags = p; else env_ldflags = "@LDFLAGS@"; if ((p = getenv ("LIBS")) != NULL && *p != '\0') env_libs = p; else env_libs = ""; } static void usage (int exit_status) { printf ("\ Usage: gimptool [OPTION]...\n\ \n\ General options:\n\ --help print this message\n\ --quiet, --silent don't echo build commands\n\ --version print the version of GIMP associated with this script\n\ -n, --just-print, --dry-run, --recon\n\ don't actually run any commands; just print them\n\ Developer options:\n\ --cflags print the compiler flags that are necessary to\n\ compile a plug-in\n\ --libs print the linker flags that are necessary to link a\n\ plug-in\n\ --prefix=PREFIX use PREFIX instead of the installation prefix that\n\ GIMP was built when computing the output for --cflags\n\ and --libs\n\ --exec-prefix=PREFIX use PREFIX instead of the installation exec prefix\n\ that GIMP was built when computing the output for\n\ --cflags and --libs\n\ \n\ The --cflags and --libs options can be appended with -noui to get appropriate\n\ settings for plug-ins which do not use GTK+.\n\ \n\ User options:\n\ --build plug-in.c build a plug-in from a source file\n\ --install plug-in.c same as --build, but installs the built\n\ plug-in as well\n\ --install-bin plug-in install a compiled plug-in\n\ --install-script script.scm install a script-fu script\n\ \n\ --uninstall-bin plug-in remove a plug-in again\n\ --uninstall-script plug-in remove a script-fu script\n\ \n\ The --install and --uninstall options have \"admin\" counterparts (with\n\ prefix --install-admin instead of --install) that can be used instead to\n\ install/uninstall a plug-in or script in the site directory instead of a\n\ user directory.\n\ \n\ For plug-ins which do not use GTK+, the --build and --install options can be\n\ appended with -noui for appropriate settings. For plug-ins that use GTK+ but\n\ not libgumpui, append -nogimpui.\n"); exit (exit_status); } static gchar * one_line_output (gchar *program, gchar *args) { FILE *pipe = popen (g_strconcat (program, " ", args, NULL), "r"); char line[1000]; if (pipe == NULL) { fprintf (stderr, "Cannot run %s\n", program); exit (1); } fgets (line, sizeof (line), pipe); if (line [strlen (line) - 1] == '\n') line [strlen (line) - 1] = '\0'; if (line [strlen (line) - 1] == '\r') line [strlen (line) - 1] = '\0'; pclose (pipe); return g_strdup (line); } #ifdef USE_PKG_CONFIG static gchar * pkg_config (gchar *args) { return one_line_output ("pkg-config", args); } #endif static gchar * glib_config (gchar *args) { #ifdef USE_PKG_CONFIG return pkg_config (g_strconcat (args, " glib-2.0", NULL)); #else return one_line_output ("glib-config", args); #endif } static gchar * gtk_config (gchar *args) { #ifdef USE_PKG_CONFIG #if defined (G_OS_WIN32) && @GIMP_MAJOR_VERSION@ == 1 && @GIMP_MINOR_VERSION@ == 2 return pkg_config (g_strconcat (args, " gtk+-1.3-win32-production", NULL)); #else return pkg_config (g_strconcat (args, " gtk+-2.0", NULL)); #endif #else return one_line_output ("gtk-config", args); #endif } static gchar * get_cflags (void) { gchar *gtk_cflags = gtk_config ("--cflags"); /* Yes, do use forward slashes here also on Windows. The compilers * accept it, and it means less quoting trouble when using backquoted * invokations of gimptool in Makefiles. */ return g_strdup_printf ("%s -I %s/include", gtk_cflags, get_prefix ('/')); } static void do_cflags (void) { printf ("%s\n", get_cflags ()); } static gchar * get_cflags_noui (void) { gchar *glib_cflags = glib_config ("--cflags"); return g_strdup_printf ("%s -I %s/include", glib_cflags, get_prefix ('/')); } static void do_cflags_noui (void) { printf ("%s\n", get_cflags_noui ()); } static gchar * get_libs (void) { gchar *gtk_libs = gtk_config ("--libs"); return g_strdup_printf ("-L %s/lib -lgimpui -lgimp %s", get_prefix ('/'), gtk_libs); } static void do_libs (void) { printf ("%s\n", get_libs ()); } static gchar * get_libs_noui (void) { gchar *glib_libs = glib_config ("--libs"); return g_strdup_printf ("-L %s/lib -lgimp %s", get_prefix ('/'), glib_libs); } static void do_libs_noui (void) { printf ("%s\n", get_libs_noui ()); } static void maybe_run (gchar *cmd) { if (!silent) printf ("%s\n", cmd); if (!dry_run) system (cmd); } static void do_build_2 (gchar *cflags, gchar *libs, gchar *install_dir, gchar *what) { gchar *cmd; gchar *dest_dir; gchar *dest_exe; gchar *p, *q, *r; find_out_env_flags (); if (install_dir != NULL) dest_dir = g_strconcat (install_dir, "/", NULL); else dest_dir = ""; dest_exe = g_strdup (what); p = strrchr (dest_exe, '.'); if (p == NULL || strcmp (p, ".c") != 0) { fprintf (stderr, "plug-in source %s is not a C file?\n", what); exit (1); } *p = '\0'; q = strrchr (dest_exe, G_DIR_SEPARATOR); #ifdef G_OS_WIN32 r = strrchr (dest_exe, '/'); if (r != NULL && (q == NULL || r > q)) q = r; #endif if (q == NULL) q = dest_exe; else q++; dest_exe = g_strconcat (q, EXEEXT, NULL); cmd = g_strdup_printf ("gcc %s %s -o %s%s %s %s %s %s", env_cflags, cflags, dest_dir, dest_exe, what, env_ldflags, libs, env_libs); maybe_run (cmd); } static void do_build (char *what) { do_build_2 (get_cflags (), get_libs (), NULL, what); } static void do_build_noui (char *what) { do_build_2 (get_cflags_noui (), get_libs_noui (), NULL, what); } static void do_build_nogimpui (char *what) { do_build (what); } static gchar * get_user_plugin_dir (gboolean forward_slashes) { #ifdef G_OS_WIN32 /* In the --install-bin and --uninstall modes we want * to use backward slashes on Win32, because we invoke * the COPY and DEL commands directly with system(). */ const gchar slash = forward_slashes ? '/' : '\\'; #else const gchar slash = '/'; #endif return g_strdup_printf ("%s%c@gimpdir@%cplug-ins", g_get_home_dir (), slash, slash); } static void do_install (char *what) { do_build_2 (get_cflags (), get_libs (), get_user_plugin_dir (TRUE), what); } static void do_install_noui (char *what) { do_build_2 (get_cflags_noui (), get_libs_noui (), get_user_plugin_dir (TRUE), what); } static void do_install_nogimpui (char *what) { do_install (what); } static gchar * get_sys_plugin_dir (gboolean forward_slashes) { #ifdef G_OS_WIN32 const gchar slash = forward_slashes ? '/' : '\\'; #else const gchar slash = '/'; #endif return g_strdup_printf ("%s%clib%cgimp%c%d.%d%cplug-ins", get_prefix (slash), slash, slash, slash, @GIMP_MAJOR_VERSION@, @GIMP_MINOR_VERSION@, slash); } static void do_install_admin (char *what) { do_build_2 (get_cflags (), get_libs (), get_sys_plugin_dir (FALSE), what); } static void do_install_admin_noui (char *what) { do_build_2 (get_cflags_noui (), get_libs_noui (), get_sys_plugin_dir (FALSE), what); } static void do_install_admin_nogimpui (char *what) { do_build_2 (get_cflags (), get_libs (), get_sys_plugin_dir (FALSE), what); } static void do_install_bin_2 (gchar *dir, gchar *what) { maybe_run (g_strconcat (COPY, " ", what, dir, NULL)); } static void do_install_bin (char *what) { do_install_bin_2 (get_user_plugin_dir (FALSE), what); } static void do_install_admin_bin (char *what) { do_install_bin_2 (get_sys_plugin_dir (FALSE), what); } static void do_uninstall (gchar *dir, gchar *what) { maybe_run (g_strconcat (REMOVE, " ", dir, G_DIR_SEPARATOR_S, what, NULL)); } static gchar * maybe_append_exe (gchar *what) { #ifdef G_OS_WIN32 gchar *p = strrchr (what, '.'); if (p == NULL || g_strcasecmp (p, ".exe") != 0) return g_strconcat (what, ".exe"); #endif return what; } static void do_uninstall_bin (char *what) { do_uninstall (get_user_plugin_dir (FALSE), maybe_append_exe (what)); } static void do_uninstall_admin_bin (char *what) { do_uninstall (get_sys_plugin_dir (FALSE), maybe_append_exe (what)); } static gchar * get_user_script_dir (gboolean forward_slashes) { #ifdef G_OS_WIN32 const gchar slash = forward_slashes ? '/' : '\\'; #else const gchar slash = '/'; #endif return g_strdup_printf ("%s%c@gimpdir@%cscripts", g_get_home_dir (), slash, slash); } static void do_install_script (char *what) { do_install_bin_2 (get_user_script_dir (FALSE), what); } static gchar * get_sys_script_dir (gboolean forward_slashes) { #ifdef G_OS_WIN32 const gchar slash = forward_slashes ? '/' : '\\'; #else const gchar slash = '/'; #endif return g_strdup_printf ("%s%cshare%cgimp%c%d.%d%cscripts", get_prefix (slash), slash, slash, slash, @GIMP_MAJOR_VERSION@, @GIMP_MINOR_VERSION@, slash); } static void do_install_admin_script (char *what) { do_install_bin_2 (get_sys_script_dir (FALSE), what); } static void do_uninstall_script (char *what) { do_uninstall (get_user_script_dir (FALSE), what); } static void do_uninstall_admin_script (char *what) { do_uninstall (get_sys_script_dir (FALSE), what); } int main (int argc, char **argv) { int argi = 0; if (argc == 1) usage (0); while (++argi < argc) { if (strcmp (argv[argi], "--help") == 0) usage (0); else if (strcmp (argv[argi], "--quiet") == 0 || strcmp (argv[argi], "--silent") == 0) silent = TRUE; else if (strcmp (argv[argi], "--version") == 0) { printf ("%d.%d.%d\n", @GIMP_MAJOR_VERSION@, @GIMP_MINOR_VERSION@, @GIMP_MICRO_VERSION@); exit (0); } else if (strcmp (argv[argi], "-n") == 0 || strcmp (argv[argi], "--just-print") == 0 || strcmp (argv[argi], "--dry-run") == 0 || strcmp (argv[argi], "--recon") == 0) dry_run = TRUE; else if (strcmp (argv[argi], "--cflags") == 0) do_cflags (); else if (strcmp (argv[argi], "--cflags-noui") == 0) do_cflags_noui (); else if (strcmp (argv[argi], "--cflags-nogimpui") == 0) do_cflags (); else if (strcmp (argv[argi], "--libs") == 0) do_libs (); else if (strcmp (argv[argi], "--libs-noui") == 0) do_libs_noui (); else if (strcmp (argv[argi], "--prefix") == 0) prefix = argv[++argi]; else if (strcmp (argv[argi], "--build") == 0) do_build (argv[++argi]); else if (strcmp (argv[argi], "--build-noui") == 0) do_build_noui (argv[++argi]); else if (strcmp (argv[argi], "--build-nogimpui") == 0) do_build_nogimpui (argv[++argi]); else if (strcmp (argv[argi], "--install") == 0) do_install (argv[++argi]); else if (strcmp (argv[argi], "--install-noui") == 0) do_install_noui (argv[++argi]); else if (strcmp (argv[argi], "--install-nogimpui") == 0) do_install_nogimpui (argv[++argi]); else if (strcmp (argv[argi], "--install-admin") == 0) do_install_admin (argv[++argi]); else if (strcmp (argv[argi], "--install-admin-noui") == 0) do_install_admin_noui (argv[++argi]); else if (strcmp (argv[argi], "--install-admin-nogimpui") == 0) do_install_admin_nogimpui (argv[++argi]); else if (strcmp (argv[argi], "--install-bin") == 0) do_install_bin (argv[++argi]); else if (strcmp (argv[argi], "--install-admin-bin") == 0) do_install_admin_bin (argv[++argi]); else if (strcmp (argv[argi], "--uninstall-bin") == 0) do_uninstall_bin (argv[++argi]); else if (strcmp (argv[argi], "--uninstall-admin-bin") == 0) do_uninstall_admin_bin (argv[++argi]); else if (strcmp (argv[argi], "--install-script") == 0) do_install_script (argv[++argi]); else if (strcmp (argv[argi], "--install-admin-script") == 0) do_install_admin_script (argv[++argi]); else if (strcmp (argv[argi], "--uninstall-script") == 0) do_uninstall_script (argv[++argi]); else if (strcmp (argv[argi], "--uninstall-admin-script") == 0) do_uninstall_admin_script (argv[++argi]); else { fprintf (stderr, "Unrecognized switch %s\n", argv[argi]); usage (1); } } exit (0); }