static const char * MR_trace_source_jump(const char *server_cmd, const char *server_name, const char *filename, int lineno, MR_bool verbose) { char system_call[MR_SYSCALL_BUFFER_SIZE]; int status; // Point the source server to the given context. sprintf(system_call, "%s --servername \"%s\" --remote '+%d' %s", server_cmd, server_name, lineno, filename); status = MR_verbose_system_call(system_call, verbose); if (status != 0) { return "warning: source synchronisation failed"; } // Center the current line in the vim window. We need to put // the server in normal mode, just in case the user has changed // mode since the previous command was sent. status = MR_trace_source_send(server_cmd, server_name, MR_SOURCE_SERVER_RESET_STRING MR_SOURCE_SERVER_CENTRE_STRING, verbose); if (status != 0) { return "warning: source synchronisation failed"; } return NULL; }
const char * MR_trace_source_close(MR_TraceSourceServer *server, MR_bool verbose) { const char *real_server_cmd; const char *msg; if (server->server_cmd != NULL) { real_server_cmd = server->server_cmd; } else { real_server_cmd = MR_DEFAULT_SOURCE_SERVER_COMMAND; } msg = MR_trace_source_check_server(real_server_cmd, server->server_name, verbose); if (msg != NULL) { return msg; } MR_trace_source_send(real_server_cmd, server->server_name, MR_SOURCE_SERVER_RESET_STRING MR_SOURCE_SERVER_QUIT_STRING, verbose); #if 0 /* ** XXX This doesn't work properly because the server takes some ** time to close. Sometimes the server is still open when we do ** the test, and this leads to a false warning. */ /* ** We expect the following to fail. If it doesn't, the server ** hasn't closed properly. */ msg = MR_trace_source_check_server(real_server_cmd, server->server_name, verbose); if (msg == NULL) { return "warning: failed to close source server"; } else { return NULL; } #else return NULL; #endif }
const char * MR_trace_source_sync(MR_TraceSourceServer *server, const char *filename, int lineno, const char *parent_filename, int parent_lineno, MR_bool verbose) { const char *real_server_cmd; const char *msg; int status; MR_bool have_parent; MR_bool have_current; have_parent = MR_strdiff(parent_filename, "") && parent_lineno != 0; have_current = MR_strdiff(filename, "") && lineno != 0; if (!have_parent && !have_current) { // No point in continuing. return NULL; } if (server->server_cmd != NULL) { real_server_cmd = server->server_cmd; } else { real_server_cmd = MR_DEFAULT_SOURCE_SERVER_COMMAND; } msg = MR_trace_source_check_server(real_server_cmd, server->server_name, verbose); if (msg != NULL) { return msg; } if (server->split) { // When in split mode, we open two windows on the vim server, // a primary window and the secondary window. The primary window // displays what would normally be shown in non-split mode. // // If there is no parent context (e.g. at internal events) // we leave the secondary window alone. // // If we have a parent context it will be displayed in the // primary window, so in this case we show the current context // in the secondary window. // // The primary window is the one second from the top (which will // usually be the bottom window). The secondary window is at the top. if (have_parent && have_current) { // Move to the secondary (top) window. status = MR_trace_source_send(real_server_cmd, server->server_name, MR_SOURCE_SERVER_RESET_STRING MR_SOURCE_SERVER_TOP_STRING, verbose); if (status != 0) { return "warning: source synchronisation failed"; } msg = MR_trace_source_jump(real_server_cmd, server->server_name, filename, lineno, verbose); if (msg != NULL) { return msg; } // Move down one window to the primary. status = MR_trace_source_send(real_server_cmd, server->server_name, MR_SOURCE_SERVER_RESET_STRING MR_SOURCE_SERVER_DOWN_STRING, verbose); if (status != 0) { return "warning: source synchronisation failed"; } } else { // Move to the primary (second from top) window. status = MR_trace_source_send(real_server_cmd, server->server_name, MR_SOURCE_SERVER_RESET_STRING MR_SOURCE_SERVER_TOP_STRING MR_SOURCE_SERVER_DOWN_STRING, verbose); if (status != 0) { return "warning: source synchronisation failed"; } } } // We show the parent context if we can, since if both are present // the parent context is usually more interesting. Otherwise we // show the current context. if (have_parent) { msg = MR_trace_source_jump(real_server_cmd, server->server_name, parent_filename, parent_lineno, verbose); if (msg != NULL) { return msg; } } else { msg = MR_trace_source_jump(real_server_cmd, server->server_name, filename, lineno, verbose); if (msg != NULL) { return msg; } } return NULL; }
const char * MR_trace_source_open_server(MR_TraceSourceServer *server, const char *window_cmd, int timeout, MR_bool verbose) { const char *real_window_cmd; const char *real_server_cmd; char *name; const char *msg; char system_call[MR_SYSCALL_BUFFER_SIZE]; int status; size_t base_len; size_t i; if (window_cmd != NULL) { real_window_cmd = window_cmd; } else { real_window_cmd = MR_DEFAULT_SOURCE_WINDOW_COMMAND; } if (server->server_cmd != NULL) { real_server_cmd = server->server_cmd; } else { real_server_cmd = MR_DEFAULT_SOURCE_SERVER_COMMAND; } // 1) check that display is set; // 2) check that server is valid; // 3) start a server with a unique name; // 4) wait until the server is found; // 5) (if required) split the window. msg = MR_trace_source_check_display(); if (msg != NULL) { return msg; } msg = MR_trace_source_check_server_cmd(real_server_cmd, verbose); if (msg != NULL) { return msg; } // If possible, we generate a unique name of the form // '<basename>.<hostname>.<pid>', but if the required functions // aren't available, we fall back to appending numbers to the // basename in sequence until one is found that is not being used. // This is quite a slow way of doing things, and there is also a // race condition, but it is difficult to see a better way. // We should let the server pick a unique name for itself, but // how would you communicate this name back to this process? base_len = strlen(MR_SOURCE_SERVER_BASENAME); #if defined(MR_HAVE_GETPID) && defined(MR_HAVE_GETHOSTNAME) // Need to leave enough room for the pid, two '.'s and the // terminating zero. name = MR_malloc(base_len + MR_SERVER_HOSTNAME_CHARS + 32); strcpy(name, MR_SOURCE_SERVER_BASENAME); // Append the '.' and hostname. name[base_len] = '.'; gethostname(name + base_len + 1, MR_SERVER_HOSTNAME_CHARS); // Do this just in case gethostname didn't terminate the string: name[base_len + 1 + MR_SERVER_HOSTNAME_CHARS] = '\0'; // Find the end of the string and append the '.' and pid. i = base_len + 1 + strlen(name + base_len + 1); sprintf(name + i, ".%ld", (long) getpid()); #else i = 0; // Need to leave enough room for a '.', the integer and the // terminating zero. name = MR_malloc(base_len + 10); do { i++; sprintf(name, "%s.%lu", MR_SOURCE_SERVER_BASENAME, (unsigned long)i); // This should fail if there is no server with this name. msg = MR_trace_source_check_server(real_server_cmd, name, verbose); } while (msg == NULL); #endif server->server_name = name; // Start the server in read-only mode, to discourage the user // from trying to edit the source. sprintf(system_call, "%s %s -R --servername \"%s\" %s &", real_window_cmd, real_server_cmd, name, (verbose ? "" : "> /dev/null 2>&1")); MR_verbose_system_call(system_call, verbose); // We need to wait until the source server has registered itself // with the X server. If we don't, we may execute remote commands // before the server has started up, and this will result in vim // (rather inconveniently) starting its own server on mdb's terminal. msg = MR_trace_source_attach(server, timeout, verbose); if (msg != NULL) { // Something went wrong, so we should free the server name // we allocated just above. MR_free(server->server_name); server->server_name = NULL; return msg; } if (server->split) { // Split the window. status = MR_trace_source_send(real_server_cmd, server->server_name, MR_SOURCE_SERVER_RESET_STRING MR_SOURCE_SERVER_SPLIT_STRING, verbose); if (status != 0) { server->split = MR_FALSE; return "warning: unable to split source window"; } } return NULL; }