summaryrefslogtreecommitdiff
path: root/src/integration.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/integration.c')
-rw-r--r--src/integration.c1198
1 files changed, 1198 insertions, 0 deletions
diff --git a/src/integration.c b/src/integration.c
new file mode 100644
index 000000000..05a9dec3f
--- /dev/null
+++ b/src/integration.c
@@ -0,0 +1,1198 @@
+/* vi:set ts=8 sw=8:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * Visual Workshop integration by Gordon Prieur
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * Integration with Sun Workshop.
+ *
+ * This file should not change much, it's also used by other editors that
+ * connect to Workshop. Consider changing workshop.c instead.
+ */
+/*
+-> consider using MakeSelectionVisible instead of gotoLine hacks
+ to show the line properly
+ -> consider using glue instead of our own message wrapping functions
+ (but can only use glue if we don't have to distribute source)
+*/
+
+#include "vim.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+
+#ifdef INET_SOCKETS
+#include <netdb.h>
+#include <netinet/in.h>
+#else
+#include <sys/un.h>
+#endif
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#ifdef HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+#include <unistd.h>
+#include <string.h>
+
+#include <X11/Intrinsic.h>
+#include <Xm/Xm.h>
+#include <Xm/AtomMgr.h>
+#include <Xm/PushB.h>
+
+#ifdef HAVE_X11_XPM_H
+# include <X11/xpm.h>
+#else
+# ifdef HAVE_XM_XPMP_H
+# include <Xm/XpmP.h>
+# endif
+#endif
+
+#ifdef HAVE_UTIL_DEBUG_H
+# include <util/debug.h>
+#endif
+#ifdef HAVE_UTIL_MSGI18N_H
+# include <util/msgi18n.h>
+#endif
+
+#include "integration.h" /* <EditPlugin/integration.h> */
+#ifdef HAVE_FRAME_H
+# include <frame.h>
+#endif
+
+#ifndef MAX
+# define MAX(a, b) (a) > (b) ? (a) : (b)
+#endif
+
+#ifndef NOCATGETS
+# define NOCATGETS(x) x
+#endif
+
+/* Functions private to this file */
+static void workshop_connection_closed(void);
+static void messageFromEserve(XtPointer clientData, int *NOTUSED1, XtInputId *NOTUSED2);
+static void workshop_disconnect(void);
+static void workshop_sensitivity(int num, char *table);
+static void adjust_sign_name(char *filename);
+static void process_menuItem(char *);
+static void process_toolbarButton(char *);
+static void workshop_set_option_first(char *name, char *value);
+
+
+#define CMDBUFSIZ 2048
+
+#ifdef DEBUG
+static FILE *dfd;
+static void pldebug(char *, ...);
+static void unrecognised_message(char *);
+
+#define HANDLE_ERRORS(cmd) else unrecognised_message(cmd);
+#else
+#define HANDLE_ERRORS(cmd)
+#endif
+
+/*
+ * Version number of the protocol between an editor and eserve.
+ * This number should be incremented when the protocol
+ * is changed.
+ */
+#define PROTOCOL_VERSION "4.0.0"
+
+static int sd = -1;
+static XtInputId inputHandler; /* Cookie for input */
+
+Boolean save_files = True; /* When true, save all files before build actions */
+
+void
+workshop_connection_closed(void)
+{
+ /*
+ * socket closed on other end
+ */
+ XtRemoveInput(inputHandler);
+ inputHandler = 0;
+ sd = -1;
+}
+
+ static char *
+getCommand(void)
+{
+ int len; /* length of this command */
+ char lenbuf[7]; /* get the length string here */
+ char *newcb; /* used to realloc cmdbuf */
+ static char *cmdbuf;/* get the command string here */
+ static int cbsize;/* size of cmdbuf */
+
+ if ((len = read(sd, &lenbuf, 6)) == 6) {
+ lenbuf[6] = 0; /* Terminate buffer such that atoi() works right */
+ len = atoi(lenbuf);
+ if (cbsize < (len + 1)) {
+ newcb = (char *) realloc(cmdbuf,
+ MAX((len + 256), CMDBUFSIZ));
+ if (newcb != NULL) {
+ cmdbuf = newcb;
+ cbsize = MAX((len + 256), CMDBUFSIZ);
+ }
+ }
+ if (cbsize >= len && (len = read(sd, cmdbuf, len)) > 0) {
+ cmdbuf[len] = 0;
+ return cmdbuf;
+ } else {
+ return NULL;
+ }
+ } else {
+ if (len == 0) { /* EOF */
+ workshop_connection_closed();
+ }
+ return NULL;
+ }
+
+}
+
+/*ARGSUSED*/
+void
+messageFromEserve(XtPointer clientData, int *NOTUSED1, XtInputId *NOTUSED2)
+{
+ char *cmd; /* the 1st word of the command */
+
+ cmd = getCommand();
+ if (cmd == NULL) {
+ /* We're being shut down by eserve and the "quit" message
+ * didn't arrive before the socket connection got closed */
+ return;
+ }
+#ifdef DEBUG
+ pldebug("%s\n", cmd);
+#endif
+ switch (*cmd) {
+ case 'a':
+ if (cmd[1] == 'c' &&
+ strncmp(cmd, NOCATGETS("ack "), 4) == 0) {
+ int ackNum;
+ char buf[20];
+
+ ackNum = atoi(&cmd[4]);
+ sprintf(buf, NOCATGETS("ack %d\n"), ackNum);
+ write(sd, buf, strlen(buf));
+ } else if (strncmp(cmd,
+ NOCATGETS("addMarkType "), 12) == 0) {
+ int idx;
+ char *color;
+ char *sign;
+
+ idx = atoi(strtok(&cmd[12], " "));
+ color = strtok(NULL, NOCATGETS("\001"));
+ sign = strtok(NULL, NOCATGETS("\001"));
+ /* Skip space that separates names */
+ if (color) {
+ color++;
+ }
+ if (sign) {
+ sign++;
+ }
+ /* Change sign name to accomodate a different size? */
+ adjust_sign_name(sign);
+ workshop_add_mark_type(idx, color, sign);
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'b':
+ if (strncmp(cmd,
+ NOCATGETS("balloon "), 8) == 0) {
+ char *tip;
+
+ tip = strtok(&cmd[8], NOCATGETS("\001"));
+ workshop_show_balloon_tip(tip);
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'c':
+ if (strncmp(cmd,
+ NOCATGETS("changeMarkType "), 15) == 0) {
+ char *file;
+ int markId;
+ int type;
+
+ file = strtok(&cmd[15], " ");
+ markId = atoi(strtok(NULL, " "));
+ type = atoi(strtok(NULL, " "));
+ workshop_change_mark_type(file, markId, type);
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'd':
+ if (strncmp(cmd, NOCATGETS("deleteMark "), 11) == 0) {
+ char *file;
+ int markId;
+
+ file = strtok(&cmd[11], " ");
+ markId = atoi(strtok(NULL, " "));
+ workshop_delete_mark(file, markId);
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'f':
+ if (cmd[1] == 'o' &&
+ strncmp(cmd, NOCATGETS("footerMsg "), 10) == 0) {
+ int severity;
+ char *message;
+
+ severity =
+ atoi(strtok(&cmd[10], " "));
+ message = strtok(NULL, NOCATGETS("\001"));
+
+ workshop_footer_message(message, severity);
+ } else if (strncmp(cmd,
+ NOCATGETS("frontFile "), 10) == 0) {
+ char *file;
+
+ file = strtok(&cmd[10], " ");
+ workshop_front_file(file);
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'g':
+ if (cmd[1] == 'e' &&
+ strncmp(cmd, NOCATGETS("getMarkLine "), 12) == 0) {
+ char *file;
+ int markid;
+ int line;
+ char buf[100];
+
+ file = strtok(&cmd[12], " ");
+ markid = atoi(strtok(NULL, " "));
+ line = workshop_get_mark_lineno(file, markid);
+ sprintf(buf, NOCATGETS("markLine %s %d %d\n"),
+ file, markid, line);
+ write(sd, buf, strlen(buf));
+ } else if (cmd[1] == 'o' && cmd[4] == 'L' &&
+ strncmp(cmd, NOCATGETS("gotoLine "), 9) == 0) {
+ char *file;
+ int lineno;
+
+ file = strtok(&cmd[9], " ");
+ lineno = atoi(strtok(NULL, " "));
+ workshop_goto_line(file, lineno);
+ } else if (strncmp(cmd,
+ NOCATGETS("gotoMark "), 9) == 0) {
+ char *file;
+ int markId;
+ char *message;
+
+ file = strtok(&cmd[9], " ");
+ markId = atoi(strtok(NULL, " "));
+ message = strtok(NULL, NOCATGETS("\001"));
+ workshop_goto_mark(file, markId, message);
+#ifdef NOHANDS_SUPPORT_FUNCTIONS
+ } else if (strcmp(cmd, NOCATGETS("getCurrentFile")) == 0) {
+ char *f = workshop_test_getcurrentfile();
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("currentFile %d %s"),
+ f ? strlen(f) : 0, f ? f : "");
+ workshop_send_message(buffer);
+ } else if (strcmp(cmd, NOCATGETS("getCursorRow")) == 0) {
+ int row = workshop_test_getcursorrow();
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("cursorRow %d"), row);
+ workshop_send_message(buffer);
+ } else if (strcmp(cmd, NOCATGETS("getCursorCol")) == 0) {
+ int col = workshop_test_getcursorcol();
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("cursorCol %d"), col);
+ workshop_send_message(buffer);
+ } else if (strcmp(cmd, NOCATGETS("getCursorRowText")) == 0) {
+ char *t = workshop_test_getcursorrowtext();
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("cursorRowText %d %s"),
+ t ? strlen(t) : 0, t ? t : "");
+ workshop_send_message(buffer);
+ } else if (strcmp(cmd, NOCATGETS("getSelectedText")) == 0) {
+ char *t = workshop_test_getselectedtext();
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("selectedText %d %s"),
+ t ? strlen(t) : 0, t ? t : "");
+ workshop_send_message(buffer);
+#endif
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'l':
+ if (strncmp(cmd, NOCATGETS("loadFile "), 9) == 0) {
+ char *file;
+ int line;
+ char *frameid;
+
+ file = strtok(&cmd[9], " ");
+ line = atoi(strtok(NULL, " "));
+ frameid = strtok(NULL, " ");
+ workshop_load_file(file, line, frameid);
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'm': /* Menu, minimize, maximize */
+ if (cmd[1] == 'e' && cmd[4] == 'B' &&
+ strncmp(cmd, NOCATGETS("menuBegin "), 10) == 0) {
+ workshop_menu_begin(&cmd[10]);
+ } else if (cmd[1] == 'e' && cmd[4] == 'I' &&
+ strncmp(cmd, NOCATGETS("menuItem "), 9) == 0) {
+ process_menuItem(cmd);
+ } else if (cmd[1] == 'e' && cmd[4] == 'E' &&
+ strcmp(cmd, NOCATGETS("menuEnd")) == 0) {
+ workshop_menu_end();
+ } else if (cmd[1] == 'a' &&
+ strcmp(cmd, NOCATGETS("maximize")) == 0) {
+ workshop_maximize();
+ } else if (strcmp(cmd, NOCATGETS("minimize")) == 0) {
+ workshop_minimize();
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'o':
+ if (cmd[1] == 'p' &&
+ strcmp(cmd, NOCATGETS("option"))) {
+ char *name;
+ char *value;
+
+ name = strtok(&cmd[7], " ");
+ value = strtok(NULL, " ");
+ workshop_set_option_first(name, value);
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'p':
+ if (strcmp(cmd, NOCATGETS("ping")) == 0) {
+#if 0
+ int pingNum;
+
+ pingNum = atoi(&cmd[5]);
+ workshop_send_ack(ackNum);
+ WHAT DO I DO HERE?
+#endif
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'q':
+ if (strncmp(cmd, NOCATGETS("quit"), 4) == 0) {
+
+ /* Close the connection. It's important to do
+ * that now, since workshop_quit might be
+ * looking at open files. For example, if you
+ * have modified one of the files without
+ * saving, NEdit will ask you what you want to
+ * do, and spin loop by calling
+ * XtAppProcessEvent while waiting for your
+ * reply. In this case, if we still have an
+ * input handler and the socket has been
+ * closed on the other side when eserve
+ * expired, we will hang in IoWait.
+ */
+ workshop_disconnect();
+
+ workshop_quit();
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 'r':
+ if (cmd[1] == 'e' &&
+ strncmp(cmd, NOCATGETS("reloadFile "), 11) == 0) {
+ char *file;
+ int line;
+
+ file = strtok(&cmd[11], " ");
+ line = atoi(strtok(NULL, " "));
+ workshop_reload_file(file, line);
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 's':
+ if (cmd[1] == 'e' && cmd[2] == 't' &&
+ strncmp(cmd, NOCATGETS("setMark "), 8) == 0) {
+ char *file;
+ int line;
+ int markId;
+ int type;
+
+ file = strtok(&cmd[8], " ");
+ line = atoi(strtok(NULL, " "));
+ markId = atoi(strtok(NULL, " "));
+ type = atoi(strtok(NULL, " "));
+ workshop_set_mark(file, line, markId, type);
+ } else if (cmd[1] == 'h' &&
+ strncmp(cmd, NOCATGETS("showFile "), 9) == 0) {
+ workshop_show_file(&cmd[9]);
+ } else if (cmd[1] == 'u' &&
+ strncmp(cmd, NOCATGETS("subMenu "), 8) == 0) {
+ char *label;
+
+ label = strtok(&cmd[8], NOCATGETS("\001"));
+ workshop_submenu_begin(label);
+ } else if (cmd[1] == 'u' &&
+ strcmp(cmd, NOCATGETS("subMenuEnd")) == 0) {
+ workshop_submenu_end();
+ } else if (cmd[1] == 'e' && cmd[2] == 'n' &&
+ strncmp(cmd, NOCATGETS("sensitivity "), 12) == 0) {
+ int num;
+ char *bracket;
+ char *table;
+
+ num = atoi(strtok(&cmd[12], " "));
+ bracket = strtok(NULL, " ");
+ if (*bracket != '[') {
+ fprintf(stderr, NOCATGETS("Parsing "
+ "error for sensitivity\n"));
+ } else {
+ table = strtok(NULL, NOCATGETS("]"));
+ workshop_sensitivity(num, table);
+ }
+ } else if (cmd[1] == 'e' && cmd[2] == 'n' && cmd[3] == 'd' &&
+ strncmp(cmd, NOCATGETS("sendVerb "), 9) == 0) {
+ /* Send the given verb back (used for the
+ * debug.lineno callback (such that other tools
+ * can obtain the position coordinates or the
+ * selection) */
+ char *verb;
+
+ verb = strtok(&cmd[9], " ");
+ workshop_perform_verb(verb, NULL);
+ } else if (cmd[1] == 'a' &&
+ strncmp(cmd, NOCATGETS("saveFile "), 9) == 0) {
+ workshop_save_file(&cmd[9]);
+#ifdef NOHANDS_SUPPORT_FUNCTIONS
+ } else if (strncmp(cmd, NOCATGETS("saveSensitivity "), 16) == 0) {
+ char *file;
+
+ file = strtok(&cmd[16], " ");
+ workshop_save_sensitivity(file);
+#endif
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+ case 't': /* Toolbar */
+ if (cmd[8] == 'e' &&
+ strncmp(cmd, NOCATGETS("toolbarBegin"), 12) == 0) {
+ workshop_toolbar_begin();
+ } else if (cmd[8] == 'u' &&
+ strncmp(cmd, NOCATGETS("toolbarButton"), 13) == 0) {
+ process_toolbarButton(cmd);
+ } else if (cmd[7] == 'E' &&
+ strcmp(cmd, NOCATGETS("toolbarEnd")) == 0) {
+ workshop_toolbar_end();
+ }
+ HANDLE_ERRORS(cmd);
+ break;
+
+#ifdef DEBUG
+ default:
+ unrecognised_message(cmd);
+ break;
+#endif
+ }
+}
+
+static void
+process_menuItem(
+ char *cmd)
+{
+ char *label = strtok(&cmd[9], NOCATGETS("\001"));
+ char *verb = strtok(NULL, NOCATGETS("\001"));
+ char *acc = strtok(NULL, NOCATGETS("\001"));
+ char *accText = strtok(NULL, NOCATGETS("\001"));
+ char *name = strtok(NULL, NOCATGETS("\001"));
+ char *sense = strtok(NULL, NOCATGETS("\n"));
+ char *filepos = strtok(NULL, NOCATGETS("\n"));
+ if (*acc == '-') {
+ acc = NULL;
+ }
+ if (*accText == '-') {
+ accText = NULL;
+ }
+ workshop_menu_item(label, verb, acc, accText, name, filepos, sense);
+
+}
+
+
+static void
+process_toolbarButton(
+ char *cmd) /* button definition */
+{
+ char *label = strtok(&cmd[14], NOCATGETS("\001"));
+ char *verb = strtok(NULL, NOCATGETS("\001"));
+ char *senseVerb = strtok(NULL, NOCATGETS("\001"));
+ char *filepos = strtok(NULL, NOCATGETS("\001"));
+ char *help = strtok(NULL, NOCATGETS("\001"));
+ char *sense = strtok(NULL, NOCATGETS("\001"));
+ char *file = strtok(NULL, NOCATGETS("\001"));
+ char *left = strtok(NULL, NOCATGETS("\n"));
+
+ if (!strcmp(label, NOCATGETS("-"))) {
+ label = NULL;
+ }
+ if (!strcmp(help, NOCATGETS("-"))) {
+ help = NULL;
+ }
+ if (!strcmp(file, NOCATGETS("-"))) {
+ file = NULL;
+ }
+ if (!strcmp(senseVerb, NOCATGETS("-"))) {
+ senseVerb = NULL;
+ }
+ workshop_toolbar_button(label, verb, senseVerb, filepos, help,
+ sense, file, left);
+}
+
+
+#ifdef DEBUG
+void
+unrecognised_message(
+ char *cmd)
+{
+ pldebug("Unrecognised eserve message:\n\t%s\n", cmd);
+ /* abort(); */
+}
+#endif
+
+
+/* Change sign name to accomodate a different size:
+ * Create the filename based on the height. The filename format
+ * of multisize icons are:
+ * x.xpm : largest icon
+ * x1.xpm : smaller icon
+ * x2.xpm : smallest icon */
+ void
+adjust_sign_name(char *filename)
+{
+ char *s;
+ static int fontSize = -1;
+
+ if (fontSize == -1)
+ fontSize = workshop_get_font_height();
+ if (fontSize == 0)
+ return;
+ if (filename[0] == '-')
+ return;
+
+ /* This is ugly: later we should instead pass the fontheight over
+ * to eserve on startup and let eserve just send the right filenames
+ * to us in the first place
+
+ * I know that the filename will end with 1.xpm (see
+ * GuiEditor.cc`LispPrintSign if you wonder why) */
+ s = filename+strlen(filename)-5;
+ if (fontSize <= 11)
+ strcpy(s, "2.xpm");
+ else if (fontSize <= 15)
+ strcpy(s, "1.xpm");
+ else
+ strcpy(s, ".xpm");
+}
+
+/* Were we invoked by WorkShop? This function can be used early during startup
+ if you want to do things differently if the editor is started standalone
+ or in WorkShop mode. For example, in standalone mode you may not want to
+ add a footer/message area or a sign gutter. */
+int
+workshop_invoked()
+{
+ static int result = -1;
+ if (result == -1) {
+ result = (getenv(NOCATGETS("SPRO_EDITOR_SOCKET")) != NULL);
+ }
+ return result;
+}
+
+/* Connect back to eserve */
+void workshop_connect(XtAppContext context)
+{
+#ifdef INET_SOCKETS
+ struct sockaddr_in server;
+ struct hostent * host;
+ int port;
+#else
+ struct sockaddr_un server;
+#endif
+ char buf[32];
+ char * address;
+#ifdef DEBUG
+ char *file;
+#endif
+
+ address = getenv(NOCATGETS("SPRO_EDITOR_SOCKET"));
+ if (address == NULL) {
+ return;
+ }
+
+#ifdef INET_SOCKETS
+ port = atoi(address);
+
+ if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ PERROR(NOCATGETS("workshop_connect"));
+ return;
+ }
+
+ /* Get the server internet address and put into addr structure */
+ /* fill in the socket address structure and connect to server */
+ memset((char *)&server, '\0', sizeof(server));
+ server.sin_family = AF_INET;
+ server.sin_port = port;
+ if ((host = gethostbyname(NOCATGETS("localhost"))) == NULL) {
+ PERROR(NOCATGETS("gethostbyname"));
+ sd = -1;
+ return;
+ }
+ memcpy((char *)&server.sin_addr, host->h_addr, host->h_length);
+#else
+ if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ PERROR(NOCATGETS("workshop_connect"));
+ return;
+ }
+
+ server.sun_family = AF_UNIX;
+ strcpy(server.sun_path, address);
+#endif
+ /* Connect to server */
+ if (connect(sd, (struct sockaddr *)&server, sizeof(server))) {
+ if (errno == ECONNREFUSED) {
+ close(sd);
+#ifdef INET_SOCKETS
+ if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ PERROR(NOCATGETS("workshop_connect"));
+ return;
+ }
+#else
+ if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ PERROR(NOCATGETS("workshop_connect"));
+ return;
+ }
+#endif
+ if (connect(sd, (struct sockaddr *)&server,
+ sizeof(server))) {
+ PERROR(NOCATGETS("workshop_connect"));
+ return;
+ }
+
+ } else {
+ PERROR(NOCATGETS("workshop_connect"));
+ return;
+ }
+ }
+
+ /* tell notifier we are interested in being called
+ * when there is input on the editor connection socket
+ */
+ inputHandler = XtAppAddInput(context, sd, (XtPointer) XtInputReadMask,
+ messageFromEserve, NULL);
+#ifdef DEBUG
+ if ((file = getenv(NOCATGETS("SPRO_PLUGIN_DEBUG"))) != NULL) {
+ char buf[BUFSIZ];
+
+ unlink(file);
+ sprintf(buf, "date > %s", file);
+ system(buf);
+ dfd = fopen(file, "a");
+ } else {
+ dfd = NULL;
+ }
+#endif
+
+ sprintf(buf, NOCATGETS("connected %s %s %s\n"),
+ workshop_get_editor_name(),
+ PROTOCOL_VERSION,
+ workshop_get_editor_version());
+ write(sd, buf, strlen(buf));
+
+ sprintf(buf, NOCATGETS("ack 1\n"));
+ write(sd, buf, strlen(buf));
+}
+
+void workshop_disconnect()
+{
+ /* Probably need to send some message here */
+
+ /*
+ * socket closed on other end
+ */
+ XtRemoveInput(inputHandler);
+ close(sd);
+ inputHandler = 0;
+ sd = -1;
+
+}
+
+/*
+ * Utility functions
+ */
+
+/* Set icon for the window */
+void
+workshop_set_icon(Display *display, Widget shell, char **xpmdata,
+ int width, int height)
+{
+ Pixel bgPixel;
+ XpmAttributes xpmAttributes;
+ XSetWindowAttributes attr;
+ Window iconWindow;
+ int depth;
+ int screenNum;
+ Pixmap pixmap;
+
+ /* Create the pixmap/icon window which is shown when you
+ * iconify the sccs viewer
+ * This code snipped was adapted from Sun WorkShop's source base,
+ * setIcon.cc.
+ */
+ XtVaGetValues(shell, XmNbackground, &bgPixel, NULL);
+ screenNum = XScreenNumberOfScreen(XtScreen(shell));
+ depth = DisplayPlanes(display, screenNum);
+ xpmAttributes.valuemask = XpmColorSymbols;
+ xpmAttributes.numsymbols = 1;
+ xpmAttributes.colorsymbols =
+ (XpmColorSymbol *)XtMalloc(sizeof (XpmColorSymbol) *
+ xpmAttributes.numsymbols);
+ xpmAttributes.colorsymbols[0].name = NOCATGETS("BgColor");
+ xpmAttributes.colorsymbols[0].value = NULL;
+ xpmAttributes.colorsymbols[0].pixel = bgPixel;
+ if (XpmCreatePixmapFromData(display,
+ RootWindow(display, screenNum), xpmdata, &pixmap,
+ NULL, &xpmAttributes) >= 0) {
+ attr.background_pixmap = pixmap;
+ iconWindow = XCreateWindow(display, RootWindow(display,
+ screenNum), 0, 0, width, height, 0, depth,
+ (unsigned int)CopyFromParent,
+ CopyFromParent, CWBackPixmap, &attr);
+
+ XtVaSetValues(shell,
+ XtNiconWindow, iconWindow, NULL);
+ }
+ XtFree((char *)xpmAttributes.colorsymbols);
+}
+
+/* Minimize and maximize shells. From libutil's shell.cc. */
+
+/* utility functions from libutil's shell.cc */
+static Boolean
+isWindowMapped(Display *display, Window win)
+{
+ XWindowAttributes winAttrs;
+ XGetWindowAttributes(display,
+ win,
+ &winAttrs);
+ if (winAttrs.map_state == IsViewable) {
+ return(True);
+ } else {
+ return(False);
+ }
+}
+
+static Boolean
+isMapped(Widget widget)
+{
+ if (widget == NULL) {
+ return(False);
+ }
+
+ if (XtIsRealized(widget) == False) {
+ return(False);
+ }
+
+ return(isWindowMapped(XtDisplay(widget), XtWindow(widget)));
+}
+
+static Boolean
+widgetIsIconified(
+ Widget w)
+{
+ Atom wm_state;
+ Atom act_type; /* actual Atom type returned */
+ int act_fmt; /* actual format returned */
+ u_long nitems_ret; /* number of items returned */
+ u_long bytes_after; /* number of bytes remaining */
+ u_long *property; /* actual property returned */
+
+ /*
+ * If a window is iconified its WM_STATE is set to IconicState. See
+ * ICCCM Version 2.0, section 4.1.3.1 for more details.
+ */
+
+ wm_state = XmInternAtom(XtDisplay(w), NOCATGETS("WM_STATE"), False);
+ if (XtWindow(w) != 0) { /* only check if window exists! */
+ XGetWindowProperty(XtDisplay(w), XtWindow(w), wm_state, 0L, 2L,
+ False, AnyPropertyType, &act_type, &act_fmt, &nitems_ret,
+ &bytes_after, (u_char **) &property);
+ if (nitems_ret == 2 && property[0] == IconicState) {
+ return True;
+ }
+ }
+
+ return False;
+
+} /* end widgetIsIconified */
+
+void
+workshop_minimize_shell(Widget shell)
+{
+ if (shell != NULL &&
+ XtIsObject(shell) &&
+ XtIsRealized(shell) == True) {
+ if (isMapped(shell) == True) {
+ XIconifyWindow(XtDisplay(shell), XtWindow(shell),
+ XScreenNumberOfScreen(XtScreen(shell)));
+ }
+ XtVaSetValues(shell,
+ XmNiconic, True,
+ NULL);
+ }
+}
+
+void workshop_maximize_shell(Widget shell)
+{
+ if (shell != NULL &&
+ XtIsRealized(shell) == True &&
+ widgetIsIconified(shell) == True &&
+ isMapped(shell) == False) {
+ XtMapWidget(shell);
+ /* This used to be
+ XtPopdown(shell);
+ XtPopup(shell, XtGrabNone);
+ However, I found that that would drop any transient
+ windows that had been iconified with the window.
+ According to the ICCCM, XtMapWidget should be used
+ to bring a window from Iconic to Normal state.
+ However, Rich Mauri did a lot of work on this during
+ Bart, and found that XtPopDown,XtPopup was required
+ to fix several bugs involving multiple CDE workspaces.
+ I've tested it now and things seem to work fine but
+ I'm leaving this note for history in case this needs
+ to be revisited.
+ */
+ }
+}
+
+
+Boolean workshop_get_width_height(int *width, int *height)
+{
+ static int wid = 0;
+ static int hgt = 0;
+ static Boolean firstTime = True;
+ static Boolean success = False;
+
+ if (firstTime) {
+ char *settings;
+
+ settings = getenv(NOCATGETS("SPRO_GUI_WIDTH_HEIGHT"));
+ if (settings != NULL) {
+ wid = atoi(settings);
+ settings = strrchr(settings, ':');
+ if (settings++ != NULL) {
+ hgt = atoi(settings);
+ }
+ if (wid > 0 && hgt > 0) {
+ success = True;
+ }
+ firstTime = False;
+ }
+ }
+
+ if (success) {
+ *width = wid;
+ *height = hgt;
+ }
+ return success;
+}
+
+
+Boolean workshop_get_rows_cols(int *rows, int *cols)
+{
+ static int r = 0;
+ static int c = 0;
+ static Boolean firstTime = True;
+ static Boolean success = False;
+
+ if (firstTime) {
+ char *settings;
+
+ settings = getenv(NOCATGETS("SPRO_GUI_ROWS_COLS"));
+ if (settings != NULL) {
+ r = atoi(settings);
+ settings = strrchr(settings, ':');
+ if (settings++ != NULL) {
+ c = atoi(settings);
+ }
+ if (r > 0 && c > 0) {
+ success = True;
+ }
+ firstTime = False;
+ }
+ }
+
+ if (success) {
+ *rows = r;
+ *cols = c;
+ }
+ return success;
+}
+
+/*
+ * Toolbar code
+ */
+
+void workshop_sensitivity(int num, char *table)
+{
+ /* build up a verb table */
+ VerbSense *vs;
+ int i;
+ char *s;
+ if ((num < 1) || (num > 500)) {
+ return;
+ }
+
+ vs = (VerbSense *)malloc((num+1)*sizeof(VerbSense));
+
+ /* Point to the individual names (destroys the table string, but
+ * that's okay -- this is more efficient than duplicating strings) */
+ s = table;
+ for (i = 0; i < num; i++) {
+ while (*s == ' ') {
+ s++;
+ }
+ vs[i].verb = s;
+ while (*s && (*s != ' ') && (*s != '\001')) {
+ s++;
+ }
+ if (*s == 0) {
+ vs[i].verb = NULL;
+ break;
+ }
+ if (*s == '\001') {
+ *s = 0;
+ s++;
+ }
+ *s = 0;
+ s++;
+ while (*s == ' ') {
+ s++;
+ }
+ if (*s == '1') {
+ vs[i].sense = 1;
+ } else {
+ vs[i].sense = 0;
+ }
+ s++;
+ }
+ vs[i].verb = NULL;
+
+ workshop_frame_sensitivities(vs);
+
+ free(vs);
+}
+
+/*
+ * Options code
+ */
+/* Set an editor option.
+ * IGNORE an option if you do not recognize it.
+ */
+void workshop_set_option_first(char *name, char *value)
+{
+ /* Currently value can only be on/off. This may change later (for
+ * example to set an option like "balloon evaluate delay", but
+ * for now just convert it into a boolean */
+ Boolean on = !strcmp(value, "on");
+
+ if (!strcmp(name, "workshopkeys")) {
+ workshop_hotkeys(on);
+ } else if (!strcmp(name, "savefiles")) {
+ save_files = on;
+ } else if (!strcmp(name, "balloon")) {
+ workshop_balloon_mode(on);
+ } else if (!strcmp(name, "balloondelay")) {
+ int delay = atoi(value);
+ /* Should I validate the number here?? */
+ workshop_balloon_delay(delay);
+ } else {
+ /* Let editor interpret it */
+ workshop_set_option(name, value);
+ }
+}
+
+
+
+/*
+ * Send information to eserve on certain editor events
+ * You must make sure these are called when necessary
+ */
+
+void workshop_file_closed(char *filename)
+{
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("deletedFile %s\n"), filename);
+ write(sd, buffer, strlen(buffer));
+}
+
+void workshop_file_closed_lineno(char *filename, int lineno)
+{
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("deletedFile %s %d\n"), filename, lineno);
+ write(sd, buffer, strlen(buffer));
+}
+
+void workshop_file_opened(char *filename, int readOnly)
+{
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("loadedFile %s %d\n"), filename, readOnly);
+ write(sd, buffer, strlen(buffer));
+}
+
+
+void workshop_file_saved(char *filename)
+{
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("savedFile %s\n"), filename);
+ write(sd, buffer, strlen(buffer));
+
+ /* Let editor report any moved marks that the eserve client
+ * should deal with (for example, moving location-based breakpoints) */
+ workshop_moved_marks(filename);
+}
+
+void workshop_move_mark(char *filename, int markId, int newLineno)
+{
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("moveMark %s %d %d\n"), filename, markId, newLineno);
+ write(sd, buffer, strlen(buffer));
+}
+
+void workshop_file_modified(char *filename)
+{
+ char buffer[2*MAXPATHLEN];
+ sprintf(buffer, NOCATGETS("modifiedFile %s\n"), filename);
+ write(sd, buffer, strlen(buffer));
+}
+
+void workshop_frame_moved(int new_x, int new_y, int new_w, int new_h)
+{
+ char buffer[200];
+
+ if (sd >= 0)
+ {
+ sprintf(buffer, NOCATGETS("frameAt %d %d %d %d\n"),
+ new_x, new_y, new_w, new_h);
+ write(sd, buffer, strlen(buffer));
+ }
+}
+
+/* A button in the toolbar has been pushed.
+ * Clientdata is a pointer used by the editor code to figure out the
+ * positions for this toolbar (probably by storing a window pointer,
+ * and then fetching the current buffer for that window and looking up
+ * cursor and selection positions etc.) */
+void workshop_perform_verb(char *verb, void *clientData)
+{
+ char *filename;
+ int curLine;
+ int curCol;
+ int selStartLine;
+ int selStartCol;
+ int selEndLine;
+ int selEndCol;
+ int selLength;
+ char *selection;
+
+ char buf[2*MAXPATHLEN];
+/* Later: needsFilePos indicates whether or not we need to fetch all this
+ * info for this verb... for now, however, it looks as if
+ * eserve parsing routines depend on it always being present */
+
+ if (workshop_get_positions(clientData,
+ &filename,
+ &curLine,
+ &curCol,
+ &selStartLine,
+ &selStartCol,
+ &selEndLine,
+ &selEndCol,
+ &selLength,
+ &selection)) {
+ if (selection == NULL) {
+ selection = NOCATGETS("");
+ }
+
+ /* Should I save the files??? This is currently done by checking
+ if the verb is one of a few recognized ones. Later we can pass
+ this list from eserve to the editor (it's currently hardcoded in
+ vi and emacs as well). */
+ if (save_files) {
+ if (!strcmp(verb, "build.build") || !strcmp(verb, "build.build-file") ||
+ !strcmp(verb, "debug.fix") || !strcmp(verb, "debug.fix-all")) {
+ workshop_save_files();
+ }
+ }
+
+ sprintf(buf, NOCATGETS("toolVerb %s %s %d,%d %d,%d %d,%d %d %s\n"),
+ verb,
+ filename,
+ curLine, curCol,
+ selStartLine, selStartCol,
+ selEndLine, selEndCol,
+ selLength,
+ selection);
+ write(sd, buf, strlen(buf));
+ if (*selection) {
+ free(selection);
+ }
+ }
+}
+
+/* Send a message to eserve */
+void workshop_send_message(char *buf)
+{
+ write(sd, buf, strlen(buf));
+}
+
+/* Some methods, like currentFile, cursorPos, etc. are missing here.
+ * But it looks like these are used for NoHands testing only so we
+ * won't bother requiring editors to implement these
+ */
+
+
+#ifdef DEBUG
+
+void
+pldebug(
+ char *fmt, /* a printf style format line */
+ ...)
+{
+ va_list ap;
+
+ if (dfd != NULL) {
+ va_start(ap, fmt);
+ vfprintf(dfd, fmt, ap);
+ va_end(ap);
+ fflush(dfd);
+ }
+
+} /* end pldebug */
+
+#endif