X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=utils%2Ftextclient.c;h=fe409287db6fbc0d015087505e9150b1937fa312;hb=39809ded547bdbb08207d3e514950425215b4410;hp=ed2997e0b0a0a63dc8510c97fdf56cd012e1324f;hpb=f8cf5ac7b2f53510f80a0eaf286a25298be17bfe;p=xscreensaver diff --git a/utils/textclient.c b/utils/textclient.c index ed2997e0..fe409287 100644 --- a/utils/textclient.c +++ b/utils/textclient.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2012 Jamie Zawinski +/* xscreensaver, Copyright (c) 2012-2016 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -16,11 +16,13 @@ * usePty: bool Whether to run the command interactively. * metaSendsESC: bool Whether to send Alt-x as ESC x in pty-mode. * swapBSDEL: bool Swap Backspace and Delete in pty-mode. + * + * On iOS and Android, textclient-mobile.c is used instead. */ #include "utils.h" -#ifndef USE_IPHONE /* whole file -- see OSX/iostextclient.m */ +#if !defined(USE_IPHONE) && !defined(HAVE_ANDROID) /* whole file */ #include "textclient.h" #include "resources.h" @@ -50,9 +52,12 @@ # ifdef HAVE_UTIL_H # include # endif +# ifdef HAVE_SYS_TERMIOS_H +# include +# endif #endif /* HAVE_FORKPTY */ -/*#define DEBUG*/ +#undef DEBUG extern const char *progname; @@ -60,6 +65,7 @@ struct text_data { Display *dpy; char *program; int pix_w, pix_h, char_w, char_h; + int max_lines; Bool pty_p; XtIntervalId pipe_timer; @@ -92,16 +98,53 @@ subproc_cb (XtPointer closure, int *source, XtInputId *id) } +# define BACKSLASH(c) \ + (! ((c >= 'a' && c <= 'z') || \ + (c >= 'A' && c <= 'Z') || \ + (c >= '0' && c <= '9') || \ + c == '.' || c == '_' || c == '-' || c == '+' || c == '/')) + +#ifdef HAVE_COCOA +static char * +escape_str (char *s, const char *src) +{ + while (*src) { + char c = *src++; + if (BACKSLASH(c)) *s++ = '\\'; + *s++ = c; + } + return s; +} +#endif + static void launch_text_generator (text_data *d) { XtAppContext app = XtDisplayToApplicationContext (d->dpy); char buf[255]; const char *oprogram = d->program; - char *program = (char *) malloc (strlen (oprogram) + 50); + char *s; + + size_t oprogram_size = strlen(oprogram); + size_t len; + +# ifdef HAVE_COCOA + /* /bin/sh on OS X 10.10 wipes out the PATH. */ + const char *path = getenv("PATH"); + size_t cmd_capacity = (oprogram_size + strlen(path)) * 2 + 100; + char *cmd = s = malloc (cmd_capacity); + strcpy (s, "export PATH="); + s += strlen (s); + s = escape_str (s, path); + strcpy (s, "; "); + s += strlen (s); +# else + char *cmd = s = malloc ((strlen(oprogram)) * 2 + 100); +# endif - strcpy (program, "( "); - strcat (program, oprogram); + strcpy (s, "( "); + strcat (s, oprogram); + s += strlen (s); /* Kludge! Special-case "xscreensaver-text" to tell it how wide the screen is. We used to do this by just always feeding @@ -109,14 +152,82 @@ launch_text_generator (text_data *d) "xscreensaver-text --cols %d", but that makes things blow up if someone ever uses a --program that includes a % anywhere. */ - if (!strcmp (oprogram, "xscreensaver-text")) - sprintf (program + strlen(program), " --cols %d", d->char_w); + len = 17; /* strlen("xscreensaver-text") */ + if (oprogram_size >= len && + !memcmp (oprogram, "xscreensaver-text", len) && + (oprogram[len] == ' ' || !oprogram[len])) + { + /* strstr is sloppy here. Technically, we should be parsing the command + line to identify flags and their arguments. This will blow up if one + of those pesky end users could set .textLiteral to "--cols". + */ + if (d->char_w && !strstr (oprogram, "--cols ")) + sprintf (s, " --cols %d", d->char_w); + if (d->max_lines && !strstr (oprogram, "--lines ")) + sprintf (s, " --lines %d", d->max_lines); + s += strlen(s); + +# ifdef HAVE_COCOA + /* Also special-case "xscreensaver-text" to specify the text content on + the command line. defaults(1) on macOS doesn't know about the default + screenhack resources that don't make it into the + ~/Library/Preferences/ByHost/org.jwz.xscreensaver.*.plist. + */ + + char *text_mode_flag = " --date"; + char *value_res = NULL; + char *text_mode = get_string_resource (d->dpy, "textMode", "String"); + + if (text_mode) + { + if (!strcmp (text_mode, "1") || !strcmp (text_mode, "literal")) + { + text_mode_flag = " --text"; + value_res = "textLiteral"; + } + else if (!strcmp (text_mode, "2") || !strcmp (text_mode, "file")) + { + text_mode_flag = " --file"; + value_res = "textFile"; + } + else if (!strcmp (text_mode, "3") || !strcmp (text_mode, "url")) + { + text_mode_flag = " --url"; + value_res = "textURL"; + } + else if (!strcmp (text_mode, "4") || !strcmp (text_mode, "program")) + { + text_mode_flag = " --program"; + value_res = "textProgram"; + } - strcat (program, " ) 2>&1"); + free (text_mode); + } + + strcpy (s, text_mode_flag); + s += strlen (s); + + if (value_res) + { + size_t old_s = s - cmd; + char *value = get_string_resource (d->dpy, value_res, ""); + if (!value) + value = strdup(""); + cmd = realloc(cmd, cmd_capacity + strlen(value) * 2); + s = cmd + old_s; + *s = ' '; + ++s; + s = escape_str(s, value); + free(value); + } +# endif /* HAVE_COCOA */ + } + + strcpy (s, " ) 2>&1"); # ifdef DEBUG fprintf (stderr, "%s: textclient: launch %s: %s\n", progname, - (d->pty_p ? "pty" : "pipe"), program); + (d->pty_p ? "pty" : "pipe"), cmd); # endif #ifdef HAVE_FORKPTY @@ -130,7 +241,16 @@ launch_text_generator (text_data *d) ws.ws_xpixel = d->pix_w; ws.ws_ypixel = d->pix_h; - d->pipe = NULL; + d->pipe = 0; + +# ifdef HAVE_COCOA + if (getenv ("MallocScribble")) + /* This is here to stop me from wasting my time trying to answer + this question the next time I forget about it. */ + fprintf (stderr, "%s: WARNING: forkpty hates 'Enable Guard Malloc'\n", + progname); +# endif + if ((d->pid = forkpty(&fd, NULL, NULL, &ws)) < 0) { /* Unable to fork */ @@ -146,8 +266,17 @@ launch_text_generator (text_data *d) abort(); av[i++] = "/bin/sh"; av[i++] = "-c"; - av[i++] = program; + av[i++] = cmd; av[i] = 0; +# ifdef DEBUG + { + int j; + fprintf (stderr, "%s: textclient: execvp:", progname); + for (j = 0; j < i; j++) + fprintf (stderr, " %s", av[j]); + fprintf (stderr, "\n"); + } +# endif execvp (av[0], av); sprintf (buf, "%.100s: %.100s", progname, oprogram); perror (buf); @@ -156,7 +285,9 @@ launch_text_generator (text_data *d) else { /* This is the parent fork. */ + if (d->pipe) abort(); d->pipe = fdopen (fd, "r+"); + if (d->pipe_id) abort(); d->pipe_id = XtAppAddInput (app, fileno (d->pipe), (XtPointer) (XtInputReadMask | XtInputExceptMask), @@ -177,8 +308,10 @@ launch_text_generator (text_data *d) protected_stdin_p = 1; } - if ((d->pipe = popen (program, "r"))) + if (d->pipe) abort(); + if ((d->pipe = popen (cmd, "r"))) { + if (d->pipe_id) abort(); d->pipe_id = XtAppAddInput (app, fileno (d->pipe), (XtPointer) (XtInputReadMask | XtInputExceptMask), @@ -189,12 +322,12 @@ launch_text_generator (text_data *d) } else { - sprintf (buf, "%.100s: %.100s", progname, program); + sprintf (buf, "%.100s: %.100s", progname, cmd); perror (buf); } } - free (program); + free (cmd); } @@ -211,10 +344,58 @@ relaunch_generator_timer (XtPointer closure, XtIntervalId *id) } +static void +start_timer (text_data *d) +{ + XtAppContext app = XtDisplayToApplicationContext (d->dpy); + +# ifdef DEBUG + fprintf (stderr, "%s: textclient: relaunching in %d\n", progname, + (int) d->subproc_relaunch_delay); +# endif + if (d->pipe_timer) + XtRemoveTimeOut (d->pipe_timer); + d->pipe_timer = + XtAppAddTimeOut (app, d->subproc_relaunch_delay, + relaunch_generator_timer, + (XtPointer) d); +} + + +static void +close_pipe (text_data *d) +{ + if (d->pid) + { +# ifdef DEBUG + fprintf (stderr, "%s: textclient: kill %d\n", progname, d->pid); +# endif + kill (d->pid, SIGTERM); + } + d->pid = 0; + + if (d->pipe_id) + XtRemoveInput (d->pipe_id); + d->pipe_id = 0; + + if (d->pipe) + { +# ifdef DEBUG + fprintf (stderr, "%s: textclient: pclose\n", progname); +# endif + pclose (d->pipe); + } + d->pipe = 0; + + +} + + void textclient_reshape (text_data *d, int pix_w, int pix_h, - int char_w, int char_h) + int char_w, int char_h, + int max_lines) { # if defined(HAVE_FORKPTY) && defined(TIOCSWINSZ) @@ -222,6 +403,7 @@ textclient_reshape (text_data *d, d->pix_h = pix_h; d->char_w = char_w; d->char_h = char_h; + d->max_lines = max_lines; # ifdef DEBUG fprintf (stderr, "%s: textclient: reshape: %dx%d, %dx%d\n", progname, @@ -238,6 +420,9 @@ textclient_reshape (text_data *d, ws.ws_ypixel = pix_h; ioctl (fileno (d->pipe), TIOCSWINSZ, &ws); kill (d->pid, SIGWINCH); +# ifdef DEBUG + fprintf (stderr, "%s: textclient: SIGWINCH\n", progname); +# endif } # endif /* HAVE_FORKPTY && TIOCSWINSZ */ @@ -248,14 +433,12 @@ textclient_reshape (text_data *d, */ if (!strcmp (d->program, "xscreensaver-text")) { - if (d->pid) - kill (d->pid, SIGTERM); - if (d->pipe_id) - XtRemoveInput (d->pipe_id); - if (d->pipe) - pclose (d->pipe); +# ifdef DEBUG + fprintf (stderr, "%s: textclient: reshape relaunch\n", progname); +# endif + close_pipe (d); d->input_available_p = False; - relaunch_generator_timer (d, 0); + start_timer (d); } } @@ -283,7 +466,11 @@ textclient_open (Display *dpy) } d->subproc_relaunch_delay = - (1000 * get_integer_resource (dpy, "relaunchDelay", "Time")); + get_integer_resource (dpy, "relaunchDelay", "Time"); + if (d->subproc_relaunch_delay < 1) + d->subproc_relaunch_delay = 1; + d->subproc_relaunch_delay *= 1000; + d->meta_sends_esc_p = get_boolean_resource (dpy, "metaSendsESC", "Boolean"); d->swap_bs_del_p = get_boolean_resource (dpy, "swapBSDEL", "Boolean"); @@ -299,11 +486,15 @@ textclient_open (Display *dpy) { d->pty_p = 1; d->program = strdup (getenv ("SHELL")); +# ifdef DEBUG + fprintf (stderr, "%s: textclient: standalone: %s\n", + progname, d->program); +# endif } } # endif - launch_text_generator (d); + start_timer (d); return d; } @@ -316,14 +507,12 @@ textclient_close (text_data *d) fprintf (stderr, "%s: textclient: free\n", progname); # endif + close_pipe (d); if (d->program) free (d->program); - if (d->pipe_id) - XtRemoveInput (d->pipe_id); - if (d->pipe) - pclose (d->pipe); if (d->pipe_timer) XtRemoveTimeOut (d->pipe_timer); + d->pipe_timer = 0; memset (d, 0, sizeof (*d)); free (d); } @@ -350,10 +539,6 @@ textclient_getc (text_data *d) ret = s[0]; else /* EOF */ { - if (d->pipe_id) - XtRemoveInput (d->pipe_id); - d->pipe_id = 0; - if (d->pid) { # ifdef DEBUG @@ -361,17 +546,10 @@ textclient_getc (text_data *d) progname, d->pid); # endif waitpid (d->pid, NULL, 0); - fclose (d->pipe); d->pid = 0; } - else - { -# ifdef DEBUG - fprintf (stderr, "%s: textclient: pclose\n", progname); -# endif - pclose (d->pipe); - } - d->pipe = 0; + + close_pipe (d); if (d->out_column > 0) { @@ -382,14 +560,7 @@ textclient_getc (text_data *d) d->out_buffer = "\r\n\r\n"; } -# ifdef DEBUG - fprintf (stderr, "%s: textclient: relaunching in %d\n", progname, - (int) d->subproc_relaunch_delay); -# endif - d->pipe_timer = - XtAppAddTimeOut (app, d->subproc_relaunch_delay, - relaunch_generator_timer, - (XtPointer) d); + start_timer (d); } d->input_available_p = False; }