winprefs.c revision 05b261ec
1/*
2 * Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
19 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * Except as contained in this notice, the name of the XFree86 Project
24 * shall not be used in advertising or otherwise to promote the sale, use
25 * or other dealings in this Software without prior written authorization
26 * from the XFree86 Project.
27 *
28 * Authors:     Earle F. Philhower, III
29 */
30
31#ifdef HAVE_XWIN_CONFIG_H
32#include <xwin-config.h>
33#endif
34#include <stdio.h>
35#include <stdlib.h>
36#ifdef __CYGWIN__
37#include <sys/resource.h>
38#endif
39#include "win.h"
40
41#include <X11/Xwindows.h>
42#include <shellapi.h>
43
44#include "winprefs.h"
45#include "winmultiwindowclass.h"
46
47/* Where will the custom menu commands start counting from? */
48#define STARTMENUID WM_USER
49
50/* External global variables */
51#ifdef XWIN_MULTIWINDOW
52extern DWORD g_dwCurrentThreadID;
53#endif
54
55extern const char *winGetBaseDir(void);
56
57/* From winmultiwindowflex.l, the real parser */
58extern void parse_file (FILE *fp);
59
60/* From winprefyacc.y, the pref structure loaded by the parser */
61extern WINPREFS pref;
62
63/* The global X default icon */
64extern HICON		g_hIconX;
65extern HICON		g_hSmallIconX;
66
67/* Currently in use command ID, incremented each new menu item created */
68static int g_cmdid = STARTMENUID;
69
70
71/* Defined in DIX */
72extern char *display;
73
74/* Local function to handle comma-ified icon names */
75static HICON
76LoadImageComma (char *fname, int sx, int sy, int flags);
77
78
79/*
80 * Creates or appends a menu from a MENUPARSED structure
81 */
82static HMENU
83MakeMenu (char *name,
84	  HMENU editMenu,
85	  int editItem)
86{
87  int i;
88  int item;
89  MENUPARSED *m;
90  HMENU hmenu, hsub;
91
92  for (i=0; i<pref.menuItems; i++)
93    {
94      if (!strcmp(name, pref.menu[i].menuName))
95	break;
96    }
97
98  /* Didn't find a match, bummer */
99  if (i==pref.menuItems)
100    {
101      ErrorF("MakeMenu: Can't find menu %s\n", name);
102      return NULL;
103    }
104
105  m = &(pref.menu[i]);
106
107  if (editMenu)
108    {
109      hmenu = editMenu;
110      item = editItem;
111    }
112  else
113    {
114      hmenu = CreatePopupMenu();
115      if (!hmenu)
116	{
117	  ErrorF("MakeMenu: Unable to CreatePopupMenu() %s\n", name);
118	  return NULL;
119	}
120      item = 0;
121    }
122
123  /* Add the menu items */
124  for (i=0; i<m->menuItems; i++)
125    {
126      /* Only assign IDs one time... */
127      if ( m->menuItem[i].commandID == 0 )
128	m->menuItem[i].commandID = g_cmdid++;
129
130      switch (m->menuItem[i].cmd)
131	{
132	case CMD_EXEC:
133	case CMD_ALWAYSONTOP:
134	case CMD_RELOAD:
135	  InsertMenu (hmenu,
136		      item,
137		      MF_BYPOSITION|MF_ENABLED|MF_STRING,
138		      m->menuItem[i].commandID,
139		      m->menuItem[i].text);
140	  break;
141
142	case CMD_SEPARATOR:
143	  InsertMenu (hmenu,
144		      item,
145		      MF_BYPOSITION|MF_SEPARATOR,
146		      0,
147		      NULL);
148	  break;
149
150	case CMD_MENU:
151	  /* Recursive! */
152	  hsub = MakeMenu (m->menuItem[i].param, 0, 0);
153	  if (hsub)
154	    InsertMenu (hmenu,
155			item,
156			MF_BYPOSITION|MF_POPUP|MF_ENABLED|MF_STRING,
157			(UINT_PTR)hsub,
158			m->menuItem[i].text);
159	  break;
160	}
161
162      /* If item==-1 (means to add at end of menu) don't increment) */
163      if (item>=0)
164	item++;
165    }
166
167  return hmenu;
168}
169
170
171#ifdef XWIN_MULTIWINDOW
172/*
173 * Callback routine that is executed once per window class.
174 * Removes or creates custom window settings depending on LPARAM
175 */
176static wBOOL CALLBACK
177ReloadEnumWindowsProc (HWND hwnd, LPARAM lParam)
178{
179  HICON   hicon;
180  Window  wid;
181
182  if (!hwnd) {
183    ErrorF("ReloadEnumWindowsProc: hwnd==NULL!\n");
184    return FALSE;
185  }
186
187  /* It's our baby, either clean or dirty it */
188  if (lParam==FALSE)
189    {
190      hicon = (HICON)GetClassLong(hwnd, GCL_HICON);
191
192      /* Unselect any icon in the class structure */
193      SetClassLong (hwnd, GCL_HICON, (LONG)LoadIcon (NULL, IDI_APPLICATION));
194
195      /* If it's generated on-the-fly, get rid of it, will regen */
196      winDestroyIcon (hicon);
197
198      hicon = (HICON)GetClassLong(hwnd, GCL_HICONSM);
199
200      /* Unselect any icon in the class structure */
201      SetClassLong (hwnd, GCL_HICONSM, 0);
202
203      /* If it's generated on-the-fly, get rid of it, will regen */
204      winDestroyIcon (hicon);
205
206      /* Remove any menu additions, use bRevert flag */
207      GetSystemMenu (hwnd, TRUE);
208
209      /* This window is now clean of our taint */
210    }
211  else
212    {
213      /* Make the icon default, dynamic, or from xwinrc */
214      SetClassLong (hwnd, GCL_HICON, (LONG)g_hIconX);
215      SetClassLong (hwnd, GCL_HICONSM, (LONG)g_hSmallIconX);
216      wid = (Window)GetProp (hwnd, WIN_WID_PROP);
217      if (wid)
218	winUpdateIcon (wid);
219      /* Update the system menu for this window */
220      SetupSysMenu ((unsigned long)hwnd);
221
222      /* That was easy... */
223    }
224
225  return TRUE;
226}
227#endif
228
229
230/*
231 * Removes any custom icons in classes, custom menus, etc.
232 * Frees all members in pref structure.
233 * Reloads the preferences file.
234 * Set custom icons and menus again.
235 */
236static void
237ReloadPrefs (void)
238{
239  int i;
240
241#ifdef XWIN_MULTIWINDOW
242  /* First, iterate over all windows replacing their icon with system */
243  /* default one and deleting any custom system menus                 */
244  EnumThreadWindows (g_dwCurrentThreadID, ReloadEnumWindowsProc, FALSE);
245#endif
246
247  /* Now, free/clear all info from our prefs structure */
248  for (i=0; i<pref.menuItems; i++)
249    free (pref.menu[i].menuItem);
250  free (pref.menu);
251  pref.menu = NULL;
252  pref.menuItems = 0;
253
254  pref.rootMenuName[0] = 0;
255
256  free (pref.sysMenu);
257  pref.sysMenuItems = 0;
258
259  pref.defaultSysMenuName[0] = 0;
260  pref.defaultSysMenuPos = 0;
261
262  pref.iconDirectory[0] = 0;
263  pref.defaultIconName[0] = 0;
264  pref.trayIconName[0] = 0;
265
266  for (i=0; i<pref.iconItems; i++)
267    if (pref.icon[i].hicon)
268      DestroyIcon ((HICON)pref.icon[i].hicon);
269  free (pref.icon);
270  pref.icon = NULL;
271  pref.iconItems = 0;
272
273  /* Free global default X icon */
274  if (g_hIconX)
275    DestroyIcon (g_hIconX);
276  if (g_hSmallIconX)
277    DestroyIcon (g_hSmallIconX);
278
279  /* Reset the custom command IDs */
280  g_cmdid = STARTMENUID;
281
282  /* Load the updated resource file */
283  LoadPreferences();
284
285  g_hIconX = NULL;
286  g_hSmallIconX = NULL;
287
288#ifdef XWIN_MULTIWINDOW
289  winInitGlobalIcons();
290#endif
291
292#ifdef XWIN_MULTIWINDOW
293  /* Rebuild the icons and menus */
294  EnumThreadWindows (g_dwCurrentThreadID, ReloadEnumWindowsProc, TRUE);
295#endif
296
297  /* Whew, done */
298}
299
300/*
301 * Check/uncheck the ALWAYSONTOP items in this menu
302 */
303void
304HandleCustomWM_INITMENU(unsigned long hwndIn,
305			unsigned long hmenuIn)
306{
307  HWND    hwnd;
308  HMENU   hmenu;
309  DWORD   dwExStyle;
310  int     i, j;
311
312  hwnd = (HWND)hwndIn;
313  hmenu = (HMENU)hmenuIn;
314  if (!hwnd || !hmenu)
315    return;
316
317  if (GetWindowLong (hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)
318    dwExStyle = MF_BYCOMMAND | MF_CHECKED;
319  else
320    dwExStyle = MF_BYCOMMAND | MF_UNCHECKED;
321
322  for (i=0; i<pref.menuItems; i++)
323    for (j=0; j<pref.menu[i].menuItems; j++)
324      if (pref.menu[i].menuItem[j].cmd==CMD_ALWAYSONTOP)
325	CheckMenuItem (hmenu, pref.menu[i].menuItem[j].commandID, dwExStyle );
326
327}
328
329/*
330 * Searches for the custom WM_COMMAND command ID and performs action.
331 * Return TRUE if command is proccessed, FALSE otherwise.
332 */
333Bool
334HandleCustomWM_COMMAND (unsigned long hwndIn,
335			int           command)
336{
337  HWND hwnd;
338  int i, j;
339  MENUPARSED *m;
340  DWORD			dwExStyle;
341
342  hwnd = (HWND)hwndIn;
343
344  if (!command)
345    return FALSE;
346
347  for (i=0; i<pref.menuItems; i++)
348    {
349      m = &(pref.menu[i]);
350      for (j=0; j<m->menuItems; j++)
351	{
352	  if (command==m->menuItem[j].commandID)
353	    {
354	      /* Match! */
355	      switch(m->menuItem[j].cmd)
356		{
357#ifdef __CYGWIN__
358		case CMD_EXEC:
359		  if (fork()==0)
360		    {
361		      struct rlimit rl;
362		      unsigned long i;
363
364		      /* Close any open descriptors except for STD* */
365		      getrlimit (RLIMIT_NOFILE, &rl);
366		      for (i = STDERR_FILENO+1; i < rl.rlim_cur; i++)
367			close(i);
368
369		      /* Disassociate any TTYs */
370		      setsid();
371
372		      execl ("/bin/sh",
373			     "/bin/sh",
374			     "-c",
375			     m->menuItem[j].param,
376			     NULL);
377		      exit (0);
378		    }
379		  else
380		    return TRUE;
381		  break;
382#else
383		case CMD_EXEC:
384                  {
385		    /* Start process without console window */
386		    STARTUPINFO start;
387		    PROCESS_INFORMATION child;
388
389		    memset (&start, 0, sizeof (start));
390		    start.cb = sizeof (start);
391		    start.dwFlags = STARTF_USESHOWWINDOW;
392		    start.wShowWindow = SW_HIDE;
393
394		    memset (&child, 0, sizeof (child));
395
396		    if (CreateProcess (NULL, m->menuItem[j].param, NULL, NULL, FALSE, 0,
397				       NULL, NULL, &start, &child))
398		    {
399			CloseHandle (child.hThread);
400			CloseHandle (child.hProcess);
401		    }
402		    else
403			MessageBox(NULL, m->menuItem[j].param, "Mingrc Exec Command Error!", MB_OK | MB_ICONEXCLAMATION);
404                  }
405		  return TRUE;
406#endif
407		case CMD_ALWAYSONTOP:
408		  if (!hwnd)
409		    return FALSE;
410
411		  /* Get extended window style */
412		  dwExStyle = GetWindowLong (hwnd, GWL_EXSTYLE);
413
414		  /* Handle topmost windows */
415		  if (dwExStyle & WS_EX_TOPMOST)
416		    SetWindowPos (hwnd,
417				  HWND_NOTOPMOST,
418				  0, 0,
419				  0, 0,
420				  SWP_NOSIZE | SWP_NOMOVE);
421		  else
422		    SetWindowPos (hwnd,
423				  HWND_TOPMOST,
424				  0, 0,
425				  0, 0,
426				  SWP_NOSIZE | SWP_NOMOVE);
427#if XWIN_MULTIWINDOW
428		  /* Reflect the changed Z order */
429		  winReorderWindowsMultiWindow ();
430#endif
431		  return TRUE;
432
433		case CMD_RELOAD:
434		  ReloadPrefs();
435		  return TRUE;
436
437		default:
438		  return FALSE;
439	      }
440	    } /* match */
441	} /* for j */
442    } /* for i */
443
444  return FALSE;
445}
446
447
448#ifdef XWIN_MULTIWINDOW
449/*
450 * Add the default or a custom menu depending on the class match
451 */
452void
453SetupSysMenu (unsigned long hwndIn)
454{
455  HWND    hwnd;
456  HMENU	  sys;
457  int     i;
458  WindowPtr pWin;
459  char *res_name, *res_class;
460
461  hwnd = (HWND)hwndIn;
462  if (!hwnd)
463    return;
464
465  pWin = GetProp (hwnd, WIN_WINDOW_PROP);
466
467  sys = GetSystemMenu (hwnd, FALSE);
468  if (!sys)
469    return;
470
471  if (pWin)
472    {
473      /* First see if there's a class match... */
474      if (winMultiWindowGetClassHint (pWin, &res_name, &res_class))
475	{
476	  for (i=0; i<pref.sysMenuItems; i++)
477	    {
478	      if (!strcmp(pref.sysMenu[i].match, res_name) ||
479		  !strcmp(pref.sysMenu[i].match, res_class) )
480		{
481		  free(res_name);
482		  free(res_class);
483
484		  MakeMenu (pref.sysMenu[i].menuName, sys,
485			    pref.sysMenu[i].menuPos==AT_START?0:-1);
486		  return;
487		}
488	    }
489
490	  /* No match, just free alloc'd strings */
491	  free(res_name);
492	  free(res_class);
493	} /* Found wm_class */
494    } /* if pwin */
495
496  /* Fallback to system default */
497  if (pref.defaultSysMenuName[0])
498    {
499      if (pref.defaultSysMenuPos==AT_START)
500	MakeMenu (pref.defaultSysMenuName, sys, 0);
501      else
502	MakeMenu (pref.defaultSysMenuName, sys, -1);
503    }
504}
505#endif
506
507
508/*
509 * Possibly add a menu to the toolbar icon
510 */
511void
512SetupRootMenu (unsigned long hmenuRoot)
513{
514  HMENU root;
515
516  root = (HMENU)hmenuRoot;
517  if (!root)
518    return;
519
520  if (pref.rootMenuName[0])
521    {
522      MakeMenu(pref.rootMenuName, root, 0);
523    }
524}
525
526
527/*
528 * Check for and return an overridden default ICON specified in the prefs
529 */
530unsigned long
531winOverrideDefaultIcon(int size)
532{
533  HICON hicon;
534
535  if (pref.defaultIconName[0])
536    {
537      hicon = LoadImageComma (pref.defaultIconName, size, size, 0);
538      if (hicon==NULL)
539        ErrorF ("winOverrideDefaultIcon: LoadImageComma(%s) failed\n",
540		pref.defaultIconName);
541
542      return (unsigned long)hicon;
543    }
544
545  return 0;
546}
547
548
549/*
550 * Return the HICON to use in the taskbar notification area
551 */
552unsigned long
553winTaskbarIcon(void)
554{
555  HICON hicon;
556
557  hicon = 0;
558  /* First try and load an overridden, if success then return it */
559  if (pref.trayIconName[0])
560    {
561      hicon = LoadImageComma (pref.trayIconName,
562			      GetSystemMetrics (SM_CXSMICON),
563			      GetSystemMetrics (SM_CYSMICON),
564			      0 );
565    }
566
567  /* Otherwise return the default */
568  if (!hicon)
569    hicon =  (HICON) LoadImage (g_hInstance,
570				MAKEINTRESOURCE(IDI_XWIN),
571				IMAGE_ICON,
572				GetSystemMetrics (SM_CXSMICON),
573				GetSystemMetrics (SM_CYSMICON),
574				0);
575
576  return (unsigned long)hicon;
577}
578
579
580/*
581 * Parse a filename to extract an icon:
582 *  If fname is exactly ",nnn" then extract icon from our resource
583 *  else if it is "file,nnn" then extract icon nnn from that file
584 *  else try to load it as an .ico file and if that fails return NULL
585 */
586static HICON
587LoadImageComma (char *fname, int sx, int sy, int flags)
588{
589  HICON  hicon;
590  int    index;
591  char   file[PATH_MAX+NAME_MAX+2];
592
593  /* Some input error checking */
594  if (!fname || !fname[0])
595    return NULL;
596
597  index = 0;
598  hicon = NULL;
599
600  if (fname[0]==',')
601    {
602      /* It's the XWIN.EXE resource they want */
603      index = atoi (fname+1);
604      hicon = LoadImage (g_hInstance,
605                        MAKEINTRESOURCE(index),
606                        IMAGE_ICON,
607                        sx,
608                        sy,
609                        flags);
610    }
611  else
612    {
613      file[0] = 0;
614      /* Prepend path if not given a "X:\" filename */
615      if ( !(fname[0] && fname[1]==':' && fname[2]=='\\') )
616        {
617         strcpy (file, pref.iconDirectory);
618         if (pref.iconDirectory[0])
619           if (fname[strlen(fname)-1]!='\\')
620             strcat (file, "\\");
621        }
622      strcat (file, fname);
623
624      if (strrchr (file, ','))
625       {
626         /* Specified as <fname>,<index> */
627
628         *(strrchr (file, ',')) = 0; /* End string at comma */
629         index = atoi (strrchr (fname, ',') + 1);
630         hicon = ExtractIcon (g_hInstance, file, index);
631       }
632      else
633       {
634         /* Just an .ico file... */
635
636         hicon = (HICON)LoadImage (NULL,
637                                   file,
638                                   IMAGE_ICON,
639                                   sx,
640                                   sy,
641                                   LR_LOADFROMFILE|flags);
642       }
643    }
644  return hicon;
645}
646
647/*
648 * Check for a match of the window class to one specified in the
649 * ICONS{} section in the prefs file, and load the icon from a file
650 */
651unsigned long
652winOverrideIcon (unsigned long longWin)
653{
654  WindowPtr pWin = (WindowPtr) longWin;
655  char *res_name, *res_class;
656  int i;
657  HICON hicon;
658  char *wmName;
659
660  if (pWin==NULL)
661    return 0;
662
663  /* If we can't find the class, we can't override from default! */
664  if (!winMultiWindowGetClassHint (pWin, &res_name, &res_class))
665    return 0;
666
667  winMultiWindowGetWMName (pWin, &wmName);
668
669  for (i=0; i<pref.iconItems; i++) {
670    if (!strcmp(pref.icon[i].match, res_name) ||
671	!strcmp(pref.icon[i].match, res_class) ||
672	(wmName && strstr(wmName, pref.icon[i].match)))
673      {
674	free (res_name);
675	free (res_class);
676	if (wmName)
677	  free (wmName);
678
679	if (pref.icon[i].hicon)
680	  return pref.icon[i].hicon;
681
682       hicon = LoadImageComma (pref.icon[i].iconFile, 0, 0, LR_DEFAULTSIZE);
683       if (hicon==NULL)
684         ErrorF ("winOverrideIcon: LoadImageComma(%s) failed\n",
685                  pref.icon[i].iconFile);
686
687	pref.icon[i].hicon = (unsigned long)hicon;
688	return (unsigned long)hicon;
689      }
690  }
691
692  /* Didn't find the icon, fail gracefully */
693  free (res_name);
694  free (res_class);
695  if (wmName)
696    free (wmName);
697
698  return 0;
699}
700
701
702/*
703 * Should we free this icon or leave it in memory (is it part of our
704 * ICONS{} overrides)?
705 */
706int
707winIconIsOverride(unsigned hiconIn)
708{
709  HICON hicon;
710  int i;
711
712  hicon = (HICON)hiconIn;
713
714  if (!hicon)
715    return 0;
716
717  for (i=0; i<pref.iconItems; i++)
718    if ((HICON)pref.icon[i].hicon == hicon)
719      return 1;
720
721  return 0;
722}
723
724
725
726/*
727 * Try and open ~/.XWinrc and /usr/X11R6/lib/X11/system.XWinrc
728 * Load it into prefs structure for use by other functions
729 */
730void
731LoadPreferences ()
732{
733  char *home;
734  char fname[PATH_MAX+NAME_MAX+2];
735  FILE *prefFile;
736  char szDisplay[512];
737  char *szEnvDisplay;
738  int i, j;
739  char param[PARAM_MAX+1];
740  char *srcParam, *dstParam;
741
742  /* First, clear all preference settings */
743  memset (&pref, 0, sizeof(pref));
744  prefFile = NULL;
745
746  /* Now try and find a ~/.xwinrc file */
747  home = getenv ("HOME");
748  if (home)
749    {
750      strcpy (fname, home);
751      if (fname[strlen(fname)-1]!='/')
752	strcat (fname, "/");
753      strcat (fname, ".XWinrc");
754
755      prefFile = fopen (fname, "r");
756      if (prefFile)
757	ErrorF ("winPrefsLoadPreferences: %s\n", fname);
758    }
759
760  /* No home file found, check system default */
761  if (!prefFile)
762    {
763      char buffer[MAX_PATH];
764#ifdef RELOCATE_PROJECTROOT
765      snprintf(buffer, sizeof(buffer), "%s\\system.XWinrc", winGetBaseDir());
766#else
767      strncpy(buffer, PROJECTROOT"/lib/X11/system.XWinrc", sizeof(buffer));
768#endif
769      buffer[sizeof(buffer)-1] = 0;
770      prefFile = fopen (buffer, "r");
771      if (prefFile)
772	ErrorF ("winPrefsLoadPreferences: %s\n", buffer);
773    }
774
775  /* If we could open it, then read the settings and close it */
776  if (prefFile)
777    {
778      parse_file (prefFile);
779      fclose (prefFile);
780    }
781
782  /* Setup a DISPLAY environment variable, need to allocate on heap */
783  /* because putenv doesn't copy the argument... */
784  snprintf (szDisplay, 512, "DISPLAY=127.0.0.1:%s.0", display);
785  szEnvDisplay = (char *)(malloc (strlen(szDisplay)+1));
786  if (szEnvDisplay)
787    {
788      strcpy (szEnvDisplay, szDisplay);
789      putenv (szEnvDisplay);
790    }
791
792  /* Replace any "%display%" in menu commands with display string */
793  snprintf (szDisplay, 512, "127.0.0.1:%s.0", display);
794  for (i=0; i<pref.menuItems; i++)
795    {
796      for (j=0; j<pref.menu[i].menuItems; j++)
797	{
798	  if (pref.menu[i].menuItem[j].cmd==CMD_EXEC)
799	    {
800	      srcParam = pref.menu[i].menuItem[j].param;
801	      dstParam = param;
802	      while (*srcParam) {
803		if (!strncmp(srcParam, "%display%", 9))
804		  {
805		    memcpy (dstParam, szDisplay, strlen(szDisplay));
806		    dstParam += strlen(szDisplay);
807		    srcParam += 9;
808		  }
809		else
810		  {
811		    *dstParam = *srcParam;
812		    dstParam++;
813		    srcParam++;
814		  }
815	      }
816	      *dstParam = 0;
817	      strcpy (pref.menu[i].menuItem[j].param, param);
818	    } /* cmd==cmd_exec */
819	} /* for all menuitems */
820    } /* for all menus */
821
822}
823