X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/bd25266244d48e7a7254a8e5f3b893b13002a564..14819cb379da2ae6438322f3a4c646faca7c088e:/lib-src/emacsclient.c diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c index 97261b716a..c4e01b616f 100644 --- a/lib-src/emacsclient.c +++ b/lib-src/emacsclient.c @@ -1,5 +1,5 @@ /* Client process that communicates with GNU Emacs acting as server. - Copyright (C) 1986, 1987, 1994 Free Software Foundation, Inc. + Copyright (C) 1986, 1987, 1994, 1999, 2000 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -21,18 +21,22 @@ Boston, MA 02111-1307, USA. */ #define NO_SHORTNAMES #include <../src/config.h> -#undef read -#undef write -#undef open -#undef close #undef signal #include #include +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef VMS +# include "vms-pwd.h" +#else +# include +#endif /* not VMS */ char *getenv (), *getwd (); char *getcwd (); -int geteuid (); /* This is defined with -D from the compilation command, which extracts it from ../lisp/version.el. */ @@ -47,14 +51,20 @@ char *progname; /* Nonzero means don't wait for a response from Emacs. --no-wait. */ int nowait = 0; +void print_help_and_exit (); + struct option longopts[] = { { "no-wait", no_argument, NULL, 'n' }, { "help", no_argument, NULL, 'H' }, { "version", no_argument, NULL, 'V' }, + { "alternate-editor",required_argument, NULL, 'a' }, { 0 } }; + +const char * alternate_editor = NULL; + /* Decode the options from argv and argc. The global variable `optind' will say how many arguments we used up. */ @@ -66,24 +76,30 @@ decode_options (argc, argv) while (1) { int opt = getopt_long (argc, argv, - "VHn", longopts, 0); + "VHna:", longopts, 0); if (opt == EOF) break; + alternate_editor = getenv ("ALTERNATE_EDITOR"); + switch (opt) { case 0: /* If getopt returns 0, then it has already processed a long-named option. We should do nothing. */ break; - + + case 'a': + alternate_editor = optarg; + break; + case 'n': nowait = 1; break; case 'V': - fprintf (stderr, "Version %s\n", VERSION); + fprintf (stderr, "emacsclient %s\n", VERSION); exit (1); break; @@ -94,13 +110,17 @@ decode_options (argc, argv) } } +void print_help_and_exit () { fprintf (stderr, - "Usage: %s [-n] [--no-wait] [+LINENUMBER] FILENAME\n", + "Usage: %s [-a ALTERNATE-EDITOR] [-n] [--no-wait] [+LINENUMBER] FILENAME\n", + progname); + fprintf (stderr, + "Or %s --version\n", progname); fprintf (stderr, - "Report bugs to bug-gnu-emacs@prep.ai.mit.edu.\n"); + "Report bugs to bug-gnu-emacs@gnu.org.\n"); exit (1); } @@ -135,17 +155,17 @@ quote_file_name (name) } *q++ = 0; + return copy; } -#ifdef C_ALLOCA /* Like malloc but get fatal error if memory is exhausted. */ -char * +long * xmalloc (size) unsigned int size; { - char *result = (char *) malloc (size); + long *result = (long *) malloc (size); if (result == NULL) { perror ("malloc"); @@ -153,7 +173,28 @@ xmalloc (size) } return result; } -#endif /* C_ALLOCA */ + +/* + Try to run a different command, or --if no alternate editor is + defined-- exit with an errorcode. +*/ +fail (argc, argv) + int argc; + char **argv; +{ + if (alternate_editor) + { + int i = optind -1 ; + execvp (alternate_editor, argv + i); + } + else + { + exit (1); + } +} + + + #if !defined (HAVE_SOCKETS) && !defined (HAVE_SYSVIPC) @@ -164,7 +205,8 @@ main (argc, argv) fprintf (stderr, "%s: Sorry, the Emacs server is supported only\n", argv[0]); fprintf (stderr, "on systems with Berkeley sockets or System V IPC.\n"); - exit (1); + + fail (argc, argv); } #else /* HAVE_SOCKETS or HAVE_SYSVIPC */ @@ -181,12 +223,33 @@ main (argc, argv) extern char *strerror (); extern int errno; +/* Three possibilities: + 2 - can't be `stat'ed (sets errno) + 1 - isn't owned by us + 0 - success: none of the above */ + +static int +socket_status (socket_name) + char *socket_name; +{ + struct stat statbfr; + + if (stat (socket_name, &statbfr) == -1) + return 2; + + if (statbfr.st_uid != geteuid ()) + return 1; + + return 0; +} + int main (argc, argv) int argc; char **argv; { - char system_name[32]; + char *system_name; + int system_name_length; int s, i; FILE *out, *in; struct sockaddr_un server; @@ -209,42 +272,95 @@ main (argc, argv) { fprintf (stderr, "%s: ", argv[0]); perror ("socket"); - exit (1); + fail (argc, argv); } + server.sun_family = AF_UNIX; + + { + system_name_length = 32; + + while (1) + { + system_name = (char *) xmalloc (system_name_length + 1); + + /* system_name must be null-terminated string. */ + system_name[system_name_length] = '\0'; + + if (gethostname (system_name, system_name_length) == 0) + break; + + free (system_name); + system_name_length *= 2; + } + } + #ifndef SERVER_HOME_DIR { struct stat statbfr; + int sock_status = 0; - gethostname (system_name, sizeof (system_name)); sprintf (server.sun_path, "/tmp/esrv%d-%s", geteuid (), system_name); - if (stat (server.sun_path, &statbfr) == -1) + /* See if the socket exists, and if it's owned by us. */ + sock_status = socket_status (server.sun_path); + if (sock_status) { - if (errno == ENOENT) - fprintf (stderr, - "%s: can't find socket; have you started the server?\n", - argv[0]); - else - fprintf (stderr, "%s: can't stat %s: %s\n", - argv[0], server.sun_path, strerror (errno)); - exit (1); - } - if (statbfr.st_uid != geteuid ()) - { - fprintf (stderr, "%s: Invalid socket owner\n", argv[0]); - exit (1); + /* Failing that, see if LOGNAME or USER exist and differ from + our euid. If so, look for a socket based on the UID + associated with the name. This is reminiscent of the logic + that init_editfns uses to set the global Vuser_full_name. */ + + char *user_name = (char *) getenv ("LOGNAME"); + if (!user_name) + user_name = (char *) getenv ("USER"); + + if (user_name) + { + struct passwd *pw = getpwnam (user_name); + if (pw && (pw->pw_uid != geteuid ())) + { + /* We're running under su, apparently. */ + sprintf (server.sun_path, "/tmp/esrv%d-%s", + pw->pw_uid, system_name); + sock_status = socket_status (server.sun_path); + } + } } + + switch (sock_status) + { + case 1: + /* There's a socket, but it isn't owned by us. This is OK if + we are root. */ + if (0 != geteuid ()) + { + fprintf (stderr, "%s: Invalid socket owner\n", argv[0]); + fail (argc, argv); + } + break; + + case 2: + /* `stat' failed */ + if (errno == ENOENT) + fprintf (stderr, + "%s: can't find socket; have you started the server?\n", + argv[0]); + else + fprintf (stderr, "%s: can't stat %s: %s\n", + argv[0], server.sun_path, strerror (errno)); + fail (argc, argv); + break; + } } #else if ((homedir = getenv ("HOME")) == NULL) { fprintf (stderr, "%s: No home directory\n", argv[0]); - exit (1); + fail (argc, argv); } strcpy (server.sun_path, homedir); strcat (server.sun_path, "/.emacs-server-"); - gethostname (system_name, sizeof (system_name)); strcat (server.sun_path, system_name); #endif @@ -253,7 +369,7 @@ main (argc, argv) { fprintf (stderr, "%s: ", argv[0]); perror ("connect"); - exit (1); + fail (argc, argv); } /* We use the stream OUT to send our command to the server. */ @@ -261,7 +377,7 @@ main (argc, argv) { fprintf (stderr, "%s: ", argv[0]); perror ("fdopen"); - exit (1); + fail (argc, argv); } /* We use the stream IN to read the response. @@ -273,7 +389,7 @@ main (argc, argv) { fprintf (stderr, "%s: ", argv[0]); perror ("fdopen"); - exit (1); + fail (argc, argv); } #ifdef BSD_SYSTEM @@ -291,7 +407,7 @@ main (argc, argv) "Cannot get current working directory", #endif strerror (errno)); - exit (1); + fail (argc, argv); } if (nowait) @@ -304,10 +420,10 @@ main (argc, argv) char *p = argv[i] + 1; while (*p >= '0' && *p <= '9') p++; if (*p != 0) - fprintf (out, "%s/", cwd); + fprintf (out, "%s/", quote_file_name (cwd)); } else if (*argv[i] != '/') - fprintf (out, "%s/", cwd); + fprintf (out, "%s/", quote_file_name (cwd)); fprintf (out, "%s ", quote_file_name (argv[i])); } @@ -422,7 +538,7 @@ main (argc, argv) fprintf (stderr, "%s: Cannot get current working directory: %s\n", argv[0], strerror (errno)); #endif - exit (1); + fail (argc, argv); } msgp->mtext[0] = 0; @@ -455,7 +571,8 @@ main (argc, argv) modified_arg = quote_file_name (modified_arg); if (need_cwd) - used += strlen (cwd); + /* Overestimate in case we have to quote something in CWD. */ + used += 2 * strlen (cwd); used += strlen (modified_arg) + 1; while (used + 2 > size_allocated) { @@ -466,7 +583,7 @@ main (argc, argv) } if (need_cwd) - strcat (msgp->mtext, cwd); + strcat (msgp->mtext, quote_file_name (cwd)); strcat (msgp->mtext, modified_arg); strcat (msgp->mtext, " "); @@ -477,7 +594,7 @@ main (argc, argv) if (strlen (msgp->mtext) >= 512) { fprintf (stderr, "%s: args too long for msgsnd\n", progname); - exit (1); + fail (argc, argv); } #endif msgp->mtype = 1; @@ -485,7 +602,7 @@ main (argc, argv) { fprintf (stderr, "%s: ", progname); perror ("msgsnd"); - exit (1); + fail (argc, argv); } /* Maybe wait for an answer. */