[Gimp-developer] gimptool.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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);
}



[Index of Archives]     [Video For Linux]     [Photo]     [Yosemite News]     [gtk]     [GIMP for Windows]     [KDE]     [GEGL]     [Gimp's Home]     [Gimp on GUI]     [Gimp on Windows]     [Steve's Art]

  Powered by Linux