123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632 |
- /*
- * gtkmain.c: the common main-program code between the straight-up
- * Unix PuTTY and pterm, which they do not share with the
- * multi-session gtkapp.c.
- */
- #define _GNU_SOURCE
- #include <string.h>
- #include <assert.h>
- #include <stdlib.h>
- #include <string.h>
- #include <signal.h>
- #include <stdio.h>
- #include <time.h>
- #include <errno.h>
- #include <locale.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <gtk/gtk.h>
- #if !GTK_CHECK_VERSION(3,0,0)
- #include <gdk/gdkkeysyms.h>
- #endif
- #if GTK_CHECK_VERSION(2,0,0)
- #include <gtk/gtkimmodule.h>
- #endif
- #define MAY_REFER_TO_GTK_IN_HEADERS
- #include "putty.h"
- #include "terminal.h"
- #include "gtkcompat.h"
- #include "gtkfont.h"
- #include "gtkmisc.h"
- #ifndef NOT_X_WINDOWS
- #include <gdk/gdkx.h>
- #include <X11/Xlib.h>
- #include <X11/Xutil.h>
- #include <X11/Xatom.h>
- #endif
- static char *progname, **gtkargvstart;
- static int ngtkargs;
- extern char **pty_argv; /* declared in pty.c */
- extern int use_pty_argv;
- static const char *app_name = "pterm";
- char *x_get_default(const char *key)
- {
- #ifndef NOT_X_WINDOWS
- return XGetDefault(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
- app_name, key);
- #else
- return NULL;
- #endif
- }
- void fork_and_exec_self(int fd_to_close, ...)
- {
- /*
- * Re-execing ourself is not an exact science under Unix. I do
- * the best I can by using /proc/self/exe if available and by
- * assuming argv[0] can be found on $PATH if not.
- *
- * Note that we also have to reconstruct the elements of the
- * original argv which gtk swallowed, since the user wants the
- * new session to appear on the same X display as the old one.
- */
- char **args;
- va_list ap;
- int i, n;
- int pid;
- /*
- * Collect the arguments with which to re-exec ourself.
- */
- va_start(ap, fd_to_close);
- n = 2; /* progname and terminating NULL */
- n += ngtkargs;
- while (va_arg(ap, char *) != NULL)
- n++;
- va_end(ap);
- args = snewn(n, char *);
- args[0] = progname;
- args[n-1] = NULL;
- for (i = 0; i < ngtkargs; i++)
- args[i+1] = gtkargvstart[i];
- i++;
- va_start(ap, fd_to_close);
- while ((args[i++] = va_arg(ap, char *)) != NULL);
- va_end(ap);
- assert(i == n);
- /*
- * Do the double fork.
- */
- pid = fork();
- if (pid < 0) {
- perror("fork");
- sfree(args);
- return;
- }
- if (pid == 0) {
- int pid2 = fork();
- if (pid2 < 0) {
- perror("fork");
- _exit(1);
- } else if (pid2 > 0) {
- /*
- * First child has successfully forked second child. My
- * Work Here Is Done. Note the use of _exit rather than
- * exit: the latter appears to cause destroy messages
- * to be sent to the X server. I suspect gtk uses
- * atexit.
- */
- _exit(0);
- }
- /*
- * If we reach here, we are the second child, so we now
- * actually perform the exec.
- */
- if (fd_to_close >= 0)
- close(fd_to_close);
- execv("/proc/self/exe", args);
- execvp(progname, args);
- perror("exec");
- _exit(127);
- } else {
- int status;
- sfree(args);
- waitpid(pid, &status, 0);
- }
- }
- void launch_duplicate_session(Conf *conf)
- {
- /*
- * For this feature we must marshal conf and (possibly) pty_argv
- * into a byte stream, create a pipe, and send this byte stream
- * to the child through the pipe.
- */
- int i, ret, sersize, size;
- char *data;
- char option[80];
- int pipefd[2];
- if (pipe(pipefd) < 0) {
- perror("pipe");
- return;
- }
- size = sersize = conf_serialised_size(conf);
- if (use_pty_argv && pty_argv) {
- for (i = 0; pty_argv[i]; i++)
- size += strlen(pty_argv[i]) + 1;
- }
- data = snewn(size, char);
- conf_serialise(conf, data);
- if (use_pty_argv && pty_argv) {
- int p = sersize;
- for (i = 0; pty_argv[i]; i++) {
- strcpy(data + p, pty_argv[i]);
- p += strlen(pty_argv[i]) + 1;
- }
- assert(p == size);
- }
- sprintf(option, "---[%d,%d]", pipefd[0], size);
- noncloexec(pipefd[0]);
- fork_and_exec_self(pipefd[1], option, NULL);
- close(pipefd[0]);
- i = ret = 0;
- while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)
- i += ret;
- if (ret < 0)
- perror("write to pipe");
- close(pipefd[1]);
- sfree(data);
- }
- void launch_new_session(void)
- {
- fork_and_exec_self(-1, NULL);
- }
- void launch_saved_session(const char *str)
- {
- fork_and_exec_self(-1, "-load", str, NULL);
- }
- int read_dupsession_data(Conf *conf, char *arg)
- {
- int fd, i, ret, size, size_used;
- char *data;
- if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
- fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);
- exit(1);
- }
- data = snewn(size, char);
- i = ret = 0;
- while (i < size && (ret = read(fd, data + i, size - i)) > 0)
- i += ret;
- if (ret < 0) {
- perror("read from pipe");
- exit(1);
- } else if (i < size) {
- fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",
- appname);
- exit(1);
- }
- size_used = conf_deserialise(conf, data, size);
- if (use_pty_argv && size > size_used) {
- int n = 0;
- i = size_used;
- while (i < size) {
- while (i < size && data[i]) i++;
- if (i >= size) {
- fprintf(stderr, "%s: malformed Duplicate Session data\n",
- appname);
- exit(1);
- }
- i++;
- n++;
- }
- pty_argv = snewn(n+1, char *);
- pty_argv[n] = NULL;
- n = 0;
- i = size_used;
- while (i < size) {
- char *p = data + i;
- while (i < size && data[i]) i++;
- assert(i < size);
- i++;
- pty_argv[n++] = dupstr(p);
- }
- }
- sfree(data);
- return 0;
- }
- static void help(FILE *fp) {
- if(fprintf(fp,
- "pterm option summary:\n"
- "\n"
- " --display DISPLAY Specify X display to use (note '--')\n"
- " -name PREFIX Prefix when looking up resources (default: pterm)\n"
- " -fn FONT Normal text font\n"
- " -fb FONT Bold text font\n"
- " -geometry GEOMETRY Position and size of window (size in characters)\n"
- " -sl LINES Number of lines of scrollback\n"
- " -fg COLOUR, -bg COLOUR Foreground/background colour\n"
- " -bfg COLOUR, -bbg COLOUR Foreground/background bold colour\n"
- " -cfg COLOUR, -bfg COLOUR Foreground/background cursor colour\n"
- " -T TITLE Window title\n"
- " -ut, +ut Do(default) or do not update utmp\n"
- " -ls, +ls Do(default) or do not make shell a login shell\n"
- " -sb, +sb Do(default) or do not display a scrollbar\n"
- " -log PATH, -sessionlog PATH Log all output to a file\n"
- " -nethack Map numeric keypad to hjklyubn direction keys\n"
- " -xrm RESOURCE-STRING Set an X resource\n"
- " -e COMMAND [ARGS...] Execute command (consumes all remaining args)\n"
- ) < 0 || fflush(fp) < 0) {
- perror("output error");
- exit(1);
- }
- }
- static void version(FILE *fp) {
- if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) {
- perror("output error");
- exit(1);
- }
- }
- static struct gui_data *the_inst;
- static const char *geometry_string;
- int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
- Conf *conf)
- {
- int err = 0;
- char *val;
- /*
- * Macros to make argument handling easier. Note that because
- * they need to call `continue', they cannot be contained in
- * the usual do {...} while (0) wrapper to make them
- * syntactically single statements; hence it is not legal to
- * use one of these macros as an unbraced statement between
- * `if' and `else'.
- */
- #define EXPECTS_ARG { \
- if (--argc <= 0) { \
- err = 1; \
- fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
- continue; \
- } else \
- val = *++argv; \
- }
- #define SECOND_PASS_ONLY { if (!do_everything) continue; }
- while (--argc > 0) {
- const char *p = *++argv;
- int ret;
- /*
- * Shameless cheating. Debian requires all X terminal
- * emulators to support `-T title'; but
- * cmdline_process_param will eat -T (it means no-pty) and
- * complain that pterm doesn't support it. So, in pterm
- * only, we convert -T into -title.
- */
- if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&
- !strcmp(p, "-T"))
- p = "-title";
- ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
- do_everything ? 1 : -1, conf);
- if (ret == -2) {
- cmdline_error("option \"%s\" requires an argument", p);
- } else if (ret == 2) {
- --argc, ++argv; /* skip next argument */
- continue;
- } else if (ret == 1) {
- continue;
- }
- if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
- FontSpec *fs;
- EXPECTS_ARG;
- SECOND_PASS_ONLY;
- fs = fontspec_new(val);
- conf_set_fontspec(conf, CONF_font, fs);
- fontspec_free(fs);
- } else if (!strcmp(p, "-fb")) {
- FontSpec *fs;
- EXPECTS_ARG;
- SECOND_PASS_ONLY;
- fs = fontspec_new(val);
- conf_set_fontspec(conf, CONF_boldfont, fs);
- fontspec_free(fs);
- } else if (!strcmp(p, "-fw")) {
- FontSpec *fs;
- EXPECTS_ARG;
- SECOND_PASS_ONLY;
- fs = fontspec_new(val);
- conf_set_fontspec(conf, CONF_widefont, fs);
- fontspec_free(fs);
- } else if (!strcmp(p, "-fwb")) {
- FontSpec *fs;
- EXPECTS_ARG;
- SECOND_PASS_ONLY;
- fs = fontspec_new(val);
- conf_set_fontspec(conf, CONF_wideboldfont, fs);
- fontspec_free(fs);
- } else if (!strcmp(p, "-cs")) {
- EXPECTS_ARG;
- SECOND_PASS_ONLY;
- conf_set_str(conf, CONF_line_codepage, val);
- } else if (!strcmp(p, "-geometry")) {
- EXPECTS_ARG;
- SECOND_PASS_ONLY;
- geometry_string = val;
- } else if (!strcmp(p, "-sl")) {
- EXPECTS_ARG;
- SECOND_PASS_ONLY;
- conf_set_int(conf, CONF_savelines, atoi(val));
- } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
- !strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
- !strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {
- EXPECTS_ARG;
- SECOND_PASS_ONLY;
- {
- #if GTK_CHECK_VERSION(3,0,0)
- GdkRGBA rgba;
- int success = gdk_rgba_parse(&rgba, val);
- #else
- GdkColor col;
- int success = gdk_color_parse(val, &col);
- #endif
- if (!success) {
- err = 1;
- fprintf(stderr, "%s: unable to parse colour \"%s\"\n",
- appname, val);
- } else {
- #if GTK_CHECK_VERSION(3,0,0)
- int r = rgba.red * 255;
- int g = rgba.green * 255;
- int b = rgba.blue * 255;
- #else
- int r = col.red / 256;
- int g = col.green / 256;
- int b = col.blue / 256;
- #endif
- int index;
- index = (!strcmp(p, "-fg") ? 0 :
- !strcmp(p, "-bg") ? 2 :
- !strcmp(p, "-bfg") ? 1 :
- !strcmp(p, "-bbg") ? 3 :
- !strcmp(p, "-cfg") ? 4 :
- !strcmp(p, "-cbg") ? 5 : -1);
- assert(index != -1);
- conf_set_int_int(conf, CONF_colours, index*3+0, r);
- conf_set_int_int(conf, CONF_colours, index*3+1, g);
- conf_set_int_int(conf, CONF_colours, index*3+2, b);
- }
- }
- } else if (use_pty_argv && !strcmp(p, "-e")) {
- /* This option swallows all further arguments. */
- if (!do_everything)
- break;
- if (--argc > 0) {
- int i;
- pty_argv = snewn(argc+1, char *);
- ++argv;
- for (i = 0; i < argc; i++)
- pty_argv[i] = argv[i];
- pty_argv[argc] = NULL;
- break; /* finished command-line processing */
- } else
- err = 1, fprintf(stderr, "%s: -e expects an argument\n",
- appname);
- } else if (!strcmp(p, "-title")) {
- EXPECTS_ARG;
- SECOND_PASS_ONLY;
- conf_set_str(conf, CONF_wintitle, val);
- } else if (!strcmp(p, "-log")) {
- Filename *fn;
- EXPECTS_ARG;
- SECOND_PASS_ONLY;
- fn = filename_from_str(val);
- conf_set_filename(conf, CONF_logfilename, fn);
- conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
- filename_free(fn);
- } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
- SECOND_PASS_ONLY;
- conf_set_int(conf, CONF_stamp_utmp, 0);
- } else if (!strcmp(p, "-ut")) {
- SECOND_PASS_ONLY;
- conf_set_int(conf, CONF_stamp_utmp, 1);
- } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
- SECOND_PASS_ONLY;
- conf_set_int(conf, CONF_login_shell, 0);
- } else if (!strcmp(p, "-ls")) {
- SECOND_PASS_ONLY;
- conf_set_int(conf, CONF_login_shell, 1);
- } else if (!strcmp(p, "-nethack")) {
- SECOND_PASS_ONLY;
- conf_set_int(conf, CONF_nethack_keypad, 1);
- } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
- SECOND_PASS_ONLY;
- conf_set_int(conf, CONF_scrollbar, 0);
- } else if (!strcmp(p, "-sb")) {
- SECOND_PASS_ONLY;
- conf_set_int(conf, CONF_scrollbar, 1);
- } else if (!strcmp(p, "-name")) {
- EXPECTS_ARG;
- app_name = val;
- } else if (!strcmp(p, "-xrm")) {
- EXPECTS_ARG;
- provide_xrm_string(val);
- } else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {
- help(stdout);
- exit(0);
- } else if(!strcmp(p, "-version") || !strcmp(p, "--version")) {
- version(stdout);
- exit(0);
- } else if (!strcmp(p, "-pgpfp")) {
- pgp_fingerprints();
- exit(1);
- } else if(p[0] != '-' && (!do_everything ||
- process_nonoption_arg(p, conf,
- allow_launch))) {
- /* do nothing */
- } else {
- err = 1;
- fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);
- }
- }
- return err;
- }
- GtkWidget *make_gtk_toplevel_window(void *frontend)
- {
- return gtk_window_new(GTK_WINDOW_TOPLEVEL);
- }
- extern int cfgbox(Conf *conf);
- int main(int argc, char **argv)
- {
- Conf *conf;
- int need_config_box;
- setlocale(LC_CTYPE, "");
- {
- /* Call the function in ux{putty,pterm}.c to do app-type
- * specific setup */
- extern void setup(int);
- setup(TRUE); /* TRUE means we are a one-session process */
- }
- progname = argv[0];
- /*
- * Copy the original argv before letting gtk_init fiddle with
- * it. It will be required later.
- */
- {
- int i, oldargc;
- gtkargvstart = snewn(argc-1, char *);
- for (i = 1; i < argc; i++)
- gtkargvstart[i-1] = dupstr(argv[i]);
- oldargc = argc;
- gtk_init(&argc, &argv);
- ngtkargs = oldargc - argc;
- }
- conf = conf_new();
- gtkcomm_setup();
- /*
- * Block SIGPIPE: if we attempt Duplicate Session or similar and
- * it falls over in some way, we certainly don't want SIGPIPE
- * terminating the main pterm/PuTTY. However, we'll have to
- * unblock it again when pterm forks.
- */
- block_signal(SIGPIPE, 1);
- if (argc > 1 && !strncmp(argv[1], "---", 3)) {
- extern const int dup_check_launchable;
- read_dupsession_data(conf, argv[1]);
- /* Splatter this argument so it doesn't clutter a ps listing */
- smemclr(argv[1], strlen(argv[1]));
- assert(!dup_check_launchable || conf_launchable(conf));
- need_config_box = FALSE;
- } else {
- /* By default, we bring up the config dialog, rather than launching
- * a session. This gets set to TRUE if something happens to change
- * that (e.g., a hostname is specified on the command-line). */
- int allow_launch = FALSE;
- if (do_cmdline(argc, argv, 0, &allow_launch, conf))
- exit(1); /* pre-defaults pass to get -class */
- do_defaults(NULL, conf);
- if (do_cmdline(argc, argv, 1, &allow_launch, conf))
- exit(1); /* post-defaults, do everything */
- cmdline_run_saved(conf);
- if (loaded_session)
- allow_launch = TRUE;
- need_config_box = (!allow_launch || !conf_launchable(conf));
- }
- /*
- * Put up the config box.
- */
- if (need_config_box && !cfgbox(conf))
- exit(0); /* config box hit Cancel */
- /*
- * Create the main session window. We don't really need to keep
- * the return value - the fact that it'll be linked from a zillion
- * GTK and glib bits and bobs known to the main loop will be
- * sufficient to make everything actually happen - but we stash it
- * in a global variable anyway, so that it'll be easy to find in a
- * debugger.
- */
- the_inst = new_session_window(conf, geometry_string);
- gtk_main();
- return 0;
- }
|