[comp.sources.sun] v03i002: From SunView to OPEN LOOK via Developer's GUIDE

mcgrew@aramis.rutgers.edu (Charles Mcgrew) (05/15/91)

Submitted-by: leif@winsoft.se (Leif Samuelsson)
Posting-number: Volume 3, Issue 1
Archive-name: sunview2open-look

[ My apologies to one and all for the long quiet spell on this news-
group.  Various work and non-work (like getting married) items got
in the way of my doing moderation duties.  I do not plan for this
"silence" to occur again. - CWM]


Porting an application from SunView to XView is easy - if all you want
is to make it compile and run.  Changing the user interface to comply
with the OPEN LOOK style guide is harder and takes a lot more time.

Here is a utility I wrote for dumping a SunView window hierarchy to a
file in Developer's GUIDE format.  This makes it easier to change the
layout of subwindows and panel objects.  Uisng GUIDE, a new ui module
can then be generated and used to replace the old SunView window code.



Leif Samuelsson

Winsoft Data AB			Phone: (011 468) 730-1240
Lidgatan 18			Fax:   (011 468) 730-3102
S-171 58 SOLNA			E-mail: leif@winsoft.se
Sweden


--------Cut here-----
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by winsoft!leif on Sat Jan 19 17:12:03 MET 1991
# Contents:  README svg.c
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'
README for svg.c

This module can be used with a SunView application to generate a
user interface description file in Developer's GUIDE format.

To use it, compile svg.c and link it with the old SunView program.
At least one function call needs to be added to the main module of
the application.  See the instructions below.

This utility is meant as an aid only and will probably not work very
well with applications that use anything other than standard
(simple) SunView objects. In particular, the following limitations
apply:

      -	All objects need to be created (instantiated) before the
	call to window_main_loop().

      - Variable names are only preserved for global top-level
	variables.

      - Button label strings can only be extracted (using an ugly
	brute-force method) if the button font is the same as the
	panel font.

      - Unparented objects have to be dumped with separate calls. The
	only such calls provided are for frames and menus.

      - This software is provided as is. I do not guarantee any
	support, but am grateful for any comments, bug fixes and
	enhancements.

      - Core dumps are not unlikely to happen.



Instructions
------------

1.	Add the call

		svg_dump(base_frame, argv);

	to your application just before (or instead of)
	window_main_loop().  When run, it dumps the entire window
	hierarchy below base_frame to a file called progname.G

	If you have floating menus that are created without parents,
	use these calls instead of the one above:

		svg_start(argv);
		svg_dump_baseframe(base_frame);
		svg_dump_menu(menu_1);
		...
		svg_dump_menu(menu_n);
		svg_end();


2.	Compile svg.c:

		cc -c svg.c


3.	Link svg.o with application using the -Bstatic flag.



Copyright
---------

	Copyright (c) 1990, Winsoft Data AB

	This code may be freely copied, as long as this copyright
	notice is left intact.



-------
Leif Samuelsson

Winsoft Data AB			Phone: +46 8 7301240
Lidgatan 18			Fax:   +46 8 7303102
S-171 58 SOLNA			E-mail: leif@winsoft.se
Sweden
@//E*O*F README//
chmod u=rw,g=rw,o=r README
 
echo x - svg.c
sed 's/^@//' > "svg.c" <<'@//E*O*F svg.c//'
/*	svg.c		- Dumps SunView windows in DevGuide format
 *
 *	Copyright (c) 1991, Winsoft Data AB
 *
 *	This code may be freely copied, as long as this copyright
 *	notice is left intact.
 *
 *	Link with -Bstatic to application
 */

#ifndef lint
static	char sccsid[] = "@(#)svg.c 1.4 91-01-19 Copyright (c) 1991 Winsoft Data AB";
#endif

#include <suntool/sunview.h>
#include <suntool/frame.h>
#include <suntool/canvas.h>
#include <suntool/panel.h>
#include <suntool/textsw.h>
#include <suntool/tty.h>
#include <suntool/menu.h>
#include <stdio.h>
#include <a.out.h>
#include <stab.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>


typedef void (*funcp)();

/*
 *    External interface:
 *
 *	svg_dump(base_frame, argv);
 *
 *    This call should be placed just before (or instead of)
 *    window_main_loop().  It dumps the entire hierarchy under
 *    base_frame to the file progname.G
 *
 *
 *    Alternative calls for applications with menus and other
 *    objects which are not in the base_frame hierarchy:
 *
 *	svg_start(argv);
 *	svg_dump_baseframe(base_frame);
 *	svg_dump_menu(menu);
 *	...
 *	svg_end();
 *
 */

void	svg_dump(),
	svg_start(),
	svg_dump_baseframe(),
	svg_dump_subframe(),
	svg_dump_menu(),
	svg_end();

static void
	dump_subwindow(),
	dump_panel_items(),
	dump_button(),
	dump_icon(),
	dump_messageitem(),
	dump_textitem(),
	dump_choice(),
	dump_slider(),
	putname(),
	putstr(),
	putint(),
	putbool(),
	really_dump_menus();

static char
	*string_from_button_pr();

static Menu
	dump_panel_menu();


char  *newname(), *symbol(), *malloc();

static FILE		*svg_file = NULL;
static struct nlist	*svg_symtab;
static char		*svg_strtab;
static int		 svg_nsyms;
static Menu 		 svg_menus[256];
static int		 svg_nmenus;
static char		*progname;





void
svg_dump(frame, argv)
     Frame frame;
     char **argv;
{
    svg_start(argv);
    svg_dump_baseframe(frame);
    svg_end();
}


void
svg_dump_baseframe(frame)
     Frame frame;
{
    Window win;
    int n;
    
    fprintf(svg_file, "(\n");
    putname("type",		":base-window");

    putname("name", symbol(frame, TRUE, "frame"));

    putint("width",		window_get(frame, WIN_WIDTH) - 12);
    putint("height",		window_get(frame, WIN_HEIGHT) - 22);

    putstr("label",		window_get(frame, FRAME_LABEL));

    putname("label-type",	":string");

    putbool("mapped",		TRUE);

    putbool("show-footer",	TRUE);

    fprintf(svg_file, ")\n");

    n = 0;
    while (win = window_get(frame, FRAME_NTH_SUBWINDOW, n++, 0))
	dump_subwindow(win);

    n = 0;
    while (win = window_get(frame, FRAME_NTH_SUBFRAME, n++, 0))
	svg_dump_subframe(win);
}




void
svg_dump_subframe(frame)
     Frame frame;
{
    Window win;
    int n;
    
    fprintf(svg_file, "(\n");
    putname("type",		":popup-window");
    putname("name", symbol(frame, TRUE, "popup"));
    putname("owner",		symbol(window_get(frame, WIN_OWNER), TRUE, "frame"));

    putint("width",		window_get(frame, WIN_WIDTH) - 12);
    putint("height",		window_get(frame, WIN_HEIGHT) - 22);
    putstr("label",		window_get(frame, FRAME_LABEL));
    putname("label-type",	":string");
    putbool("mapped",		FALSE);
    putbool("show-footer",	TRUE);

    fprintf(svg_file, ")\n");

    n = 0;
    while (win = window_get(frame, FRAME_NTH_SUBWINDOW, n++, 0))
	dump_subwindow(win);
}





void
svg_dump_menu(menu)
     Menu	menu;
{
    int		i, nitems;
    Menu	submenu;
    Menu_item	item;

    nitems = (int)menu_get(menu, MENU_NITEMS);
    if (nitems == 0)
	return;

    for (i=1; i <= nitems; i++) {
	item = (Menu_item)menu_get(menu, MENU_NTH_ITEM, i);
	if (submenu = (Menu)menu_get(item, MENU_PULLRIGHT))
	    svg_dump_menu(submenu);
    }
    for (i=0; i<svg_nmenus; i++) {
	if (svg_menus[i] == menu)
	    return;
    }
    svg_menus[svg_nmenus++] = menu;
}


void
svg_start(argv)
     char **argv;
{
    int		fd;
    caddr_t	binfile;
    struct stat	statbuf;
    char	fname[128];
    struct exec	*exec;

    if (svg_file)
	return;

    progname = argv[0];

    fd = open(progname, 0);

    if (fd < 0) {
	perror("sv_dump");
/*	fprintf(stderr, "Can't find %s\n", progname); */
	exit(1);
    }

    fstat(fd, &statbuf);

    binfile = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);

    close(fd);

    if ((int)binfile == -1) {
	fprintf(stderr, "svg_dump: Can't mmap file\n");
	exit(2);
    }

    exec = (struct exec *)binfile;

    if (N_SYMOFF(*exec) > statbuf.st_size) {
	fprintf(stderr, "svg_dump: Can't find symbol table\n");
	exit(3);
    }
    svg_symtab = (struct nlist *) (binfile + N_SYMOFF(*exec));

    svg_nsyms = exec->a_syms / sizeof(struct nlist);

    svg_strtab = (char *)(binfile + N_STROFF(*exec));


    sprintf(fname, "%s.G", progname);
    svg_file = fopen(fname, "w");
    if (!svg_file) {
	perror(fname);
	exit(4);
    }
    printf("svg_dump: Dumping to %s", fname);
    fprintf(svg_file, ";GIL-1\n");
    fprintf(svg_file, ";\n");
    fprintf(svg_file, "; %s\n", fname);
    fprintf(svg_file, ";\n");
    fprintf(svg_file, "; This file was automatically generated by svg_dump()\n");
    fprintf(svg_file, ";\n");
    fprintf(svg_file, "(\n");
}


void
svg_end()
{
    if (!svg_file)
	return;

    really_dump_menus();

    fprintf(svg_file, ")\n");
    fclose(svg_file);
    printf("\n");
}






/* Private functions */

static void
dump_subwindow(win)
     Window win;
{
    Window_type	type;
    funcp fp;
    Menu	menu;

    extern void window_default_event_func();
    extern void panel_default_event();

    type = (Window_type)window_get(win, WIN_TYPE);

    fprintf(svg_file, "(\n");

    switch(type) {
      case ATTR_PKG_CANVAS:
	putname("type",	":canvas-pane");
	putname("name",	symbol(win, TRUE, "canvas"));
	break;

      case ATTR_PKG_PANEL:
	putname("type",	":control-area");
	putname("name", symbol(win, TRUE, "panel"));
	break;

      case ATTR_PKG_TEXTSW:
	putname("type", ":text-pane");
	putname("name",	symbol(win, TRUE, "textsw"));
	break;

      case ATTR_PKG_TTY:
	putname("type",	":term-pane");
	putname("name",	symbol(win, TRUE, "tty"));
	break;
    }

    putname("owner",	symbol(window_get(win, WIN_OWNER), TRUE, "frame"));
    putint("x",		window_get(win, WIN_X));
    putint("y",		window_get(win, WIN_Y));
    putint("width",	window_get(win, WIN_WIDTH));
    putint("height",	window_get(win, WIN_HEIGHT));

    switch(type) {
      case ATTR_PKG_CANVAS:
	menu = window_get(win, WIN_MENU);
	putname("menu",	symbol(menu, TRUE, "menu"));
	svg_dump_menu(menu);
	putbool("horizontal-scrollbar",
			    window_get(win, WIN_HORIZONTAL_SCROLLBAR));
	putint("scrollable-width", window_get(win, CANVAS_WIDTH));
	putbool("vertical-scrollbar",
			    window_get(win, WIN_VERTICAL_SCROLLBAR));
	putint("scrollable-height", window_get(win, CANVAS_HEIGHT));
	putname("repaint-proc", window_get(win, CANVAS_REPAINT_PROC));
	break;

      case ATTR_PKG_PANEL:
	putbool("show-border",	FALSE);
	putname("menu",	symbol(window_get(win, WIN_MENU), TRUE, "menu"));
	break;

      case ATTR_PKG_TEXTSW:
	putbool("show-border",	TRUE);
	menu = window_get(win, WIN_MENU);
	break;

      case ATTR_PKG_TTY:
	putbool("show-border",	TRUE);
	break;
    }


    /* Event handler and input mask */

    fp = (funcp)window_get(win, WIN_EVENT_PROC);
    switch(type) {
      case ATTR_PKG_PANEL:
	if (fp && fp != panel_default_event)
	    putname("event-handler", symbol(fp, FALSE, "proc"));
	break;

      default:
	if (fp && fp != window_default_event_func)
	    putname("event-handler", symbol(fp, FALSE, "proc"));
    }

    fprintf(svg_file, "\t:%-25s(", "events");
    if (window_get(win, WIN_CONSUME_KBD_EVENT, WIN_ASCII_EVENTS))
	fprintf(svg_file, ":keyboard ");
    if (window_get(win, WIN_CONSUME_KBD_EVENT, WIN_LEFT_KEYS))
	fprintf(svg_file, ":keyboard-left ");
    if (window_get(win, WIN_CONSUME_KBD_EVENT, WIN_RIGHT_KEYS))
	fprintf(svg_file, ":keyboard-right ");
    if (window_get(win, WIN_CONSUME_KBD_EVENT, WIN_TOP_KEYS))
	fprintf(svg_file, ":keyboard-top ");
    if (window_get(win, WIN_CONSUME_PICK_EVENT, MS_LEFT)
	|| window_get(win, WIN_CONSUME_PICK_EVENT, MS_MIDDLE)
	|| window_get(win, WIN_CONSUME_PICK_EVENT, MS_RIGHT))
	fprintf(svg_file, ":mouse ");
    if (window_get(win, WIN_CONSUME_PICK_EVENT, LOC_MOVE))
	fprintf(svg_file, ":mouse-move ");
    if (window_get(win, WIN_CONSUME_PICK_EVENT, LOC_DRAG))
	fprintf(svg_file, ":mouse-drag ");
    if (window_get(win, WIN_CONSUME_PICK_EVENT, LOC_WINENTER))
	fprintf(svg_file, ":mouse-enter ");
    if (window_get(win, WIN_CONSUME_PICK_EVENT, LOC_WINEXIT))
	fprintf(svg_file, ":mouse-exit");
    fprintf(svg_file, ")\n");

    fprintf(svg_file, ")\n");

    if (type == ATTR_PKG_PANEL)
	dump_panel_items(win);
}




/******** PANEL ITEMS *********/


/* types of items */
typedef enum { 
   PANEL_MESSAGE_ITEM,
   PANEL_BUTTON_ITEM, 
   PANEL_CHOICE_ITEM, 
   PANEL_TOGGLE_ITEM, 
   PANEL_TEXT_ITEM,
   PANEL_SLIDER_ITEM,
   PANEL_LINE_ITEM
} PANEL_ITEM_TYPE;
    
struct panel_item {
   int	i1[2];
   PANEL_ITEM_TYPE      item_type;      /* type of this item */
   int	i2[6];
   struct {
      int i3, i4:1;
      int i5[2];
      short s1;
   } st1;
   int	i6[6];
   struct menu          *menu;          /* menu of choices */
};

static void
dump_panel_items(panel)
     Panel panel;
{
    Panel_item	pi;
    char	*name, *str, prname[256];
    int		type;
    funcp	fun;
    struct pixrect *pr;
    struct menu	*menu;
    extern int	panel_nullproc();

    panel_each_item(panel, pi) {
	fprintf(svg_file, "(\n");

	switch (((struct panel_item *)pi)->item_type) {
	  case PANEL_BUTTON_ITEM:
	    dump_button(pi, panel);
	    break;

	  case PANEL_MESSAGE_ITEM:
	    dump_messageitem(pi);
	    break;

	  case PANEL_TEXT_ITEM:
	    dump_textitem(pi);
	    break;

	  case PANEL_CHOICE_ITEM:
	  case PANEL_TOGGLE_ITEM:
	    dump_choice(pi);
	    break;

	  case PANEL_SLIDER_ITEM:
	    dump_slider(pi);
	    break;

	  case PANEL_LINE_ITEM:
	    break;
	}

	name = symbol(pi, TRUE, "panel_item");
	putname("name",	 name);
	putname("owner", symbol(panel_get(pi, PANEL_PARENT_PANEL), TRUE, "panel"));
	putint("x",	 panel_get(pi, PANEL_ITEM_X));
	putint("y",	 panel_get(pi, PANEL_ITEM_Y));


	/* LABEL */
	if (pr = (struct pixrect *)panel_get(pi, PANEL_LABEL_IMAGE)) {
	    str = string_from_button_pr(pr, panel);
	    if (str) {
		putstr("label", str);
		putname("label-type", ":string");
	    } else {
		sprintf(prname, "%s_%s", progname, symbol(pr, TRUE, "glyph"));
		dump_icon(pr, prname);
		putstr("label", prname);
		putname("label-type", ":glyph");
	    }
	} else {
	    putstr("label", panel_get(pi, PANEL_LABEL_STRING));
	    putname("label-type", ":string");
	}

	/* MENU */
	switch (((struct panel_item *)pi)->item_type) {
	  case PANEL_CHOICE_ITEM:
	  case PANEL_TOGGLE_ITEM:
	    break;

	  default:
	    putname("menu",
		    symbol(dump_panel_menu(((struct panel_item *)pi)->menu),
			   TRUE, "menu"));
	}

	/* VALUE */
#ifdef NOTDEF
	/*
	 * GUIDE does not allow setting initial value yet
	 * so :initial-value is not used.
	 */
	switch (((struct panel_item *)pi)->item_type) {
	  case PANEL_CHOICE_ITEM:
	  case PANEL_TOGGLE_ITEM:
	  case PANEL_SLIDER_ITEM:
	    putint("initial-value", panel_get(pi, PANEL_VALUE));
	    break;

	  case PANEL_TEXT_ITEM:
	    putstr("initial-value", panel_get(pi, PANEL_VALUE));
	    break;
	}
#endif


	/* NOTIFY */
	fun = (funcp)panel_get(pi, PANEL_NOTIFY_PROC);
	if (fun && fun != (funcp)panel_nullproc)
	    putname("notify-handler", symbol(fun, FALSE, "proc"));
	fprintf(svg_file, ")\n");
    } panel_end_each;
}


static Menu
dump_panel_menu(menu)
     struct menu	*menu;
{
    int		i, nitems;
    Menu	walkmenu;

    if (!menu)
	return((Menu)0);

    walkmenu = menu_create(0);

    nitems = menu->m_itemcount;
    if (nitems == 0)
	return((Menu)0);

    for (i=0; i < nitems; i++) {
	menu_set(walkmenu, MENU_APPEND,
		 (menu->m_items[i].mi_imagetype == MENU_IMAGESTRING)
			? MENU_STRING_ITEM : MENU_IMAGE_ITEM,
				   menu->m_items[i].mi_imagedata,
				   menu->m_items[i].mi_data,
		 0);
    }
    svg_menus[svg_nmenus++] = walkmenu;
    return(walkmenu);
}





static char *
string_from_button_pr(pr, panel)
     struct pixrect *pr;
     Panel panel;
{
    static char str[128];
    char	tstr[128];
    int		i, c, prx, tprx;
    struct pixrect *tpr;


    bzero(str, 128);

    /* First check if left edge looks like a button */
    tpr = panel_button_image(panel, "xxx", 0, 0);
    c = prcmp(pr, 0, tpr, 0, 6);
    if (!c) {
	pr_destroy(tpr);
	return((char *)NULL);
    }

    /* Find start of text */
    tprx = 5;
    for (i=6; i < tpr->pr_size.x - 6; i++) {
	if (!prcmp(tpr, i, tpr, 5, 1)) {
	    tprx = i;
	    break;
	}
    }
    prx = 5;
    for (i=6; i < pr->pr_size.x - 6; i++) {
	if (!prcmp(pr, i, tpr, 5, 1)) {
	    prx = i;
	    break;
	}
    }
    pr_destroy(tpr);

    while (1) {
	for (i = 32 ; i<= 126; i++) {
	    sprintf(tstr, "%s%c", str, i);
	    tpr = panel_button_image(panel, tstr, 0, 0);
	    c = prcmp(pr, prx, tpr, tprx, tpr->pr_size.x - tprx - 6);
	    pr_destroy(tpr);
	    if (c) {
		strcpy(str, tstr);
		break;
	    }
	}
	if (i > 126) {
	    if (str[0])
		while (str[strlen(str)-1] == ' ')
		    str[strlen(str)-1] = '\0';
	    if (str[0])
		return(str);
	    else
		return((char *)NULL);
	}
    }
}


static int
prcmp(pr1, x1, pr2, x2, w)
     struct pixrect *pr1, *pr2;
     int x1, x2, w;
{
    int h, x, y;

    h = pr1->pr_size.y;
    if (h != pr2->pr_size.y)
	return(FALSE);
    for (y=0; y<h; y++) {
	for (x=0; x<w; x++) {
	    if (pr_get(pr1, x1+x, y) != pr_get(pr2, x2+x, y))
		return(FALSE);
	}
    }
    return(TRUE);
}


static void
dump_button(button, panel)
     Panel_item button;
     Panel	panel;
{
    struct rect *rect;

    putname("type",		":button");
    putbool("constant-width",	TRUE);
    rect = (struct rect *)panel_get(button, PANEL_ITEM_RECT);
    putint("width", rect->r_width - 10);
}

static void
dump_textitem(textitem)
     Panel_item textitem;
{
    putname("type",		":text-field");
}


static void
dump_messageitem(pi)
     Panel_item pi;
{
    putname("type", ":message");
    putbool("label-bold", panel_get(pi, PANEL_LABEL_BOLD));
}


static void
dump_choice(pi)
     Panel_item pi;
{
    int	  i, nchoices;
    char prname[256], types[1024];
    caddr_t choices[128];

    putname("type", ":setting");
    putname("layout-type",
	  ((Panel_setting)panel_get(pi, PANEL_LAYOUT) == PANEL_HORIZONTAL)
							    ? ":horizontal"
							    : ":vertical");
    putname("setting-type",
		(panel_get(pi, PANEL_CHOOSE_ONE) ? (((Panel_setting)panel_get(pi, PANEL_DISPLAY_LEVEL) == PANEL_CURRENT) ? ":stack" : ":exclusive") : ":nonexclusive"));
    putint("value-x",	  panel_get(pi, PANEL_VALUE_X));
    putint("value-y",	  panel_get(pi, PANEL_VALUE_Y));

    fprintf(svg_file, "\t:%-25s(", "choices");
    sprintf(types,    "\t:%-25s(", "choice-types");
    nchoices = 0;
    while (choices[nchoices] =
			(caddr_t)panel_get(pi, PANEL_CHOICE_IMAGE, nchoices))
	nchoices++;
    if (nchoices) {
	for (i=0; i<nchoices; i++) {
	    sprintf(prname, "%s_%s",
		    progname, symbol(choices[i], TRUE, "glyph"));
	    dump_icon(choices[i], prname);
	    fprintf(svg_file, "\"%s\" ", prname);
	    strcat(types, ":glyph ");
	}
    } else {
	while (choices[nchoices] =
			(caddr_t)panel_get(pi, PANEL_CHOICE_STRING, nchoices))
	    nchoices++;
	if (nchoices) {
	    for (i=0; i<nchoices; i++) {
		fprintf(svg_file, "\"%s\" ", choices[i]);
		strcat(types, ":string ");
	    }
	}
    }
    fprintf(svg_file, ")\n");
    strcat(types,    ")\n");

#ifdef NOTDEF
    /*
     * GUIDE does not accept glyphs for choices yet,
     * so :choice-types is not used.
     */
    fputs(types, svg_file); */
#endif

}



static void
dump_slider(slider)
     Panel_item slider;
{
    putname("type",		":slider");
    putname("layout-type",
	  ((Panel_setting)panel_get(slider, PANEL_LAYOUT) == PANEL_HORIZONTAL)
							    ? ":horizontal"
							    : ":vertical");
    putbool("show-range", panel_get(slider, PANEL_SHOW_RANGE));
    putbool("show-value", panel_get(slider, PANEL_SHOW_VALUE));
    putint("min-value",   panel_get(slider, PANEL_MIN_VALUE));
    putint("max-value",   panel_get(slider, PANEL_MAX_VALUE));
    putint("value-x",	  panel_get(slider, PANEL_VALUE_X));
    putint("value-y",	  panel_get(slider, PANEL_VALUE_Y));
    putint("slider-width",panel_get(slider, PANEL_SLIDER_WIDTH) - 8);
}



static void
really_dump_menus()
{
    Menu	menu, submenu;
    Menu_item	item, defitem;
    int 	i, j, nitems, cols;
    char	*str, prname[256], types[1024];
    funcp	fun;
    struct pixrect *pr;

    if (!svg_nmenus)
	return;

    for (i = 0; i < svg_nmenus; i++) {
	menu = svg_menus[i];
	fprintf(svg_file, "(\n");
	putname("type",	":menu");
	putname("name", symbol(menu, TRUE, "menu"));
	cols = (int)menu_get(menu, MENU_NCOLS);
	putint("columns", cols ? cols : 1);
	putname("menu-type", ":command");

	fun = (funcp)menu_get(menu, MENU_NOTIFY_PROC);
	if (fun && fun != (funcp)menu_return_value)
	    putname("menu-handler", symbol(fun, FALSE, "proc"));

	nitems = (int)menu_get(menu, MENU_NITEMS);
	defitem = (Menu_item)menu_get(menu, MENU_DEFAULT_ITEM);

	fprintf(svg_file, "\t:%-25s(", "menu-item-labels");
	sprintf(types,    "\t:%-25s(", "menu-item-label-types");
	for (j=1; j <= nitems; j++) {
	    item = (Menu_item)menu_get(menu, MENU_NTH_ITEM, j);

	    if (pr = (struct pixrect *)menu_get(item, MENU_IMAGE)) {
		sprintf(prname, "%s_%s", progname, symbol(pr, TRUE, "glyph"));
		dump_icon(pr, prname);
		fprintf(svg_file, "\"%s\" ", prname);
		strcat(types, ":glyph ");
	    } else {
		if (str = (char *)menu_get(item, MENU_STRING))
		    fprintf(svg_file, "\"%s\" ", str);
		else
		    fprintf(svg_file, "\"\" ", str);
		strcat(types, ":string ");
	    }
	}
	fprintf(svg_file, ")\n");
	strcat(types,    ")\n");
	fputs(types, svg_file);

	fprintf(svg_file, "\t:%-25s(", "menu-item-defaults");
	for (j=1; j <= nitems; j++) {
	    item = (Menu_item)menu_get(menu, MENU_NTH_ITEM, j);
	    fprintf(svg_file, (item == defitem) ? "t " : "nil ");
	}
	fprintf(svg_file, ")\n");

	fprintf(svg_file, "\t:%-25s(", "menu-item-handlers");
	for (j=1; j <= nitems; j++) {
	    item = (Menu_item)menu_get(menu, MENU_NTH_ITEM, j);
	    fun = (funcp)menu_get(item, MENU_ACTION_PROC);
	    fprintf(svg_file, "%s ", symbol(fun, FALSE, "proc"));
	}
	fprintf(svg_file, ")\n");

	fprintf(svg_file, "\t:%-25s(", "menu-item-menus");
	for (j=1; j <= nitems; j++) {
	    item = (Menu_item)menu_get(menu, MENU_NTH_ITEM, j);
	    submenu = (Menu)menu_get(item, MENU_PULLRIGHT);
	    fprintf(svg_file, "%s ", symbol(submenu, TRUE, "menu"));
	}
	fprintf(svg_file, ")\n");

	fprintf(svg_file, ")\n");
    }
}








static void
putname(name, value)
     char *name, *value;
{
    fprintf(svg_file, "\t:%-25s%s\n", name, value ? value : "nil");
}


static void
putstr(name, value)
     char *name, *value;
{
    fprintf(svg_file, "\t:%-25s\"%s\"\n", name, value ? value : "");
}


static void
putint(name, value)
     char	*name;
     int	value;
{
    fprintf(svg_file, "\t:%-25s%d\n", name, value);
}


static void
putbool(name, value)
     char	*name;
     int	value;
{
    fprintf(svg_file, "\t:%-25s%s\n", name, value ? "t" : "nil");
}


static char *
newname(str)
     char *str;
{
    static i = 0;
    char *p;

    p = malloc(10);

    sprintf(p, "%s_%d", str, i++);

    return (p);
}


static char *
symbol(addr, indirect, str)
     caddr_t	 addr;
     int	 indirect;
     char	*str;
{
    char	*p;
    int		i;
    caddr_t	v;
    static struct { caddr_t p; char *str; } lsyms[512];
    static int	nlsyms = 0;

    extern int	etext, edata, end;

    if (!addr)
	return("nil");

    for (i=0; i < nlsyms; i++) {
	if (addr == lsyms[i].p)
	    return(lsyms[i].str);
    }

    for (i=0; i< svg_nsyms; i++) {
	v = (caddr_t)svg_symtab[i].n_value;
	if (indirect) {
	    if (((int)v & 3) == 0
		&& v >= (caddr_t)&edata
		&& v < (caddr_t)&end
		&& *(caddr_t *)v == addr) {
		p = &svg_strtab[svg_symtab[i].n_un.n_strx];
		if (*p++ == '_'
		    && strcmp(p, "win_last_client")
		    && strncmp(p, "svg_", 4)
		    && strcmp(p, "edata"))
			return(p);
	    }
	} else {
	    if (v < (caddr_t)&etext && v == addr) {
		p = &svg_strtab[svg_symtab[i].n_un.n_strx];
		if (*p == '_') {
		    return(p+1);
		}
	    }
	}
    }

    lsyms[nlsyms].p = addr;
    lsyms[nlsyms].str = newname(str);
    return (lsyms[nlsyms++].str);
}


/*
 * dump_icon()
 *
 * code borrowed from /usr/src/sun/suntool/iconedit/iconedit_panel.c
 */
static void
dump_icon(pr, prname)
     struct pixrect *pr;
     char *prname;
{
    int		     	 i, limit;
    unsigned short	*data, *data_start;
    FILE		*fd;
    int			 width, height;
    short		 d0,d1,d2,d3,d4,d5,d6,d7;


    data_start = (unsigned short *)
		 ((struct mpr_data *)(LINT_CAST(pr->pr_data)))->md_image;
    data = data_start;

    width = pr->pr_size.x;
    height = pr->pr_size.y;

    fd = fopen(prname, "w");
    if (!fd)  {
       perror(prname);
       exit(5);
    }
    fprintf(fd, "/* Format_version=1, Width=%d, Height=%d, %s=%d\n",
	    width, height,
	    "Depth=1, Valid_bits_per_item", 8*sizeof(*data));
    fprintf(fd, " */\n");

    limit = pr->pr_size.x * pr->pr_size.y /
	     (8 * 8 * sizeof(*data));
    for (i=0;;) {
	d0 = data[0];d1 = data[1];d2 = data[2];d3 = data[3];
	d4 = data[4];d5 = data[5];d6 = data[6];d7 = data[7];
	fprintf(fd,
	   "\t0x%-04X,0x%-04X,0x%-04X,0x%-04X,0x%-04X,0x%-04X,0x%-04X,0x%-04X",
	   d0,d1,d2,d3,d4,d5,d6,d7);

       data += 8;
       if (++i < limit)
	  fprintf(fd,",\n");
       else
	  break;
    }      
    fputs("\n", fd);
    fclose(fd);
}

@//E*O*F svg.c//
chmod u=rw,g=r,o=r svg.c
 
exit 0