view src/workshop.c @ 4444:ccecb03e5e8b v7.3.970

updated for version 7.3.970 Problem: Syntax highlighting can be slow. Solution: Include the NFA regexp engine. Add the 'regexpengine' option to select which one is used. (various authors, including Ken Takata, Andrei Aiordachioaie, Russ Cox, Xiaozhou Liua, Ian Young)
author Bram Moolenaar <bram@vim.org>
date Sun, 19 May 2013 19:40:29 +0200
parents bfade53bcafb
children 33ba2adb6065
line wrap: on
line source

/* vi:set ts=8 sts=4 sw=4:
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
# include "auto/config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#ifdef HAVE_LIBGEN_H
# include <libgen.h>
#endif
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/PushB.h>

#include "integration.h"	/* <EditPlugin/integration.h> */

#include "vim.h"
#include "version.h"
#include "gui_beval.h"
#include "workshop.h"

void		 workshop_hotkeys(Boolean);

static Boolean	 isShowing(int);
static win_T	*get_window(buf_T *);
static void	 updatePriority(Boolean);
static char	*addUniqueMnemonic(char *, char *);
static char	*fixup(char *);
static char	*get_selection(buf_T *);
static char	*append_selection(int, char *, int *, int *);
static void	 load_buffer_by_name(char *, int);
static void	 load_window(char *, int lnum);
static void	 warp_to_pc(int);
#ifdef FEAT_BEVAL
void		 workshop_beval_cb(BalloonEval *, int);
static int	 computeIndex(int, char_u *, int);
#endif
static char	*fixAccelText(char *);
static void	 addMenu(char *, char *, char *);
static char	*lookupVerb(char *, int);
static void	 coloncmd(char *, Boolean);

extern Widget	 vimShell;
extern Widget	 textArea;
extern XtAppContext app_context;

static int	 tbpri;			/* ToolBar priority */
int		 usingSunWorkShop = 0;	/* set if -ws flag is used */
char		 curMenuName[BUFSIZ];
char		 curMenuPriority[BUFSIZ];

static Boolean	 workshopInitDone = False;
static Boolean	 workshopHotKeysEnabled = False;

/*
 * The following enum is from <gp_dbx/gp_dbx_common.h>. We can't include it
 * here because its C++.
 */
enum
{
    GPLineEval_EVALUATE,		/* evaluate expression */
    GPLineEval_INDIRECT,		/* evaluate *<expression> */
    GPLineEval_TYPE			/* type of expression */
};

/*
 * Store each verb in the MenuMap. This lets us map from a verb to a menu.
 * There may be multiple matches for a single verb in this table.
 */
#define MENU_INC	50		/* menuMap incremental size increases */
typedef struct
{
    char	*name;			/* name of the menu */
    char	*accel;			/* optional accelerator key */
    char	*verb;			/* menu verb */
} MenuMap;
static MenuMap	*menuMap;		/* list of verb/menu mappings */
static int	 menuMapSize;		/* current size of menuMap */
static int	 menuMapMax;		/* allocated size of menuMap */
static char	*initialFileCmd;	/* save command but defer doing it */


    void
workshop_init()
{
    char_u	 buf[64];
    int		 is_dirty = FALSE;
    int		 width, height;
    XtInputMask	 mask;

    /*
     * Turn on MenuBar, ToolBar, and Footer.
     */
    STRCPY(buf, p_go);
    if (vim_strchr(p_go, GO_MENUS) == NULL)
    {
	STRCAT(buf, "m");
	is_dirty = TRUE;
    }
    if (vim_strchr(p_go, GO_TOOLBAR) == NULL)
    {
	STRCAT(buf, "T");
	is_dirty = TRUE;
    }
    if (vim_strchr(p_go, GO_FOOTER) == NULL)
    {
	STRCAT(buf, "F");
	is_dirty = TRUE;
    }
    if (is_dirty)
	set_option_value((char_u *)"go", 0L, buf, 0);

    /*
     * Set size from workshop_get_width_height().
     */
    width = height = 0;
    if (workshop_get_width_height(&width, &height))
    {
	XtVaSetValues(vimShell,
		XmNwidth, width,
		XmNheight, height,
		NULL);
    }

    /*
     * Now read in the initial messages from eserve.
     */
    while ((mask = XtAppPending(app_context))
	    && (mask & XtIMAlternateInput) && !workshopInitDone)
	XtAppProcessEvent(app_context, (XtInputMask)XtIMAlternateInput);
}

    void
workshop_postinit()
{
    do_cmdline_cmd((char_u *)initialFileCmd);
    ALT_INPUT_LOCK_OFF;
    free(initialFileCmd);
    initialFileCmd = NULL;
}

    void
ex_wsverb(exarg_T *eap)
{
    msg_clr_cmdline();
    workshop_perform_verb((char *) eap->arg, NULL);
}

/*
 * Editor name
 * This string is recognized by eserve and should be all lower case.
 * This is how the editor detects that it is talking to gvim instead
 * of NEdit, for example, when the connection is initiated from the editor.
 */
    char *
workshop_get_editor_name()
{
    return "gvim";
}

/*
 * Version number of the editor.
 * This number is communicated along with the protocol
 * version to the application.
 */
    char *
workshop_get_editor_version()
{
    return Version;
}

/*
 * Answer functions: called by eserve
 */

/*
 * Name:
 *	workshop_load_file
 *
 * Function:
 *	Load a given file into the WorkShop buffer.
 */
    void
workshop_load_file(
	char	*filename,		/* the file to load */
	int	 line,			/* an optional line number (or 0) */
	char	*frameid UNUSED)	/* used for multi-frame support */
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_load_file(%s, %d)\n", filename, line);
#endif

#ifdef FEAT_BEVAL
    bevalServers |= BEVAL_WORKSHOP;
#endif

    load_window(filename, line);
}

/*
 * Reload the WorkShop buffer
 */
    void
workshop_reload_file(
	char	*filename,
	int	 line)
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_reload_file(%s, %d)\n", filename, line);
#endif
    load_window(filename, line);
}

    void
workshop_show_file(
    char	*filename)
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_show_file(%s)\n", filename);
#endif

    load_window(filename, 0);
}

    void
workshop_goto_line(
    char	*filename,
    int		 lineno)
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_goto_line(%s, %d)\n", filename, lineno);
#endif

    load_window(filename, lineno);
}

    void
workshop_front_file(
	char	*filename UNUSED)
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_front_file()\n");
#endif
    /*
     * Assumption: This function will always be called after a call to
     * workshop_show_file(), so the file is always showing.
     */
    if (vimShell != NULL)
	XRaiseWindow(gui.dpy, XtWindow(vimShell));
}

    void
workshop_save_file(
	    char	*filename)
{
    char	 cbuf[BUFSIZ];		/* build vim command here */

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_save_file(%s)\n", filename);
#endif

    /* Save the given file */
    vim_snprintf(cbuf, sizeof(cbuf), "w %s", filename);
    coloncmd(cbuf, TRUE);
}

    void
workshop_save_files()
{
    /* Save the given file */
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_save_files()\n");
#endif

    add_to_input_buf((char_u *) ":wall\n", 6);
}

    void
workshop_quit()
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_quit()\n");
#endif

    add_to_input_buf((char_u *) ":qall\n", 6);
}

    void
workshop_minimize()
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_minimize()\n");
#endif
    workshop_minimize_shell(vimShell);
}
    void
workshop_maximize()
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_maximize()\n");
#endif

    workshop_maximize_shell(vimShell);
}

    void
workshop_add_mark_type(
	int		 idx,
	char		*colorspec,
	char		*sign)
{
    char	 gbuf[BUFSIZ];	/* buffer for sign name */
    char	 cibuf[BUFSIZ];	/* color information */
    char	 cbuf[BUFSIZ];	/* command buffer */
    char	*bp;

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
    {
	char *cp;

	cp = strrchr(sign, '/');
	if (cp == NULL)
	    cp = sign;
	else
	    cp++;		/* skip '/' character */
	wstrace("workshop_add_mark_type(%d, \"%s\", \"%s\")\n", idx,
		colorspec && *colorspec ? colorspec : "<None>", cp);
    }
#endif

    /*
     * Isolate the basename of sign in gbuf. We will use this for the
     * GroupName in the highlight command sent to vim.
     */
    STRCPY(gbuf, gettail((char_u *)sign));
    bp = strrchr(gbuf, '.');
    if (bp != NULL)
	*bp = NUL;

    if (gbuf[0] != '-' && gbuf[1] != NUL)
    {
	if (colorspec != NULL && *colorspec)
	{
	    vim_snprintf(cbuf, sizeof(cbuf),
				  "highlight WS%s guibg=%s", gbuf, colorspec);
	    coloncmd(cbuf, FALSE);
	    vim_snprintf(cibuf, sizeof(cibuf), "linehl=WS%s", gbuf);
	}
	else
	    cibuf[0] = NUL;

	vim_snprintf(cbuf, sizeof(cbuf),
			       "sign define %d %s icon=%s", idx, cibuf, sign);
	coloncmd(cbuf, TRUE);
    }
}

    void
workshop_set_mark(
	char		*filename,	/* filename which gets the mark */
	int		 lineno,	/* line number which gets the mark */
	int		 markId,	/* unique mark identifier */
	int		 idx)		/* which mark to use */
{
    char	cbuf[BUFSIZ];	/* command buffer */

    /* Set mark in a given file */
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_set_mark(%s, %d (ln), %d (id), %d (idx))\n",
		filename, lineno, markId, idx);
#endif

    vim_snprintf(cbuf, sizeof(cbuf), "sign place %d line=%d name=%d file=%s",
					       markId, lineno, idx, filename);
    coloncmd(cbuf, TRUE);
}

    void
workshop_change_mark_type(
	char		*filename,	/* filename which gets the mark */
	int		 markId,	/* unique mark identifier */
	int		 idx)		/* which mark to use */
{
    char	cbuf[BUFSIZ];	/* command buffer */

    /* Change mark type */
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_change_mark_type(%s, %d, %d)\n",
		filename, markId, idx);
#endif

    vim_snprintf(cbuf, sizeof(cbuf),
		      "sign place %d name=%d file=%s", markId, idx, filename);
    coloncmd(cbuf, TRUE);
}

/*
 * Goto the given mark in a file (e.g. show it).
 * If message is not null, display it in the footer.
 */
    void
workshop_goto_mark(
	char		*filename,
	int		 markId,
	char		*message)
{
    char	cbuf[BUFSIZ];	/* command buffer */

    /* Goto mark */
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_goto_mark(%s, %d (id), %s)\n",
		filename, markId, message && *message &&
		!(*message == ' ' && message[1] == NULL) ?
		message : "<None>");
#endif

    vim_snprintf(cbuf, sizeof(cbuf), "sign jump %d file=%s", markId, filename);
    coloncmd(cbuf, TRUE);
    if (message != NULL && *message != NUL)
	gui_mch_set_footer((char_u *)message);
}

    void
workshop_delete_mark(
	char		*filename,
	int		 markId)
{
    char	cbuf[BUFSIZ];	/* command buffer */

    /* Delete mark */
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_delete_mark(%s, %d (id))\n",
		filename, markId);
#endif

    vim_snprintf(cbuf, sizeof(cbuf),
				 "sign unplace %d file=%s", markId, filename);
    coloncmd(cbuf, TRUE);
}

    int
workshop_get_mark_lineno(
	char	*filename,
	int	 markId)
{
    buf_T	*buf;		/* buffer containing filename */
    int		lineno;		/* line number of filename in buf */

    /* Get mark line number */
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_get_mark_lineno(%s, %d)\n",
		filename, markId);
#endif

    lineno = 0;
    buf = buflist_findname((char_u *)filename);
    if (buf != NULL)
	lineno = buf_findsign(buf, markId);

    return lineno;
}


/*
 * Are there any moved marks? If so, call workshop_move_mark on
 * each of them now. This is how eserve can find out if for example
 * breakpoints have moved when a program has been recompiled and
 * reloaded into dbx.
 */
    void
workshop_moved_marks(char *filename UNUSED)
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("XXXworkshop_moved_marks(%s)\n", filename);
#endif
}

    int
workshop_get_font_height()
{
    XmFontList	 fontList;	/* fontList made from gui.norm_font */
    XmString	 str;
    Dimension	 w;
    Dimension	 h;

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_get_font_height()\n");
#endif

    /* Pick the proper signs for this font size */
    fontList = gui_motif_create_fontlist((XFontStruct *)gui.norm_font);
    h = 0;
    if (fontList != NULL)
    {
	str = XmStringCreateLocalized("A");
	XmStringExtent(fontList, str, &w, &h);
	XmStringFree(str);
	XmFontListFree(fontList);
    }

    return (int)h;
}

    void
workshop_footer_message(
	char	*message,
	int	severity UNUSED)	/* severity is currently unused */
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_footer_message(%s, %d)\n", message, severity);
#endif

    gui_mch_set_footer((char_u *) message);
}

/*
 * workshop_menu_begin() is passed the menu name. We determine its mnemonic
 * here and store its name and priority.
 */
    void
workshop_menu_begin(
	char		*label)
{
    vimmenu_T	*menu;			/* pointer to last menu */
    int		menuPriority = 0;	/* priority of new menu */
    char	mnembuf[64];		/* store menubar mnemonics here */
    char	*name;			/* label with a mnemonic */
    char	*p;			/* used to find mnemonics */
    int		idx;			/* index into mnembuf */

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_menu_begin()\n");
#endif

    /*
     * Look through all existing (non-PopUp and non-Toolbar) menus
     * and gather their mnemonics. Use this list to decide what
     * mnemonic should be used for label.
     */

    idx = 0;
    mnembuf[idx++] = 'H';		/* H is mnemonic for Help */
    for (menu = root_menu; menu != NULL; menu = menu->next)
    {
	if (menu_is_menubar(menu->name))
	{
	    p = strchr((char *)menu->name, '&');
	    if (p != NULL)
		mnembuf[idx++] = *++p;
	}
	if (menu->next != NULL
		&& strcmp((char *) menu->next->dname, "Help") == 0)
	{
	    menuPriority = menu->priority + 10;
	    break;
	}
    }
    mnembuf[idx++] = NUL;
    name = addUniqueMnemonic(mnembuf, label);

    vim_snprintf(curMenuName, sizeof(curMenuName), "%s", name);
    sprintf(curMenuPriority, "%d.0", menuPriority);
}

/*
 * Append the name and priority to strings to be used in vim menu commands.
 */
    void
workshop_submenu_begin(
	char		*label)
{
#ifdef WSDEBUG_TRACE
    if (ws_debug  && ws_dlevel & WS_TRACE
	    && strncmp(curMenuName, "ToolBar", 7) != 0)
	wstrace("workshop_submenu_begin(%s)\n", label);
#endif

    strcat(curMenuName, ".");
    strcat(curMenuName, fixup(label));

    updatePriority(True);
}

/*
 * Remove the submenu name and priority from curMenu*.
 */

    void
workshop_submenu_end()
{
    char		*p;

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE)
	    && strncmp(curMenuName, "ToolBar", 7) != 0)
	wstrace("workshop_submenu_end()\n");
#endif

    p = strrchr(curMenuPriority, '.');
    ASSERT(p != NULL);
    *p = NUL;

    p = strrchr(curMenuName, '.');
    ASSERT(p != NULL);
    *p = NUL;
}

/*
 * This is where menus are really made. Each item will generate an amenu vim
 * command. The globals curMenuName and curMenuPriority contain the name and
 * priority of the parent menu tree.
 */
    void
workshop_menu_item(
	char		*label,
	char		*verb,
	char		*accelerator UNUSED,
	char		*acceleratorText,
	char		*name UNUSED,
	char		*filepos UNUSED,
	char		*sensitive)
{
    char		 cbuf[BUFSIZ];
    char		 namebuf[BUFSIZ];
    char		 accText[BUFSIZ];

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE)
	    && strncmp(curMenuName, "ToolBar", 7) != 0)
    {
	if (ws_dlevel & WS_TRACE_VERBOSE)
	    wsdebug("workshop_menu_item(\n"
		    "\tlabel = \"%s\",\n"
		    "\tverb = %s,\n"
		    "\taccelerator = %s,\n"
		    "\tacceleratorText = \"%s\",\n"
		    "\tname = %s,\n"
		    "\tfilepos = %s,\n"
		    "\tsensitive = %s)\n",
		    label && *label ? label : "<None>",
		    verb && *verb ? verb : "<None>",
		    accelerator && *accelerator ?
		    accelerator : "<None>",
		    acceleratorText && *acceleratorText ?
		    acceleratorText : "<None>",
		    name && *name ? name : "<None>",
		    filepos && *filepos ? filepos : "<None>",
		    sensitive);
	else if (ws_dlevel & WS_TRACE)
	    wstrace("workshop_menu_item(\"%s\", %s)\n",
		    label && *label ? label : "<None>",
		    verb && *verb ? verb : "<None>", sensitive);
    }
#endif
#ifdef WSDEBUG_SENSE
    if (ws_debug)
	wstrace("menu:   %-21.20s%-21.20s(%s)\n", label, verb,
		*sensitive == '1' ? "Sensitive" : "Insensitive");
#endif

    if (acceleratorText != NULL)
	vim_snprintf(accText, sizeof(accText), "<Tab>%s", acceleratorText);
    else
	accText[0] = NUL;
    updatePriority(False);
    vim_snprintf(namebuf, sizeof(namebuf), "%s.%s", curMenuName, fixup(label));
    vim_snprintf(cbuf, sizeof(cbuf), "amenu %s %s%s\t:wsverb %s<CR>",
	    curMenuPriority, namebuf, accText, verb);

    coloncmd(cbuf, TRUE);
    addMenu(namebuf, fixAccelText(acceleratorText), verb);

    if (*sensitive == '0')
    {
	vim_snprintf(cbuf, sizeof(cbuf), "amenu disable %s", namebuf);
	coloncmd(cbuf, TRUE);
    }
}

/*
 * This function is called when a complete WorkShop menu description has been
 * sent over from eserve. We do some menu cleanup.
 */

    void
workshop_menu_end()
{
    Boolean		 using_tearoff;	/* set per current option setting */

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_menu_end()\n");
#endif

    using_tearoff = vim_strchr(p_go, GO_TEAROFF) != NULL;
    gui_mch_toggle_tearoffs(using_tearoff);
}

    void
workshop_toolbar_begin()
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_toolbar_begin()\n");
#endif

    coloncmd("aunmenu ToolBar", True);
    tbpri = 10;
}

    void
workshop_toolbar_end()
{
    char_u	buf[64];

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
    {
	wstrace("workshop_toolbar_end()\n");
    }
#endif

    /*
     * Turn on ToolBar.
     */
    STRCPY(buf, p_go);
    if (vim_strchr(p_go, 'T') == NULL)
    {
	STRCAT(buf, "T");
	set_option_value((char_u *)"go", 0L, buf, 0);
    }
    workshopInitDone = True;
}

    void
workshop_toolbar_button(
	char	*label,
	char	*verb,
	char	*senseVerb UNUSED,
	char	*filepos UNUSED,
	char	*help,
	char	*sense,
	char	*file,
	char	*left)
{
    char	cbuf[BUFSIZ + MAXPATHLEN];
    char	namebuf[BUFSIZ];
    static int	tbid = 1;
    char_u	*p;

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE))
	wsdebug("workshop_toolbar_button(\"%s\", %s, %s,\n"
		"\t%s, \"%s\", %s,\n\t\"%s\",\n\t<%s>)\n",
		label   && *label   ? label   : "<None>",
		verb    && *verb    ? verb    : "<None>",
		senseVerb && *senseVerb    ? senseVerb    : "<None>",
		filepos && *filepos ? filepos : "<None>",
		help    && *help    ? help    : "<None>",
		sense   && *sense   ? sense   : "<None>",
		file    && *file    ? file    : "<None>",
		left    && *left    ? left    : "<None>");
    else if (WSDLEVEL(WS_TRACE))
	wstrace("workshop_toolbar_button(\"%s\", %s)\n",
		label   && *label   ? label   : "<None>",
		verb    && *verb    ? verb    : "<None>");
#endif
#ifdef WSDEBUG_SENSE
    if (ws_debug)
	wsdebug("button: %-21.20s%-21.20s(%s)\n", label, verb,
		*sense == '1' ? "Sensitive" : "Insensitive");
#endif

    if (left && *left && atoi(left) > 0)
    {
	/* Add a separator (but pass the width passed after the ':') */
	sprintf(cbuf, "amenu 1.%d ToolBar.-sep%d:%s- <nul>",
		tbpri - 5, tbid++, left);

	coloncmd(cbuf, True);
    }

    p = vim_strsave_escaped((char_u *)label, (char_u *)"\\. ");
    vim_snprintf(namebuf, sizeof(namebuf), "ToolBar.%s", p);
    vim_free(p);
    STRCPY(cbuf, "amenu <silent> ");
    if (file != NULL && *file != NUL)
    {
	p = vim_strsave_escaped((char_u *)file, (char_u *)" ");
	vim_snprintf_add(cbuf, sizeof(cbuf), "icon=%s ", p);
	vim_free(p);
    }
    vim_snprintf_add(cbuf, sizeof(cbuf),"1.%d %s :wsverb %s<CR>",
							tbpri, namebuf, verb);

    /* Define the menu item */
    coloncmd(cbuf, True);

    if (*sense == '0')
    {
	/* If menu isn't sensitive at startup... */
	vim_snprintf(cbuf, sizeof(cbuf), "amenu disable %s", namebuf);
	coloncmd(cbuf, True);
    }

    if (help && *help)
    {
	/* Do the tooltip */
	vim_snprintf(cbuf, sizeof(cbuf), "tmenu %s %s", namebuf, help);
	coloncmd(cbuf, True);
    }

    addMenu(namebuf, NULL, verb);
    tbpri += 10;
}

    void
workshop_frame_sensitivities(
	VerbSense	*vs)		/* list of verbs to (de)sensitize */
{
    VerbSense	*vp;		/* iterate through vs */
    char	*menu_name;	/* used in menu lookup */
    int		 cnt;		/* count of verbs to skip */
    int		 len;		/* length of nonvariant part of command */
    char	 cbuf[4096];

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE) || WSDLEVEL(4))
    {
	wsdebug("workshop_frame_sensitivities(\n");
	for (vp = vs; vp->verb != NULL; vp++)
	    wsdebug("\t%-25s%d\n", vp->verb, vp->sense);
	wsdebug(")\n");
    }
    else if (WSDLEVEL(WS_TRACE))
	wstrace("workshop_frame_sensitivities()\n");
#endif
#ifdef WSDEBUG_SENSE
    if (ws_debug)
	for (vp = vs; vp->verb != NULL; vp++)
	    wsdebug("change: %-21.20s%-21.20s(%s)\n",
		    "", vp->verb, vp->sense == 1 ?
		    "Sensitive" : "Insensitive");
#endif

    /*
     * Look for all matching menu entries for the verb. There may be more
     * than one if the verb has both a menu and toolbar entry.
     */
    for (vp = vs; vp->verb != NULL; vp++)
    {
	cnt = 0;
	strcpy(cbuf, "amenu");
	strcat(cbuf, " ");
	strcat(cbuf, vp->sense ? "enable" : "disable");
	strcat(cbuf, " ");
	len = strlen(cbuf);
	while ((menu_name = lookupVerb(vp->verb, cnt++)) != NULL)
	{
	    strcpy(&cbuf[len], menu_name);
	    coloncmd(cbuf, FALSE);
	}
    }
    gui_update_menus(0);
    gui_mch_flush();
}

    void
workshop_set_option(
	char	*option,		/* name of a supported option */
	char	*value)			/* value to set option to */
{
    char	 cbuf[BUFSIZ];		/* command buffer */

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
    {
	wstrace("workshop_set_option(%s, %s)\n", option, value);
    }
#endif

    cbuf[0] = NUL;
    switch (*option)		/* switch on 1st letter */
    {
	case 's':
	    if (strcmp(option, "syntax") == 0)
		vim_snprintf(cbuf, sizeof(cbuf), "syntax %s", value);
	    else if (strcmp(option, "savefiles") == 0)
	    {
		/* XXX - Not yet implemented */
	    }
	    break;

	case 'l':
	    if (strcmp(option, "lineno") == 0)
		sprintf(cbuf, "set %snu",
			(strcmp(value, "on") == 0) ? "" : "no");
	    break;

	case 'p':
	    if (strcmp(option, "parentheses") == 0)
		sprintf(cbuf, "set %ssm",
			(strcmp(value, "on") == 0) ? "" : "no");
	    break;

	case 'w':
	    /* this option is set by a direct call */
#ifdef WSDEBUG
	    wsdebug("workshop_set_option: "
		    "Got unexpected workshopkeys option");
#endif
	    break;

	case 'b':	/* these options are set from direct calls */
	    if (option[7] == NUL && strcmp(option, "balloon") == 0)
	    {
#ifdef WSDEBUG
		/* set by direct call to workshop_balloon_mode */
		wsdebug("workshop_set_option: "
			"Got unexpected ballooneval option");
#endif
	    }
	    else if (strcmp(option, "balloondelay") == 0)
	    {
#ifdef WSDEBUG
		/* set by direct call to workshop_balloon_delay */
		wsdebug("workshop_set_option: "
			"Got unexpected balloondelay option");
#endif
	    }
	    break;
    }
    if (cbuf[0] != NUL)
	coloncmd(cbuf, TRUE);
}


    void
workshop_balloon_mode(
	Boolean	 on)
{
    char	 cbuf[BUFSIZ];		/* command buffer */

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_balloon_mode(%s)\n", on ? "True" : "False");
#endif

    sprintf(cbuf, "set %sbeval", on ? "" : "no");
    coloncmd(cbuf, TRUE);
}


    void
workshop_balloon_delay(
	int	 delay)
{
    char	 cbuf[BUFSIZ];		/* command buffer */

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_balloon_delay(%d)\n", delay);
#endif

    sprintf(cbuf, "set bdlay=%d", delay);
    coloncmd(cbuf, TRUE);
}


    void
workshop_show_balloon_tip(
	char	*tip)
{
#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_show_balloon_tip(%s)\n", tip);
#endif

    if (balloonEval != NULL)
	gui_mch_post_balloon(balloonEval, (char_u *)tip);
}


    void
workshop_hotkeys(
	Boolean	on)
{
    char	 cbuf[BUFSIZ];		/* command buffer */
    MenuMap	*mp;			/* iterate over menuMap entries */

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_hotkeys(%s)\n", on ? "True" : "False");
#endif

    workshopHotKeysEnabled = on;
    if (workshopHotKeysEnabled)
	for (mp = menuMap; mp < &menuMap[menuMapSize]; mp++)
	{
	    if (mp->accel != NULL)
	    {
		vim_snprintf(cbuf, sizeof(cbuf),
			"map %s :wsverb %s<CR>", mp->accel, mp->verb);
		coloncmd(cbuf, TRUE);
	    }
	}
    else
	for (mp = menuMap; mp < &menuMap[menuMapSize]; mp++)
	{
	    if (mp->accel != NULL)
	    {
		vim_snprintf(cbuf, sizeof(cbuf), "unmap %s", mp->accel);
		coloncmd(cbuf, TRUE);
	    }
	}
}

/*
 * A button in the toolbar has been pushed.
 */
    int
workshop_get_positions(
	void		*clientData UNUSED,
	char	       **filename,	/* output data */
	int		*curLine,	/* output data */
	int		*curCol,	/* output data */
	int		*selStartLine,	/* output data */
	int		*selStartCol,	/* output data */
	int		*selEndLine,	/* output data */
	int		*selEndCol,	/* output data */
	int		*selLength,	/* output data */
	char	       **selection)	/* output data */
{
    static char	 ffname[MAXPATHLEN];

#ifdef WSDEBUG_TRACE
    if (WSDLEVEL(WS_TRACE_VERBOSE | WS_TRACE))
	wstrace("workshop_get_positions(%#x, \"%s\", ...)\n",
		clientData, (curbuf && curbuf->b_sfname != NULL)
				      ? (char *)curbuf->b_sfname : "<None>");
#endif

    if (curbuf->b_ffname == NULL)
	ffname[0] = NUL;
    else
	/* copy so nobody can change b_ffname */
	strcpy(ffname, (char *) curbuf->b_ffname);
    *filename = ffname;
    *curLine = curwin->w_cursor.lnum;
    *curCol = curwin->w_cursor.col;

    if (curbuf->b_visual.vi_mode == 'v' &&
	    equalpos(curwin->w_cursor, curbuf->b_visual.vi_end))
    {
	*selStartLine = curbuf->b_visual.vi_start.lnum;
	*selStartCol = curbuf->b_visual.vi_start.col;
	*selEndLine = curbuf->b_visual.vi_end.lnum;
	*selEndCol = curbuf->b_visual.vi_end.col;
	*selection = get_selection(curbuf);
	if (*selection)
	    *selLength = strlen(*selection);
	else
	    *selLength = 0;
    }
    else
    {
	*selStartLine = *selEndLine = -1;
	*selStartCol = *selEndCol = -1;
	*selLength = 0;
	*selection = "";
    }

    return True;
}



/************************************************************************
 * Utility functions
 ************************************************************************/

    static char *
get_selection(
	buf_T		*buf)		/* buffer whose selection we want */
{
    pos_T	*start;		/* start of the selection */
    pos_T	*end;		/* end of the selection */
    char	*lp;		/* pointer to actual line data */
    int		 llen;		/* length of actual line data */
    char	*sp;		/* pointer to selection  buffer */
    int		 slen;		/* string length in selection buffer */
    int		 size;		/* size of selection buffer */
    char	*new_sp;	/* temp pointer to new sp */
    int		 lnum;		/* line number we are appending */

    if (buf->b_visual.vi_mode == 'v')
    {
	start = &buf->b_visual.vi_start;
	end = &buf->b_visual.vi_end;
	if (start->lnum == end->lnum)
	{
	    /* selection is all on one line */
	    lp = (char *) ml_get_pos(start);
	    llen = end->col - start->col + 1;
	    sp = (char *) malloc(llen + 1);
	    if (sp != NULL)
	    {
		strncpy(sp, lp, llen);
		sp[llen] = NUL;
	    }
	}
	else
	{
	    /* multi-line selection */
	    lp = (char *) ml_get_pos(start);
	    llen = strlen(lp);
	    sp = (char *) malloc(BUFSIZ + llen);
	    if (sp != NULL)
	    {
		size = BUFSIZ + llen;
		strcpy(sp, lp);
		sp[llen] = '\n';
		slen = llen + 1;

		lnum = start->lnum + 1;
		while (lnum < end->lnum)
		    sp = append_selection(lnum++, sp, &size, &slen);

		lp = (char *) ml_get(end->lnum);
		llen = end->col + 1;
		if ((slen + llen) >= size)
		{
		    new_sp = (char *)
			realloc(sp, slen + llen + 1);
		    if (new_sp != NULL)
		    {
			size += llen + 1;
			sp = new_sp;
		    }
		}
		if ((slen + llen) < size)
		{
		    strncpy(&sp[slen], lp, llen);
		    sp[slen + llen] = NUL;
		}

	    }
	}
    }
    else
	sp = NULL;

    return sp;
}

    static char *
append_selection(
	int		 lnum,		/* line number to append */
	char		*sp,		/* pointer to selection buffer */
	int		*size,		/* ptr to size of sp */
	int		*slen)		/* ptr to length of selection string */
{
    char	*lp;		/* line of data from buffer */
    int		 llen;		/* strlen of lp */
    char	*new_sp;	/* temp pointer to new sp */

    lp = (char *)ml_get((linenr_T)lnum);
    llen = strlen(lp);

    if ((*slen + llen) <= *size)
    {
	new_sp = (char *) realloc((void *) sp, BUFSIZ + *slen + llen);
	if (*new_sp != NUL)
	{
	    *size = BUFSIZ + *slen + llen;
	    sp = new_sp;
	}
    }
    if ((*slen + llen) > *size)
    {
	strcat(&sp[*slen], lp);
	*slen += llen;
	sp[*slen++] = '\n';
    }

    return sp;
}



    static void
load_buffer_by_name(
	char	*filename,		/* the file to load */
	int	 lnum)			/* an optional line number (or 0) */
{
    char	 lnumbuf[16];		/* make line number option for :e */
    char	 cbuf[BUFSIZ];		/* command buffer */

    if (lnum > 0)
	sprintf(lnumbuf, "+%d", lnum);
    else
	lnumbuf[0] = NUL;

    vim_snprintf(cbuf, sizeof(cbuf), "e %s %s", lnumbuf, filename);
    coloncmd(cbuf, False);
}


    static void
load_window(
	char	*filename,		/* filename to load */
	int	 lnum)			/* linenumber to go to */
{
    buf_T	*buf;		/* buffer filename is stored in */
    win_T	*win;		/* window filenme is displayed in */

    /*
     * Make sure filename is displayed and is the current window.
     */

    buf = buflist_findname((char_u *)filename);
    if (buf == NULL || (win = get_window(buf)) == NULL)
    {
	/* No buffer or buffer is not in current window */
	/* wsdebug("load_window: load_buffer_by_name(\"%s\", %d)\n",
		filename, lnum); */
	load_buffer_by_name(filename, lnum);
    }
    else
    {
#ifdef FEAT_WINDOWS
	/* buf is in a window */
	if (win != curwin)
	{
	    win_enter(win, False);
	    /* wsdebug("load_window: window enter %s\n",
		    win->w_buffer->b_sfname); */
	}
#endif
	if (lnum > 0 && win->w_cursor.lnum != lnum)
	{
	    warp_to_pc(lnum);
	    /* wsdebug("load_window: warp to %s[%d]\n",
		    win->w_buffer->b_sfname, lnum); */
	}
    }
    out_flush();
}



    static void
warp_to_pc(
	int	 lnum)			/* line number to warp to */
{
    char	 lbuf[256];		/* build line command here */

    if (lnum > 0)
    {
	if (State & INSERT)
	    add_to_input_buf((char_u *) "\033", 1);
	if (isShowing(lnum))
	    sprintf(lbuf, "%dG", lnum);
	else
	    sprintf(lbuf, "%dz.", lnum);
	add_to_input_buf((char_u *) lbuf, strlen(lbuf));
    }
}

    static Boolean
isShowing(
	int	 lnum)			/* tell if line number is showing */
{
    return lnum >= curwin->w_topline && lnum < curwin->w_botline;
}



    static win_T *
get_window(
	buf_T	*buf)		/* buffer to find window for */
{
    win_T	*wp = NULL;	/* window filename is in */

    for (wp = firstwin; wp != NULL; wp = W_NEXT(wp))
	if (buf == wp->w_buffer)
	    break;
    return wp;
}


    static void
updatePriority(
	Boolean		 subMenu)	/* if True then start new submenu pri */
{
    int		 pri;		/* priority of this menu/item */
    char	*p;

    p = strrchr(curMenuPriority, '.');
    ASSERT(p != NULL);
    *p++ = NUL;

    pri = atoi(p) + 10;		/* our new priority */

    if (subMenu)
	vim_snprintf(curMenuPriority, sizeof(curMenuPriority),
					     "%s.%d.0", curMenuPriority, pri);
    else
	vim_snprintf(curMenuPriority, sizeof(curMenuPriority),
					       "%s.%d", curMenuPriority, pri);
}

    static char *
addUniqueMnemonic(
	char		*mnemonics,	/* currently used mnemonics */
	char		*label)		/* label of menu needing mnemonic */
{
    static char	 name[BUFSIZ];	/* buffer for the updated name */
    char	*p;		/* pointer into label */
    char	*found;		/* pointer to possible mnemonic */

    found = NULL;
    for (p = label; *p != NUL; p++)
	if (strchr(mnemonics, *p) == 0)
	    if (found == NULL || (isupper((int)*p) && islower((int)*found)))
		found = p;

    if (found != NULL)
    {
	strncpy(name, label, (found - label));
	strcat(name, "&");
	strcat(name, found);
    }
    else
	strcpy(name, label);

    return name;
}

/*
 * Some characters in a menu name must be escaped in vim. Since this is vim
 * specific, it must be done on this side.
 */
    static char *
fixup(
	char		*label)
{
    static char	 buf[BUFSIZ];
    char		*bp;		/* pointer into buf */
    char		*lp;		/* pointer into label */

    lp = label;
    bp = buf;
    while (*lp != NUL)
    {
	if (*lp == ' ' || *lp == '.')
	    *bp++ = '\\';
	*bp++ = *lp++;
    }
    *bp = NUL;

    return buf;
}


#ifdef NOHANDS_SUPPORT_FUNCTIONS

/* For the NoHands test suite */

    char *
workshop_test_getcurrentfile()
{
    char	*filename, *selection;
    int		curLine, curCol, selStartLine, selStartCol, selEndLine;
    int		selEndCol, selLength;

    if (workshop_get_positions(
		NULL, &filename, &curLine, &curCol, &selStartLine,
		&selStartCol, &selEndLine, &selEndCol, &selLength,
		&selection))
	return filename;
    else
	return NULL;
}

    int
workshop_test_getcursorrow()
{
    return 0;
}

    int
workshop_test_getcursorcol()
{
    char	*filename, *selection;
    int		curLine, curCol, selStartLine, selStartCol, selEndLine;
    int		selEndCol, selLength;

    if (workshop_get_positions(
		NULL, &filename, &curLine, &curCol, &selStartLine,
		&selStartCol, &selEndLine, &selEndCol, &selLength,
		&selection))
	return curCol;
    else
	return -1;
}

    char *
workshop_test_getcursorrowtext()
{
    return NULL;
}

    char *
workshop_test_getselectedtext()
{
    char	*filename, *selection;
    int		curLine, curCol, selStartLine, selStartCol, selEndLine;
    int		selEndCol, selLength;

    if (workshop_get_positions(
		NULL, &filename, &curLine, &curCol, &selStartLine,
		&selStartCol, &selEndLine, &selEndCol, &selLength,
		&selection))
	return selection;
    else
	return NULL;
}

    void
workshop_save_sensitivity(char *filename UNUSED)
{
}

#endif

    static char *
fixAccelText(
	char		*ap)		/* original acceleratorText */
{
    char	buf[256];	/* build in temp buffer */
    char	*shift;		/* shift string of "" */

    if (ap == NULL)
	return NULL;

    /* If the accelerator is shifted use the vim form */
    if (strncmp("Shift+", ap, 6) == 0)
    {
	shift = "S-";
	ap += 6;
    }
    else
	shift = "";

    if (*ap == 'F' && atoi(&ap[1]) > 0)
    {
	vim_snprintf(buf, sizeof(buf), "<%s%s>", shift, ap);
	return strdup(buf);
    }
    else
	return NULL;
}

#ifdef FEAT_BEVAL
    void
workshop_beval_cb(
	BalloonEval	*beval,
	int		 state)
{
    win_T	*wp;
    char_u	*text;
    int		 type;
    linenr_T	 lnum;
    int		 col;
    int		 idx;
    char	 buf[MAXPATHLEN * 2];
    static int	 serialNo = -1;

    if (!p_beval)
	return;

    if (get_beval_info(beval, FALSE, &wp, &lnum, &text, &col) == OK)
    {
	if (text && text[0])
	{
	    /* Send debugger request */
	    if (strlen((char *) text) > (MAXPATHLEN/2))
	    {
		/*
		 * The user has probably selected the entire
		 * buffer or something like that - don't attempt
		 * to evaluate it
		 */
		return;
	    }

	    /*
	     * WorkShop expects the col to be a character index, not
	     * a column number. Compute the index from col. Also set
	     * line to 0 because thats what dbx expects.
	     */
	    idx = computeIndex(col, text, beval->ts);
	    if (idx > 0)
	    {
		lnum = 0;

		/*
		 * If successful, it will respond with a balloon cmd.
		 */
		if (state & ControlMask)
		    /* Evaluate *(expression) */
		    type = (int)GPLineEval_INDIRECT;
		else if (state & ShiftMask)
		    /* Evaluate type(expression) */
		    type = (int)GPLineEval_TYPE;
		else
		    /* Evaluate value(expression) */
		    type = (int)GPLineEval_EVALUATE;

		/* Send request to dbx */
		vim_snprintf(buf, sizeof(buf), "toolVerb debug.balloonEval "
			"%s %ld,0 %d,0 %d,%d %ld %s\n",
			(char *)wp->w_buffer->b_ffname,
			(long)lnum, idx, type, serialNo++,
			(long)strlen((char *)text), (char *)text);
		balloonEval = beval;
		workshop_send_message(buf);
	    }
	}
    }
}

    static int
computeIndex(
	int		 wantedCol,
	char_u		*line,
	int		 ts)
{
    int		 col = 0;
    int		 idx = 0;

    while (line[idx])
    {
	if (line[idx] == '\t')
	    col += ts - (col % ts);
	else
	    col++;
	idx++;
	if (col >= wantedCol)
	    return idx;
    }

    return -1;
}
#endif

    static void
addMenu(
	char		*menu,		/* menu name */
	char		*accel,		/* accelerator text (optional) */
	char		*verb)		/* WorkShop action-verb */
{
    MenuMap		*newMap;
    char		 cbuf[BUFSIZ];

    if (menuMapSize >= menuMapMax)
    {
	newMap = realloc(menuMap,
		sizeof(MenuMap) * (menuMapMax + MENU_INC));
	if (newMap != NULL)
	{
	    menuMap = newMap;
	    menuMapMax += MENU_INC;
	}
    }
    if (menuMapSize < menuMapMax)
    {
	menuMap[menuMapSize].name = strdup(menu);
	menuMap[menuMapSize].accel = accel && *accel ? strdup(accel) : NULL;
	menuMap[menuMapSize++].verb = strdup(verb);
	if (accel && workshopHotKeysEnabled)
	{
	    vim_snprintf(cbuf, sizeof(cbuf),
					"map %s :wsverb %s<CR>", accel, verb);
	    coloncmd(cbuf, TRUE);
	}
    }
}

    static char *
nameStrip(
	char		*raw)		/* menu name, possibly with & chars */
{
    static char		buf[BUFSIZ];	/* build stripped name here */
    char		*bp = buf;

    while (*raw)
    {
	if (*raw != '&')
	    *bp++ = *raw;
	raw++;
    }
    *bp = NUL;
    return buf;
}


    static char *
lookupVerb(
	char	*verb,
	int	skip)		/* number of matches to skip */
{
    int		i;		/* loop iterator */

    for (i = 0; i < menuMapSize; i++)
	if (strcmp(menuMap[i].verb, verb) == 0 && skip-- == 0)
	    return nameStrip(menuMap[i].name);

    return NULL;
}


    static void
coloncmd(
	char	*cmd,		/* the command to print */
	Boolean	force)		/* force cursor update */
{
    char_u	*cpo_save = p_cpo;

#ifdef WSDEBUG
    if (WSDLEVEL(WS_TRACE_COLONCMD))
	wsdebug("Cmd: %s\n", cmd);
#endif

    p_cpo = empty_option;

    ALT_INPUT_LOCK_ON;
    do_cmdline_cmd((char_u *)cmd);
    ALT_INPUT_LOCK_OFF;

    p_cpo = cpo_save;

    if (force)
	gui_update_screen();
}

/*
 * setDollarVim -	Given the run directory, search for the vim install
 *			directory and set $VIM.
 *
 *			We can be running out of SUNWspro/bin or out of
 *			SUNWspro/contrib/contrib6/vim5.6/bin so we check
 *			relative to both of these directories.
 */
    static void
setDollarVim(
	char	*rundir)
{
    char	 buf[MAXPATHLEN];
    char	*cp;

    /*
     * First case: Running from <install-dir>/SUNWspro/bin
     */
    strcpy(buf, rundir);
    strcat(buf, "/../contrib/contrib6/vim" VIM_VERSION_SHORT "/share/vim/"
	    VIM_VERSION_NODOT "/syntax/syntax.vim");
    if (access(buf, R_OK) == 0)
    {
	strcpy(buf, "SPRO_WSDIR=");
	strcat(buf, rundir);
	cp = strrchr(buf, '/');
	if (cp != NULL)
	    strcpy(cp, "/WS6U2");
	putenv(strdup(buf));

	strcpy(buf, "VIM=");
	strcat(buf, rundir);
	strcat(buf, "/../contrib/contrib6/vim" VIM_VERSION_SHORT "/share/vim/"
		VIM_VERSION_NODOT);
	putenv(strdup(buf));
	return;
    }

    /*
     * Second case: Probably running from
     *		<install-dir>/SUNWspro/contrib/contrib6/vim5.6/bin
     */
    strcpy(buf, rundir);
    strcat(buf, "/../../../contrib/contrib6/vim" VIM_VERSION_SHORT
	    "/share/vim/" VIM_VERSION_NODOT "/syntax/syntax.vim");
    if (access(buf, R_OK) == 0)
    {
	strcpy(buf, "SPRO_WSDIR=");
	strcat(buf, rundir);
	cp = strrchr(buf, '/');
	if (cp != NULL)
	    strcpy(cp, "../../../../WS6U2");
	putenv(strdup(buf));

	strcpy(buf, "VIM=");
	strcat(buf, rundir);
	strcat(buf, "/../../../contrib/contrib6/vim" VIM_VERSION_SHORT
		"/share/vim/" VIM_VERSION_NODOT);
	putenv(strdup(buf));
	return;
    }
}

/*
 * findYourself -	Find the directory we are running from. This is used to
 *			set $VIM. We need to set this because users can install
 *			the package in a different directory than the compiled
 *			directory. This is a Sun Visual WorkShop requirement!
 *
 * Note:		We override a user's $VIM because it won't have the
 *			WorkShop specific files. S/he may not like this but its
 *			better than getting the wrong files (especially as the
 *			user is likely to have $VIM set to 5.4 or later).
 */
    void
findYourself(
    char	*argv0)
{
    char	*runpath = NULL;
    char	*path;
    char	*pathbuf;

    if (*argv0 == '/')
	runpath = strdup(argv0);
    else if (*argv0 == '.' || strchr(argv0, '/'))
    {
	runpath = (char *) malloc(MAXPATHLEN);
	if (getcwd(runpath, MAXPATHLEN) == NULL)
	    runpath[0] = NUL;
	strcat(runpath, "/");
	strcat(runpath, argv0);
    }
    else
    {
	path = getenv("PATH");
	if (path != NULL)
	{
	    runpath = (char *) malloc(MAXPATHLEN);
	    pathbuf = strdup(path);
	    path = strtok(pathbuf, ":");
	    do
	    {
		strcpy(runpath, path);
		strcat(runpath, "/");
		strcat(runpath, argv0);
		if (access(runpath, X_OK) == 0)
		    break;
	    } while ((path = strtok(NULL, ":")) != NULL);
	    free(pathbuf);
	}
    }

    if (runpath != NULL)
    {
	char runbuf[MAXPATHLEN];

	/*
	 * We found the run directory. Now find the install dir.
	 */
	(void)vim_FullName((char_u *)runpath, (char_u *)runbuf, MAXPATHLEN, 1);
	path = strrchr(runbuf, '/');
	if (path != NULL)
	    *path = NUL;		/* remove the vim/gvim name */
	path = strrchr(runbuf, '/');
	if (path != NULL)
	{
	    if (strncmp(path, "/bin", 4) == 0)
		setDollarVim(runbuf);
	    else if (strncmp(path, "/src", 4) == 0)
	    {
		*path = NUL;	/* development tree */
		setDollarVim(runbuf);
	    }
	}
	free(runpath);
    }
}