/* * xtitle.c * * Reads and writes xterm titles. Something of a hack. * * $Id: xtitle.c,v 1.14 2005/10/19 15:59:26 tjd Exp $ * * * Copyright (C) 2003 Tim Deegan (tjd@phlegethon.org) * * 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. */ #undef HAVE_GNU_GETOPT /* Turn this on if you have GNU getopt() */ #include #include #include #include #include #include #include #include #ifdef HAVE_GNU_GETOPT #define _GNU_SOURCE #include const struct option longopts[] = { {"help", 0, NULL, 'h'}, {"read-icon", 0, NULL, 'i'}, {"read", 0, NULL, 'r'}, {"force", 0, NULL, 'f'}, {NULL, 0, NULL, 0} }; #endif /* * Globals */ static const char * me; static struct termios saved_tios; /* * How to belch forth a usage message */ static void usage () { fprintf(stderr, "%s: usage: %s [-hrf] [title]\n" " -h%s Print this message\n" " -r%s Return the current title\n" " -i%s Return the current icon label\n" " -f%s Don't perform any sanity checks\n", me, me, #ifdef HAVE_GNU_GETOPT ", --help ", ", --read ", ", --read-icon", ", --force " #else "", "", "", "" #endif ); exit(2); } /* * How to handle timeouts */ static void timeout (int sig) { tcsetattr(0, TCSANOW, &saved_tios); fprintf(stderr, "%s: timed out - are you sure this is an xterm?\n", me); exit(3); } /* * How to handle unexpected responses */ static void garbage (void) { tcsetattr(0, TCSANOW, &saved_tios); fprintf(stderr, "%s: garbled reply - are you sure this is an xterm?\n", me); fprintf(stderr, "%s\n", strerror(errno)); exit(3); } static void stdin_write_error (void) { write(0, "\007", 1); fprintf(stderr, "%s: cannot write to stdin: %s\n", me, strerror(errno)); exit(1); } /* * The heart of it all */ int main (int argc, char **argv) { int force = 0, reading = 0, icon = 0, i, len, rv; const char *term; struct termios tios; struct sigaction sa; char b; /* * Prettified name for error messages */ if ((me = strrchr(argv[0], '/') + 1) == (char *)1) me = argv[0]; /* * Arguments */ while ( #ifdef HAVE_GNU_GETOPT (i = getopt_long(argc, argv, "hirf?", longopts, NULL)) #else (i = getopt(argc, argv, "hirf?")) #endif != -1) { switch (i) { default: case 'h': case '?': usage(); break; case 'r': reading = 1; icon = 0; break; case 'i': reading = 1; icon = 1; break; case 'f': force = 1; } } /* * Sanity checks */ if (!force) { if (!isatty(0)) { fprintf(stderr, "%s: stdin is not a TTY.\n", me); exit(1); } if ((term = getenv("TERM")) == NULL) { fprintf(stderr, "%s: TERM is not set.\n", me); exit(1); } if (strncmp(term, "xterm", 5) && strncmp(term, "rxvt", 4) && strncmp(term, "dtterm", 6)) { fprintf(stderr, "%s: TERM (%s) doesn't look like an xterm.\n", me, term); exit(1); } } /* * Operations */ if (reading) { /* Grab the current title before changing it */ /* Get the current TTY settings */ rv = tcgetattr(0, &saved_tios); /* * Set a timer in case xterm doesn't respond */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = &timeout; sigaction(SIGALRM, &sa, NULL); alarm(3); /* Turn off line-buffer and echo on stdin */ tios = saved_tios; tios.c_iflag = 0; tios.c_lflag &= ~ICANON; tios.c_lflag &= ~ECHO; tios.c_cc[VMIN] = 1; /* Could be 0 but Solaris goes odd */ tios.c_cc[VTIME] = 1; tcsetattr(0, TCSANOW, &tios); /* Send the magic request: * get title is CSI '2' '1' ';' ';' 't' * get icon name is CSI '2' '1' ';' ';' 't' */ if (write(0, (icon) ? "\033[20;;t" : "\033[21;;t", 7) != 7) { tcsetattr(0, TCSANOW, &saved_tios); stdin_write_error(); } /* Read the answer: * Title will be OSC 'l' ST * Icon label will be OSC 'L' <label> ST * -- first, wait for start of OSC (discarding any other input) */ while(1) { while ((rv = read(0, &b, 1)) != 1 && (errno == EAGAIN || errno == 0)); if (rv != 1) garbage(); waiting_for_osc: if (b == '\033') break; } /* -- now, check the next two characters */ while ((rv = read(0, &b, 1)) != 1 && (errno == EAGAIN || errno == 0)); if (rv != 1) garbage(); if (b != ']') goto waiting_for_osc; while ((rv = read(0, &b, 1)) != 1 && (errno == EAGAIN || errno == 0)); if (rv != 1) garbage(); if (b != (icon ? 'L' : 'l')) goto waiting_for_osc; /* -- copy characters until we see the ST */ while(1) { while ((rv = read(0, &b, 1)) != 1 && (errno == EAGAIN || errno == 0)); if (rv != 1) garbage(); if (b == '\033') break; fprintf(stdout, "%c", b); } while ((rv = read(0, &b, 1)) != 1 && (errno == EAGAIN || errno == 0)); if (rv != 1 || b != '\\') garbage(); /* -- done */ fprintf(stdout, "\n"); /* Disarm the timer */ sa.sa_handler = SIG_DFL; sigaction(SIGALRM, &sa, NULL); /* Put the TTY back the way we found it */ tcsetattr(0, TCSANOW, &saved_tios); } if (argv[optind]) { /* Use the rest of the command line to set the new title * ( OSC '0' ';' <title> BEL ) */ i = optind; len = strlen(argv[i]); if (write(0, "\033]0;", 4) != 4 || write(0, argv[i], len) != len) stdin_write_error(); for (i++; i<argc; i++) { len = strlen(argv[i]); if (write(0, " ", 1) != 1 || write(0, argv[i], len) != len) stdin_write_error(); } if (write(0, "\007", 1) != 1) stdin_write_error(); } return 0; } /* * EOF (xtitle.c) */