blob: 8de684c073e0adaae78d5fe542cfea0eda78f463 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sw=8:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 * Visual Workshop integration by Gordon Prieur
5 *
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
9 */
10
11/*
12 * Integration with Sun Workshop.
13 *
14 * This file should not change much, it's also used by other editors that
15 * connect to Workshop. Consider changing workshop.c instead.
16 */
17/*
18-> consider using MakeSelectionVisible instead of gotoLine hacks
19 to show the line properly
20 -> consider using glue instead of our own message wrapping functions
21 (but can only use glue if we don't have to distribute source)
22*/
23
24#include "vim.h"
25
26#include <stdio.h>
27#include <stdlib.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000028
29#ifdef INET_SOCKETS
30#include <netdb.h>
31#include <netinet/in.h>
32#else
33#include <sys/un.h>
34#endif
35
36#include <errno.h>
37#include <sys/types.h>
38#include <sys/socket.h>
39#include <sys/param.h>
40#ifdef HAVE_LIBGEN_H
41# include <libgen.h>
42#endif
43#include <unistd.h>
44#include <string.h>
45
46#include <X11/Intrinsic.h>
47#include <Xm/Xm.h>
48#include <Xm/AtomMgr.h>
49#include <Xm/PushB.h>
50
51#ifdef HAVE_X11_XPM_H
52# include <X11/xpm.h>
53#else
54# ifdef HAVE_XM_XPMP_H
55# include <Xm/XpmP.h>
56# endif
57#endif
58
59#ifdef HAVE_UTIL_DEBUG_H
60# include <util/debug.h>
61#endif
62#ifdef HAVE_UTIL_MSGI18N_H
63# include <util/msgi18n.h>
64#endif
65
66#include "integration.h" /* <EditPlugin/integration.h> */
67#ifdef HAVE_FRAME_H
68# include <frame.h>
69#endif
70
71#ifndef MAX
72# define MAX(a, b) (a) > (b) ? (a) : (b)
73#endif
74
75#ifndef NOCATGETS
76# define NOCATGETS(x) x
77#endif
78
79/* Functions private to this file */
80static void workshop_connection_closed(void);
Bram Moolenaarba07ce32010-01-06 18:25:34 +010081static void messageFromEserve(XtPointer clientData, int *dum1, XtInputId *dum2);
Bram Moolenaar071d4272004-06-13 20:20:40 +000082static void workshop_disconnect(void);
83static void workshop_sensitivity(int num, char *table);
84static void adjust_sign_name(char *filename);
85static void process_menuItem(char *);
86static void process_toolbarButton(char *);
87static void workshop_set_option_first(char *name, char *value);
88
Bram Moolenaar860cae12010-06-05 23:22:07 +020089static size_t dummy; /* to ignore return value of write() */
Bram Moolenaar071d4272004-06-13 20:20:40 +000090
91#define CMDBUFSIZ 2048
92
93#ifdef DEBUG
94static FILE *dfd;
95static void pldebug(char *, ...);
96static void unrecognised_message(char *);
97
98#define HANDLE_ERRORS(cmd) else unrecognised_message(cmd);
99#else
100#define HANDLE_ERRORS(cmd)
101#endif
102
103/*
104 * Version number of the protocol between an editor and eserve.
105 * This number should be incremented when the protocol
106 * is changed.
107 */
108#define PROTOCOL_VERSION "4.0.0"
109
110static int sd = -1;
111static XtInputId inputHandler; /* Cookie for input */
112
113Boolean save_files = True; /* When true, save all files before build actions */
114
115void
116workshop_connection_closed(void)
117{
118 /*
119 * socket closed on other end
120 */
121 XtRemoveInput(inputHandler);
122 inputHandler = 0;
123 sd = -1;
124}
125
126 static char *
127getCommand(void)
128{
129 int len; /* length of this command */
130 char lenbuf[7]; /* get the length string here */
131 char *newcb; /* used to realloc cmdbuf */
132 static char *cmdbuf;/* get the command string here */
133 static int cbsize;/* size of cmdbuf */
134
135 if ((len = read(sd, &lenbuf, 6)) == 6) {
136 lenbuf[6] = 0; /* Terminate buffer such that atoi() works right */
137 len = atoi(lenbuf);
138 if (cbsize < (len + 1)) {
139 newcb = (char *) realloc(cmdbuf,
140 MAX((len + 256), CMDBUFSIZ));
141 if (newcb != NULL) {
142 cmdbuf = newcb;
143 cbsize = MAX((len + 256), CMDBUFSIZ);
144 }
145 }
146 if (cbsize >= len && (len = read(sd, cmdbuf, len)) > 0) {
147 cmdbuf[len] = 0;
148 return cmdbuf;
149 } else {
150 return NULL;
151 }
152 } else {
153 if (len == 0) { /* EOF */
154 workshop_connection_closed();
155 }
156 return NULL;
157 }
158
159}
160
Bram Moolenaar071d4272004-06-13 20:20:40 +0000161void
Bram Moolenaarba07ce32010-01-06 18:25:34 +0100162messageFromEserve(XtPointer clientData UNUSED,
163 int *dum1 UNUSED,
164 XtInputId *dum2 UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000165{
166 char *cmd; /* the 1st word of the command */
167
168 cmd = getCommand();
169 if (cmd == NULL) {
170 /* We're being shut down by eserve and the "quit" message
171 * didn't arrive before the socket connection got closed */
172 return;
173 }
174#ifdef DEBUG
175 pldebug("%s\n", cmd);
176#endif
177 switch (*cmd) {
178 case 'a':
179 if (cmd[1] == 'c' &&
180 strncmp(cmd, NOCATGETS("ack "), 4) == 0) {
181 int ackNum;
182 char buf[20];
183
184 ackNum = atoi(&cmd[4]);
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000185 vim_snprintf(buf, sizeof(buf),
186 NOCATGETS("ack %d\n"), ackNum);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200187 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000188 } else if (strncmp(cmd,
189 NOCATGETS("addMarkType "), 12) == 0) {
190 int idx;
191 char *color;
192 char *sign;
193
194 idx = atoi(strtok(&cmd[12], " "));
195 color = strtok(NULL, NOCATGETS("\001"));
196 sign = strtok(NULL, NOCATGETS("\001"));
197 /* Skip space that separates names */
198 if (color) {
199 color++;
200 }
201 if (sign) {
202 sign++;
203 }
Bram Moolenaarba07ce32010-01-06 18:25:34 +0100204 /* Change sign name to accommodate a different size? */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000205 adjust_sign_name(sign);
206 workshop_add_mark_type(idx, color, sign);
207 }
208 HANDLE_ERRORS(cmd);
209 break;
210
211 case 'b':
212 if (strncmp(cmd,
213 NOCATGETS("balloon "), 8) == 0) {
214 char *tip;
215
216 tip = strtok(&cmd[8], NOCATGETS("\001"));
217 workshop_show_balloon_tip(tip);
218 }
219 HANDLE_ERRORS(cmd);
220 break;
221
222 case 'c':
223 if (strncmp(cmd,
224 NOCATGETS("changeMarkType "), 15) == 0) {
225 char *file;
226 int markId;
227 int type;
228
229 file = strtok(&cmd[15], " ");
230 markId = atoi(strtok(NULL, " "));
231 type = atoi(strtok(NULL, " "));
232 workshop_change_mark_type(file, markId, type);
233 }
234 HANDLE_ERRORS(cmd);
235 break;
236
237 case 'd':
238 if (strncmp(cmd, NOCATGETS("deleteMark "), 11) == 0) {
239 char *file;
240 int markId;
241
242 file = strtok(&cmd[11], " ");
243 markId = atoi(strtok(NULL, " "));
244 workshop_delete_mark(file, markId);
245 }
246 HANDLE_ERRORS(cmd);
247 break;
248
249 case 'f':
250 if (cmd[1] == 'o' &&
251 strncmp(cmd, NOCATGETS("footerMsg "), 10) == 0) {
252 int severity;
253 char *message;
254
255 severity =
256 atoi(strtok(&cmd[10], " "));
257 message = strtok(NULL, NOCATGETS("\001"));
258
259 workshop_footer_message(message, severity);
260 } else if (strncmp(cmd,
261 NOCATGETS("frontFile "), 10) == 0) {
262 char *file;
263
264 file = strtok(&cmd[10], " ");
265 workshop_front_file(file);
266 }
267 HANDLE_ERRORS(cmd);
268 break;
269
270 case 'g':
271 if (cmd[1] == 'e' &&
272 strncmp(cmd, NOCATGETS("getMarkLine "), 12) == 0) {
273 char *file;
274 int markid;
275 int line;
276 char buf[100];
277
278 file = strtok(&cmd[12], " ");
279 markid = atoi(strtok(NULL, " "));
280 line = workshop_get_mark_lineno(file, markid);
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000281 vim_snprintf(buf, sizeof(buf),
282 NOCATGETS("markLine %s %d %d\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283 file, markid, line);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200284 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000285 } else if (cmd[1] == 'o' && cmd[4] == 'L' &&
286 strncmp(cmd, NOCATGETS("gotoLine "), 9) == 0) {
287 char *file;
288 int lineno;
289
290 file = strtok(&cmd[9], " ");
291 lineno = atoi(strtok(NULL, " "));
292 workshop_goto_line(file, lineno);
293 } else if (strncmp(cmd,
294 NOCATGETS("gotoMark "), 9) == 0) {
295 char *file;
296 int markId;
297 char *message;
298
299 file = strtok(&cmd[9], " ");
300 markId = atoi(strtok(NULL, " "));
301 message = strtok(NULL, NOCATGETS("\001"));
302 workshop_goto_mark(file, markId, message);
303#ifdef NOHANDS_SUPPORT_FUNCTIONS
304 } else if (strcmp(cmd, NOCATGETS("getCurrentFile")) == 0) {
305 char *f = workshop_test_getcurrentfile();
306 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000307 vim_snprintf(buffer, sizeof(buffer),
308 NOCATGETS("currentFile %d %s"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309 f ? strlen(f) : 0, f ? f : "");
310 workshop_send_message(buffer);
311 } else if (strcmp(cmd, NOCATGETS("getCursorRow")) == 0) {
312 int row = workshop_test_getcursorrow();
313 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000314 vim_snprintf(buffer, sizeof(buffer),
315 NOCATGETS("cursorRow %d"), row);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000316 workshop_send_message(buffer);
317 } else if (strcmp(cmd, NOCATGETS("getCursorCol")) == 0) {
318 int col = workshop_test_getcursorcol();
319 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000320 vim_snprintf(buffer, sizeof(buffer),
321 NOCATGETS("cursorCol %d"), col);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000322 workshop_send_message(buffer);
323 } else if (strcmp(cmd, NOCATGETS("getCursorRowText")) == 0) {
324 char *t = workshop_test_getcursorrowtext();
325 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000326 vim_snprintf(buffer, sizeof(buffer),
327 NOCATGETS("cursorRowText %d %s"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328 t ? strlen(t) : 0, t ? t : "");
329 workshop_send_message(buffer);
330 } else if (strcmp(cmd, NOCATGETS("getSelectedText")) == 0) {
331 char *t = workshop_test_getselectedtext();
332 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000333 vim_snprintf(buffer, sizeof(buffer),
334 NOCATGETS("selectedText %d %s"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000335 t ? strlen(t) : 0, t ? t : "");
336 workshop_send_message(buffer);
337#endif
338 }
339 HANDLE_ERRORS(cmd);
340 break;
341
342 case 'l':
343 if (strncmp(cmd, NOCATGETS("loadFile "), 9) == 0) {
344 char *file;
345 int line;
346 char *frameid;
347
348 file = strtok(&cmd[9], " ");
349 line = atoi(strtok(NULL, " "));
350 frameid = strtok(NULL, " ");
351 workshop_load_file(file, line, frameid);
352 }
353 HANDLE_ERRORS(cmd);
354 break;
355
356 case 'm': /* Menu, minimize, maximize */
357 if (cmd[1] == 'e' && cmd[4] == 'B' &&
358 strncmp(cmd, NOCATGETS("menuBegin "), 10) == 0) {
359 workshop_menu_begin(&cmd[10]);
360 } else if (cmd[1] == 'e' && cmd[4] == 'I' &&
361 strncmp(cmd, NOCATGETS("menuItem "), 9) == 0) {
362 process_menuItem(cmd);
363 } else if (cmd[1] == 'e' && cmd[4] == 'E' &&
364 strcmp(cmd, NOCATGETS("menuEnd")) == 0) {
365 workshop_menu_end();
366 } else if (cmd[1] == 'a' &&
367 strcmp(cmd, NOCATGETS("maximize")) == 0) {
368 workshop_maximize();
369 } else if (strcmp(cmd, NOCATGETS("minimize")) == 0) {
370 workshop_minimize();
371 }
372 HANDLE_ERRORS(cmd);
373 break;
374
375 case 'o':
376 if (cmd[1] == 'p' &&
377 strcmp(cmd, NOCATGETS("option"))) {
378 char *name;
379 char *value;
380
381 name = strtok(&cmd[7], " ");
382 value = strtok(NULL, " ");
383 workshop_set_option_first(name, value);
384 }
385 HANDLE_ERRORS(cmd);
386 break;
387
388 case 'p':
389 if (strcmp(cmd, NOCATGETS("ping")) == 0) {
390#if 0
391 int pingNum;
392
393 pingNum = atoi(&cmd[5]);
394 workshop_send_ack(ackNum);
Bram Moolenaar09092152010-08-08 16:38:42 +0200395 /* WHAT DO I DO HERE? */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396#endif
397 }
398 HANDLE_ERRORS(cmd);
399 break;
400
401 case 'q':
402 if (strncmp(cmd, NOCATGETS("quit"), 4) == 0) {
403
404 /* Close the connection. It's important to do
405 * that now, since workshop_quit might be
406 * looking at open files. For example, if you
407 * have modified one of the files without
408 * saving, NEdit will ask you what you want to
409 * do, and spin loop by calling
410 * XtAppProcessEvent while waiting for your
411 * reply. In this case, if we still have an
412 * input handler and the socket has been
413 * closed on the other side when eserve
414 * expired, we will hang in IoWait.
415 */
416 workshop_disconnect();
417
418 workshop_quit();
419 }
420 HANDLE_ERRORS(cmd);
421 break;
422
423 case 'r':
424 if (cmd[1] == 'e' &&
425 strncmp(cmd, NOCATGETS("reloadFile "), 11) == 0) {
426 char *file;
427 int line;
428
429 file = strtok(&cmd[11], " ");
430 line = atoi(strtok(NULL, " "));
431 workshop_reload_file(file, line);
432 }
433 HANDLE_ERRORS(cmd);
434 break;
435
436 case 's':
437 if (cmd[1] == 'e' && cmd[2] == 't' &&
438 strncmp(cmd, NOCATGETS("setMark "), 8) == 0) {
439 char *file;
440 int line;
441 int markId;
442 int type;
443
444 file = strtok(&cmd[8], " ");
445 line = atoi(strtok(NULL, " "));
446 markId = atoi(strtok(NULL, " "));
447 type = atoi(strtok(NULL, " "));
448 workshop_set_mark(file, line, markId, type);
449 } else if (cmd[1] == 'h' &&
450 strncmp(cmd, NOCATGETS("showFile "), 9) == 0) {
451 workshop_show_file(&cmd[9]);
452 } else if (cmd[1] == 'u' &&
453 strncmp(cmd, NOCATGETS("subMenu "), 8) == 0) {
454 char *label;
455
456 label = strtok(&cmd[8], NOCATGETS("\001"));
457 workshop_submenu_begin(label);
458 } else if (cmd[1] == 'u' &&
459 strcmp(cmd, NOCATGETS("subMenuEnd")) == 0) {
460 workshop_submenu_end();
461 } else if (cmd[1] == 'e' && cmd[2] == 'n' &&
462 strncmp(cmd, NOCATGETS("sensitivity "), 12) == 0) {
463 int num;
464 char *bracket;
465 char *table;
466
467 num = atoi(strtok(&cmd[12], " "));
468 bracket = strtok(NULL, " ");
469 if (*bracket != '[') {
470 fprintf(stderr, NOCATGETS("Parsing "
471 "error for sensitivity\n"));
472 } else {
473 table = strtok(NULL, NOCATGETS("]"));
474 workshop_sensitivity(num, table);
475 }
476 } else if (cmd[1] == 'e' && cmd[2] == 'n' && cmd[3] == 'd' &&
477 strncmp(cmd, NOCATGETS("sendVerb "), 9) == 0) {
478 /* Send the given verb back (used for the
479 * debug.lineno callback (such that other tools
480 * can obtain the position coordinates or the
481 * selection) */
482 char *verb;
483
484 verb = strtok(&cmd[9], " ");
485 workshop_perform_verb(verb, NULL);
486 } else if (cmd[1] == 'a' &&
487 strncmp(cmd, NOCATGETS("saveFile "), 9) == 0) {
488 workshop_save_file(&cmd[9]);
489#ifdef NOHANDS_SUPPORT_FUNCTIONS
490 } else if (strncmp(cmd, NOCATGETS("saveSensitivity "), 16) == 0) {
491 char *file;
492
493 file = strtok(&cmd[16], " ");
494 workshop_save_sensitivity(file);
495#endif
496 }
497 HANDLE_ERRORS(cmd);
498 break;
499
500 case 't': /* Toolbar */
501 if (cmd[8] == 'e' &&
502 strncmp(cmd, NOCATGETS("toolbarBegin"), 12) == 0) {
503 workshop_toolbar_begin();
504 } else if (cmd[8] == 'u' &&
505 strncmp(cmd, NOCATGETS("toolbarButton"), 13) == 0) {
506 process_toolbarButton(cmd);
507 } else if (cmd[7] == 'E' &&
508 strcmp(cmd, NOCATGETS("toolbarEnd")) == 0) {
509 workshop_toolbar_end();
510 }
511 HANDLE_ERRORS(cmd);
512 break;
513
514#ifdef DEBUG
515 default:
516 unrecognised_message(cmd);
517 break;
518#endif
519 }
520}
521
522static void
523process_menuItem(
524 char *cmd)
525{
526 char *label = strtok(&cmd[9], NOCATGETS("\001"));
527 char *verb = strtok(NULL, NOCATGETS("\001"));
528 char *acc = strtok(NULL, NOCATGETS("\001"));
529 char *accText = strtok(NULL, NOCATGETS("\001"));
530 char *name = strtok(NULL, NOCATGETS("\001"));
531 char *sense = strtok(NULL, NOCATGETS("\n"));
532 char *filepos = strtok(NULL, NOCATGETS("\n"));
533 if (*acc == '-') {
534 acc = NULL;
535 }
536 if (*accText == '-') {
537 accText = NULL;
538 }
539 workshop_menu_item(label, verb, acc, accText, name, filepos, sense);
540
541}
542
543
544static void
545process_toolbarButton(
546 char *cmd) /* button definition */
547{
548 char *label = strtok(&cmd[14], NOCATGETS("\001"));
549 char *verb = strtok(NULL, NOCATGETS("\001"));
550 char *senseVerb = strtok(NULL, NOCATGETS("\001"));
551 char *filepos = strtok(NULL, NOCATGETS("\001"));
552 char *help = strtok(NULL, NOCATGETS("\001"));
553 char *sense = strtok(NULL, NOCATGETS("\001"));
554 char *file = strtok(NULL, NOCATGETS("\001"));
555 char *left = strtok(NULL, NOCATGETS("\n"));
556
557 if (!strcmp(label, NOCATGETS("-"))) {
558 label = NULL;
559 }
560 if (!strcmp(help, NOCATGETS("-"))) {
561 help = NULL;
562 }
563 if (!strcmp(file, NOCATGETS("-"))) {
564 file = NULL;
565 }
566 if (!strcmp(senseVerb, NOCATGETS("-"))) {
567 senseVerb = NULL;
568 }
569 workshop_toolbar_button(label, verb, senseVerb, filepos, help,
570 sense, file, left);
571}
572
573
574#ifdef DEBUG
575void
576unrecognised_message(
577 char *cmd)
578{
579 pldebug("Unrecognised eserve message:\n\t%s\n", cmd);
580 /* abort(); */
581}
582#endif
583
584
Bram Moolenaarba07ce32010-01-06 18:25:34 +0100585/* Change sign name to accommodate a different size:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000586 * Create the filename based on the height. The filename format
587 * of multisize icons are:
588 * x.xpm : largest icon
589 * x1.xpm : smaller icon
590 * x2.xpm : smallest icon */
591 void
592adjust_sign_name(char *filename)
593{
594 char *s;
595 static int fontSize = -1;
596
597 if (fontSize == -1)
598 fontSize = workshop_get_font_height();
599 if (fontSize == 0)
600 return;
601 if (filename[0] == '-')
602 return;
603
604 /* This is ugly: later we should instead pass the fontheight over
605 * to eserve on startup and let eserve just send the right filenames
606 * to us in the first place
607
608 * I know that the filename will end with 1.xpm (see
609 * GuiEditor.cc`LispPrintSign if you wonder why) */
610 s = filename+strlen(filename)-5;
611 if (fontSize <= 11)
612 strcpy(s, "2.xpm");
613 else if (fontSize <= 15)
614 strcpy(s, "1.xpm");
615 else
616 strcpy(s, ".xpm");
617}
618
Bram Moolenaarba07ce32010-01-06 18:25:34 +0100619#if 0
Bram Moolenaar071d4272004-06-13 20:20:40 +0000620/* Were we invoked by WorkShop? This function can be used early during startup
621 if you want to do things differently if the editor is started standalone
622 or in WorkShop mode. For example, in standalone mode you may not want to
623 add a footer/message area or a sign gutter. */
624int
625workshop_invoked()
626{
627 static int result = -1;
628 if (result == -1) {
629 result = (getenv(NOCATGETS("SPRO_EDITOR_SOCKET")) != NULL);
630 }
631 return result;
632}
Bram Moolenaarba07ce32010-01-06 18:25:34 +0100633#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000634
635/* Connect back to eserve */
636void workshop_connect(XtAppContext context)
637{
638#ifdef INET_SOCKETS
639 struct sockaddr_in server;
640 struct hostent * host;
641 int port;
642#else
643 struct sockaddr_un server;
644#endif
645 char buf[32];
646 char * address;
647#ifdef DEBUG
648 char *file;
649#endif
650
651 address = getenv(NOCATGETS("SPRO_EDITOR_SOCKET"));
652 if (address == NULL) {
653 return;
654 }
655
656#ifdef INET_SOCKETS
657 port = atoi(address);
658
659 if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
660 PERROR(NOCATGETS("workshop_connect"));
661 return;
662 }
663
664 /* Get the server internet address and put into addr structure */
665 /* fill in the socket address structure and connect to server */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200666 vim_memset((char *)&server, '\0', sizeof(server));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000667 server.sin_family = AF_INET;
668 server.sin_port = port;
669 if ((host = gethostbyname(NOCATGETS("localhost"))) == NULL) {
670 PERROR(NOCATGETS("gethostbyname"));
671 sd = -1;
672 return;
673 }
674 memcpy((char *)&server.sin_addr, host->h_addr, host->h_length);
675#else
676 if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
677 PERROR(NOCATGETS("workshop_connect"));
678 return;
679 }
680
681 server.sun_family = AF_UNIX;
682 strcpy(server.sun_path, address);
683#endif
684 /* Connect to server */
685 if (connect(sd, (struct sockaddr *)&server, sizeof(server))) {
686 if (errno == ECONNREFUSED) {
687 close(sd);
688#ifdef INET_SOCKETS
689 if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
690 PERROR(NOCATGETS("workshop_connect"));
691 return;
692 }
693#else
694 if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
695 PERROR(NOCATGETS("workshop_connect"));
696 return;
697 }
698#endif
699 if (connect(sd, (struct sockaddr *)&server,
700 sizeof(server))) {
701 PERROR(NOCATGETS("workshop_connect"));
702 return;
703 }
704
705 } else {
706 PERROR(NOCATGETS("workshop_connect"));
707 return;
708 }
709 }
710
711 /* tell notifier we are interested in being called
712 * when there is input on the editor connection socket
713 */
714 inputHandler = XtAppAddInput(context, sd, (XtPointer) XtInputReadMask,
715 messageFromEserve, NULL);
716#ifdef DEBUG
717 if ((file = getenv(NOCATGETS("SPRO_PLUGIN_DEBUG"))) != NULL) {
718 char buf[BUFSIZ];
719
720 unlink(file);
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000721 vim_snprintf(buf, sizeof(buf), "date > %s", file);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000722 system(buf);
723 dfd = fopen(file, "a");
724 } else {
725 dfd = NULL;
726 }
727#endif
728
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000729 vim_snprintf(buf, sizeof(buf), NOCATGETS("connected %s %s %s\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730 workshop_get_editor_name(),
731 PROTOCOL_VERSION,
732 workshop_get_editor_version());
Bram Moolenaar860cae12010-06-05 23:22:07 +0200733 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000734
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000735 vim_snprintf(buf, sizeof(buf), NOCATGETS("ack 1\n"));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200736 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000737}
738
739void workshop_disconnect()
740{
741 /* Probably need to send some message here */
742
743 /*
744 * socket closed on other end
745 */
746 XtRemoveInput(inputHandler);
747 close(sd);
748 inputHandler = 0;
749 sd = -1;
750
751}
752
753/*
754 * Utility functions
755 */
756
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757
758/* Minimize and maximize shells. From libutil's shell.cc. */
759
760/* utility functions from libutil's shell.cc */
761static Boolean
762isWindowMapped(Display *display, Window win)
763{
764 XWindowAttributes winAttrs;
765 XGetWindowAttributes(display,
766 win,
767 &winAttrs);
768 if (winAttrs.map_state == IsViewable) {
769 return(True);
770 } else {
771 return(False);
772 }
773}
774
775static Boolean
776isMapped(Widget widget)
777{
778 if (widget == NULL) {
779 return(False);
780 }
781
782 if (XtIsRealized(widget) == False) {
783 return(False);
784 }
785
786 return(isWindowMapped(XtDisplay(widget), XtWindow(widget)));
787}
788
789static Boolean
790widgetIsIconified(
791 Widget w)
792{
793 Atom wm_state;
794 Atom act_type; /* actual Atom type returned */
795 int act_fmt; /* actual format returned */
796 u_long nitems_ret; /* number of items returned */
797 u_long bytes_after; /* number of bytes remaining */
798 u_long *property; /* actual property returned */
799
800 /*
801 * If a window is iconified its WM_STATE is set to IconicState. See
802 * ICCCM Version 2.0, section 4.1.3.1 for more details.
803 */
804
805 wm_state = XmInternAtom(XtDisplay(w), NOCATGETS("WM_STATE"), False);
806 if (XtWindow(w) != 0) { /* only check if window exists! */
807 XGetWindowProperty(XtDisplay(w), XtWindow(w), wm_state, 0L, 2L,
808 False, AnyPropertyType, &act_type, &act_fmt, &nitems_ret,
809 &bytes_after, (u_char **) &property);
810 if (nitems_ret == 2 && property[0] == IconicState) {
811 return True;
812 }
813 }
814
815 return False;
816
817} /* end widgetIsIconified */
818
819void
820workshop_minimize_shell(Widget shell)
821{
822 if (shell != NULL &&
823 XtIsObject(shell) &&
824 XtIsRealized(shell) == True) {
825 if (isMapped(shell) == True) {
826 XIconifyWindow(XtDisplay(shell), XtWindow(shell),
827 XScreenNumberOfScreen(XtScreen(shell)));
828 }
829 XtVaSetValues(shell,
830 XmNiconic, True,
831 NULL);
832 }
833}
834
835void workshop_maximize_shell(Widget shell)
836{
837 if (shell != NULL &&
838 XtIsRealized(shell) == True &&
839 widgetIsIconified(shell) == True &&
840 isMapped(shell) == False) {
841 XtMapWidget(shell);
842 /* This used to be
843 XtPopdown(shell);
844 XtPopup(shell, XtGrabNone);
845 However, I found that that would drop any transient
846 windows that had been iconified with the window.
847 According to the ICCCM, XtMapWidget should be used
848 to bring a window from Iconic to Normal state.
849 However, Rich Mauri did a lot of work on this during
850 Bart, and found that XtPopDown,XtPopup was required
851 to fix several bugs involving multiple CDE workspaces.
852 I've tested it now and things seem to work fine but
853 I'm leaving this note for history in case this needs
854 to be revisited.
855 */
856 }
857}
858
859
860Boolean workshop_get_width_height(int *width, int *height)
861{
862 static int wid = 0;
863 static int hgt = 0;
864 static Boolean firstTime = True;
865 static Boolean success = False;
866
867 if (firstTime) {
868 char *settings;
869
870 settings = getenv(NOCATGETS("SPRO_GUI_WIDTH_HEIGHT"));
871 if (settings != NULL) {
872 wid = atoi(settings);
873 settings = strrchr(settings, ':');
874 if (settings++ != NULL) {
875 hgt = atoi(settings);
876 }
877 if (wid > 0 && hgt > 0) {
878 success = True;
879 }
880 firstTime = False;
881 }
882 }
883
884 if (success) {
885 *width = wid;
886 *height = hgt;
887 }
888 return success;
889}
890
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891/*
892 * Toolbar code
893 */
894
895void workshop_sensitivity(int num, char *table)
896{
897 /* build up a verb table */
898 VerbSense *vs;
899 int i;
900 char *s;
901 if ((num < 1) || (num > 500)) {
902 return;
903 }
904
905 vs = (VerbSense *)malloc((num+1)*sizeof(VerbSense));
906
907 /* Point to the individual names (destroys the table string, but
908 * that's okay -- this is more efficient than duplicating strings) */
909 s = table;
910 for (i = 0; i < num; i++) {
911 while (*s == ' ') {
912 s++;
913 }
914 vs[i].verb = s;
915 while (*s && (*s != ' ') && (*s != '\001')) {
916 s++;
917 }
918 if (*s == 0) {
919 vs[i].verb = NULL;
920 break;
921 }
922 if (*s == '\001') {
923 *s = 0;
924 s++;
925 }
926 *s = 0;
927 s++;
928 while (*s == ' ') {
929 s++;
930 }
931 if (*s == '1') {
932 vs[i].sense = 1;
933 } else {
934 vs[i].sense = 0;
935 }
936 s++;
937 }
938 vs[i].verb = NULL;
939
940 workshop_frame_sensitivities(vs);
941
942 free(vs);
943}
944
945/*
946 * Options code
947 */
948/* Set an editor option.
949 * IGNORE an option if you do not recognize it.
950 */
951void workshop_set_option_first(char *name, char *value)
952{
953 /* Currently value can only be on/off. This may change later (for
954 * example to set an option like "balloon evaluate delay", but
955 * for now just convert it into a boolean */
956 Boolean on = !strcmp(value, "on");
957
958 if (!strcmp(name, "workshopkeys")) {
959 workshop_hotkeys(on);
960 } else if (!strcmp(name, "savefiles")) {
961 save_files = on;
962 } else if (!strcmp(name, "balloon")) {
963 workshop_balloon_mode(on);
964 } else if (!strcmp(name, "balloondelay")) {
965 int delay = atoi(value);
966 /* Should I validate the number here?? */
967 workshop_balloon_delay(delay);
968 } else {
969 /* Let editor interpret it */
970 workshop_set_option(name, value);
971 }
972}
973
974
Bram Moolenaar071d4272004-06-13 20:20:40 +0000975void workshop_file_closed_lineno(char *filename, int lineno)
976{
977 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000978 vim_snprintf(buffer, sizeof(buffer),
979 NOCATGETS("deletedFile %s %d\n"), filename, lineno);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200980 dummy = write(sd, buffer, strlen(buffer));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000981}
982
983void workshop_file_opened(char *filename, int readOnly)
984{
985 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000986 vim_snprintf(buffer, sizeof(buffer),
987 NOCATGETS("loadedFile %s %d\n"), filename, readOnly);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200988 dummy = write(sd, buffer, strlen(buffer));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000989}
990
991
992void workshop_file_saved(char *filename)
993{
994 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000995 vim_snprintf(buffer, sizeof(buffer),
996 NOCATGETS("savedFile %s\n"), filename);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200997 dummy = write(sd, buffer, strlen(buffer));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000998
999 /* Let editor report any moved marks that the eserve client
1000 * should deal with (for example, moving location-based breakpoints) */
1001 workshop_moved_marks(filename);
1002}
1003
Bram Moolenaar071d4272004-06-13 20:20:40 +00001004void workshop_frame_moved(int new_x, int new_y, int new_w, int new_h)
1005{
1006 char buffer[200];
1007
1008 if (sd >= 0)
1009 {
Bram Moolenaar9c13b352005-05-19 20:53:52 +00001010 vim_snprintf(buffer, sizeof(buffer),
1011 NOCATGETS("frameAt %d %d %d %d\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001012 new_x, new_y, new_w, new_h);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001013 dummy = write(sd, buffer, strlen(buffer));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001014 }
1015}
1016
1017/* A button in the toolbar has been pushed.
1018 * Clientdata is a pointer used by the editor code to figure out the
1019 * positions for this toolbar (probably by storing a window pointer,
1020 * and then fetching the current buffer for that window and looking up
1021 * cursor and selection positions etc.) */
1022void workshop_perform_verb(char *verb, void *clientData)
1023{
1024 char *filename;
1025 int curLine;
1026 int curCol;
1027 int selStartLine;
1028 int selStartCol;
1029 int selEndLine;
1030 int selEndCol;
1031 int selLength;
1032 char *selection;
1033
1034 char buf[2*MAXPATHLEN];
1035/* Later: needsFilePos indicates whether or not we need to fetch all this
1036 * info for this verb... for now, however, it looks as if
1037 * eserve parsing routines depend on it always being present */
1038
1039 if (workshop_get_positions(clientData,
1040 &filename,
1041 &curLine,
1042 &curCol,
1043 &selStartLine,
1044 &selStartCol,
1045 &selEndLine,
1046 &selEndCol,
1047 &selLength,
1048 &selection)) {
1049 if (selection == NULL) {
1050 selection = NOCATGETS("");
1051 }
1052
1053 /* Should I save the files??? This is currently done by checking
1054 if the verb is one of a few recognized ones. Later we can pass
1055 this list from eserve to the editor (it's currently hardcoded in
1056 vi and emacs as well). */
1057 if (save_files) {
1058 if (!strcmp(verb, "build.build") || !strcmp(verb, "build.build-file") ||
1059 !strcmp(verb, "debug.fix") || !strcmp(verb, "debug.fix-all")) {
1060 workshop_save_files();
1061 }
1062 }
1063
Bram Moolenaar9c13b352005-05-19 20:53:52 +00001064 vim_snprintf(buf, sizeof(buf),
1065 NOCATGETS("toolVerb %s %s %d,%d %d,%d %d,%d %d %s\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066 verb,
1067 filename,
1068 curLine, curCol,
1069 selStartLine, selStartCol,
1070 selEndLine, selEndCol,
1071 selLength,
1072 selection);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001073 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001074 if (*selection) {
1075 free(selection);
1076 }
1077 }
1078}
1079
1080/* Send a message to eserve */
Bram Moolenaarba07ce32010-01-06 18:25:34 +01001081#if defined(NOHANDS_SUPPORT_FUNCTIONS) || defined(FEAT_BEVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001082void workshop_send_message(char *buf)
1083{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001084 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001085}
Bram Moolenaarba07ce32010-01-06 18:25:34 +01001086#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087
1088/* Some methods, like currentFile, cursorPos, etc. are missing here.
1089 * But it looks like these are used for NoHands testing only so we
1090 * won't bother requiring editors to implement these
1091 */
1092
1093
1094#ifdef DEBUG
1095
1096void
1097pldebug(
1098 char *fmt, /* a printf style format line */
1099 ...)
1100{
1101 va_list ap;
1102
1103 if (dfd != NULL) {
1104 va_start(ap, fmt);
1105 vfprintf(dfd, fmt, ap);
1106 va_end(ap);
1107 fflush(dfd);
1108 }
1109
1110} /* end pldebug */
1111
1112#endif