1 /* -*-c-*- */
2 /* This module, and the entire FvwmM4 program, and the concept for
3  * interfacing this module to the Window Manager, are all original work
4  * by Robert Nation
5  */
6 
7 /* This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see: <http://www.gnu.org/licenses/>
19  */
20 
21 #include "config.h"
22 
23 #include <stdio.h>
24 #include <signal.h>
25 #include <fcntl.h>
26 #include <sys/wait.h>
27 #include "libs/ftime.h"
28 #include <unistd.h>
29 #include <ctype.h>
30 #include <pwd.h>
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <netdb.h>
34 
35 #include <X11/Xlib.h>
36 #include <X11/Xutil.h>
37 #include <X11/Xproto.h>
38 #include <X11/Xatom.h>
39 #include <X11/Intrinsic.h>
40 
41 #include "libs/Module.h"
42 #include "libs/Strings.h"
43 #include "libs/System.h"
44 #include "libs/fvwm_sys_stat.h"
45 
46 #include "FvwmM4.h"
47 #include "libs/fvwmlib.h"
48 #include "libs/FShape.h"
49 #include "libs/PictureBase.h"
50 #include "libs/FSMlib.h"
51 #include <X11/StringDefs.h>
52 #include <X11/Shell.h>
53 #define Resolution(pixels, mm) ((((pixels) * 2000 / (mm)) + 1) / 2)
54 
55 char *MyName;
56 int fd[2];
57 
58 long Vx, Vy;
59 static char *MkDef(char *name, char *def);
60 static char *MkNum(char *name,int def);
61 static char *m4_defs(Display *display, const char *host, char *m4_options, char *config_file);
62 #define MAXHOSTNAME 255
63 #define EXTRA 56
64 
65 int  m4_enable;                 /* use m4? */
66 int  m4_prefix;                 /* Do GNU m4 prefixing (-P) */
67 int  m4_prefix_defines;         /* Add "m4_" to the names of the defines */
68 char m4_options[BUFSIZ];        /* Command line options to m4 */
69 char m4_outfile[BUFSIZ] = "";   /* The output filename for m4 */
70 char *m4_prog = "/usr/local/bin/gm4";           /* Name of the m4 program */
71 int  m4_default_quotes;         /* Use default m4 quotes */
72 char *m4_startquote = "`";         /* Left quote characters for m4 */
73 char *m4_endquote = "'";           /* Right quote characters for m4 */
74 
75 
76 /*
77  *
78  *  Procedure:
79  *      main - start of module
80  *
81  */
main(int argc,char ** argv)82 int main(int argc, char **argv)
83 {
84   Display *dpy;                 /* which display are we talking to */
85   char *temp, *s;
86   char *display_name = NULL;
87   char *filename = NULL;
88   char *tmp_file;
89   int i;
90   int debug = 0;
91   int lock = 0;
92   int noread = 0;
93   char *user_dir;
94   char *m4path;
95 
96   /* Figure out the working directory and go to it */
97   user_dir = getenv("FVWM_USERDIR");
98   if (user_dir != NULL)
99   {
100     if (chdir(user_dir) < 0)
101       fprintf(stderr, "%s: <<Warning>> chdir to %s failed in m4_defs",
102 	      MyName, user_dir);
103   }
104 
105   m4_enable = True;
106   m4_prefix = False;
107   m4_prefix_defines = False;
108   sprintf(m4_options, " ");
109   m4_default_quotes = 1;
110   /* add FVWM_DATADIR to the include path.  Can't use the -I option here because
111    * it is incompatible with the System V version of m4.  Instead, append it to
112    * the front of the M4PATH environment variable. */
113   m4path = getenv("M4PATH");
114   if (m4path == NULL)
115   {
116     m4path = safemalloc(sizeof("M4PATH=") + strlen(FVWM_DATADIR) + 1);
117     sprintf(m4path, "M4PATH=%s", FVWM_DATADIR);
118   }
119   else
120   {
121     char *s;
122 
123     s = safemalloc(
124       sizeof("M4PATH=") + strlen(FVWM_DATADIR) + strlen(m4path) + 2);
125     sprintf(s, "M4PATH=%s:%s", FVWM_DATADIR, m4path);
126     m4path = s;
127   }
128   putenv(m4path);
129 
130   /* Record the program name for error messages */
131   temp = argv[0];
132 
133   s=strrchr(argv[0], '/');
134   if (s != NULL)
135     temp = s + 1;
136 
137   MyName = safemalloc(strlen(temp)+2);
138   strcpy(MyName,"*");
139   strcat(MyName, temp);
140 
141   if(argc < 6)
142     {
143       fprintf(stderr,"%s Version %s should only be executed by fvwm!\n",MyName,
144 	      VERSION);
145       exit(1);
146     }
147 
148   /* We should exit if our fvwm pipes die */
149   signal (SIGPIPE, DeadPipe);
150 
151   fd[0] = atoi(argv[1]);
152   fd[1] = atoi(argv[2]);
153 
154   for(i=6;i<argc;i++)
155     {
156       if(strcasecmp(argv[i],"-m4-prefix") == 0)
157 	{
158 	  m4_prefix = True;
159 	}
160       else if(strcasecmp(argv[i],"-m4-prefix-defines") == 0)
161 	{
162 	  m4_prefix_defines = True;
163 	}
164       else if(strcasecmp(argv[i],"-m4opt") == 0)
165 	{
166 	  /* leaving this in just in case-- any option starting with '-'
167 	     will get passed on to m4 anyway */
168 	  strcat(m4_options, argv[++i]);
169 	  strcat(m4_options, " ");
170 	}
171       else if(strcasecmp(argv[i],"-m4-squote") == 0)
172 	{
173 	  m4_startquote = argv[++i];
174 	  m4_default_quotes = 0;
175 	}
176       else if(strcasecmp(argv[i],"-m4-equote") == 0)
177 	{
178 	  m4_endquote = argv[++i];
179 	  m4_default_quotes = 0;
180 	}
181       else if (strcasecmp(argv[i], "-m4prog") == 0)
182 	{
183 	  m4_prog = argv[++i];
184 	}
185       else if(strcasecmp(argv[i], "-outfile") == 0)
186 	{
187 	  strcpy(m4_outfile,argv[++i]);
188 	}
189       else if(strcasecmp(argv[i], "-debug") == 0)
190 	{
191 	  debug = 1;
192 	}
193       else if(strcasecmp(argv[i], "-lock") == 0)
194 	{
195 	  lock = 1;
196 	}
197       else if(strcasecmp(argv[i], "-noread") == 0)
198 	{
199 	  noread = 1;
200 	}
201       else if (strncasecmp(argv[i],"-",1) == 0)
202 	{
203 	  /* pass on any other arguments starting with '-' to m4 */
204 	  strcat(m4_options, argv[i]);
205 	  strcat(m4_options, " ");
206 	}
207       else
208 	filename = argv[i];
209     }
210 
211   if (!filename)
212   {
213     fprintf(stderr, "%s: no file specified.\n", MyName);
214     exit(1);
215   }
216   for(i=0;i<strlen(filename);i++)
217     if((filename[i] == '\n')||(filename[i] == '\r'))
218       {
219 	filename[i] = 0;
220       }
221 
222   if (!(dpy = XOpenDisplay(display_name)))
223     {
224       fprintf(stderr,"FvwmM4: can't open display %s",
225 	      XDisplayName(display_name));
226       exit (1);
227     }
228 
229   /* set up G */
230   PictureInitCMap(dpy);
231 
232   /* tell fvwm we're running if -lock is not used */
233   if (!lock)
234     SendFinishedStartupNotification(fd);
235 
236   tmp_file = m4_defs(dpy, display_name,m4_options, filename);
237 
238   if (!noread)
239   {
240     char *read_string = CatString3("Read '", tmp_file, "'");
241     SendText(fd, read_string, 0);
242   }
243 
244   /* tell fvwm to continue if -lock is used */
245   if (lock)
246     SendFinishedStartupNotification(fd);
247 
248   /* For a debugging version, we may wish to omit this part. */
249   /* I'll let some m4 advocates clean this up */
250   if (!debug)
251   {
252     char *delete_string;
253     char *delete_file = tmp_file;
254     if (tmp_file[0] != '/' && user_dir != NULL)
255     {
256       delete_file = safestrdup(CatString3(user_dir, "/", tmp_file));
257     }
258     delete_string = CatString3("Exec exec /bin/rm '", delete_file, "'");
259     SendText(fd, delete_string, 0);
260   }
261 
262   return 0;
263 }
264 
265 
266 
m4_defs(Display * display,const char * host,char * m4_options,char * config_file)267 static char *m4_defs(
268   Display *display, const char *host, char *m4_options, char *config_file)
269 {
270 	Screen *screen;
271 	Visual *visual;
272 	char client[MAXHOSTNAME], server[MAXHOSTNAME], *colon;
273 	char ostype[BUFSIZ];
274 	char options[BUFSIZ];
275 	static char tmp_name[BUFSIZ];
276 	struct hostent *hostname;
277 	char *vc;                     /* Visual Class */
278 	FILE *tmpf;
279 	struct passwd *pwent;
280 	int fd;
281 	int Mscreen;
282 
283 	/* Generate a temporary filename.  Honor the TMPDIR environment variable,
284 	   if set. Hope nobody deletes this file! */
285 
286 	if (strlen(m4_outfile) == 0)
287 	{
288 		if ((vc = getenv("TMPDIR")))
289 		{
290 			strcpy(tmp_name, vc);
291 		}
292 		else
293 		{
294 			strcpy(tmp_name, "/tmp");
295 		}
296 		strcat(tmp_name, "/fvwmrcXXXXXX");
297 		fd = fvwm_mkstemp(tmp_name);
298 		if (fd == -1)
299 		{
300 			fprintf(
301 				stderr,
302 				"[FvwmM4][m4_def] fvwm_mkstemp failed %s\n",
303 				tmp_name);
304 			exit(0377);
305 		}
306 	}
307 	else
308 	{
309 		strcpy(tmp_name, m4_outfile);
310 		/*
311 		 * check to make sure it doesn't exist already, to prevent
312 		 * security hole
313 		 */
314 		/* first try to unlink it */
315 		unlink(tmp_name);
316 		fd = open(
317 			tmp_name, O_WRONLY|O_EXCL|O_CREAT,
318 			FVWM_S_IRUSR | FVWM_S_IWUSR);
319 		if (fd < 0)
320 		{
321 			fprintf(
322 				stderr,
323 				"[FvwmM4][m4_defs] error opening file %s\n",
324 				tmp_name);
325 			exit(0377);
326 		}
327 	}
328 
329 	close(fd);
330 
331 	/*
332 	 * Create the appropriate command line to run m4, and
333 	 * open a pipe to the command.
334 	 */
335 
336 	if(m4_prefix)
337 	{
338 		sprintf(
339 			options, "%s --prefix-builtins %s > %s\n",
340 			m4_prog,
341 			m4_options, tmp_name);
342 	}
343 	else
344 	{
345 		sprintf(options, "%s  %s > %s\n",
346 			m4_prog,
347 			m4_options, tmp_name);
348 	}
349 	tmpf = popen(options, "w");
350 	if (tmpf == NULL)
351 	{
352 		fprintf(
353 			stderr,
354 			"[FvwmM4][m4_defs] Cannot open pipe to m4\n");
355 		exit(0377);
356 	}
357 
358 	gethostname(client,MAXHOSTNAME);
359 	getostype  (ostype, sizeof ostype);
360 
361 	/* Change the quoting characters, if specified */
362 
363 	if (!m4_default_quotes)
364 	{
365 		fprintf(
366 			tmpf, "%schangequote(%s, %s)%sdnl\n",
367 			(m4_prefix) ? "m4_" : "",
368 			m4_startquote, m4_endquote,
369 			(m4_prefix) ? "m4_" : "");
370 	}
371 
372 	hostname = gethostbyname(client);
373 	strcpy(server, XDisplayName(host));
374 	colon = strchr(server, ':');
375 	if (colon != NULL) *colon = '\0';
376 	if ((server[0] == '\0') || (!strcmp(server, "unix")))
377 		strcpy(server, client); /* must be connected to :0 or unix:0 */
378 
379 	/* TWM_TYPE is fvwm, for completeness */
380 
381 	fputs(MkDef("TWM_TYPE", "fvwm"), tmpf);
382 
383 	/* The machine running the X server */
384 	fputs(MkDef("SERVERHOST", server), tmpf);
385 	/* The machine running the window manager process */
386 	fputs(MkDef("CLIENTHOST", client), tmpf);
387 	if (hostname)
388 		fputs(MkDef("HOSTNAME", (char *)hostname->h_name), tmpf);
389 	else
390 		fputs(MkDef("HOSTNAME", (char *)client), tmpf);
391 
392 	fputs(MkDef("OSTYPE", ostype), tmpf);
393 
394 	pwent=getpwuid(geteuid());
395 	fputs(MkDef("USER", pwent->pw_name), tmpf);
396 
397 	fputs(MkDef("HOME", getenv("HOME")), tmpf);
398 	fputs(MkNum("VERSION", ProtocolVersion(display)), tmpf);
399 	fputs(MkNum("REVISION", ProtocolRevision(display)), tmpf);
400 	fputs(MkDef("VENDOR", ServerVendor(display)), tmpf);
401 	fputs(MkNum("RELEASE", VendorRelease(display)), tmpf);
402 
403 	Mscreen= DefaultScreen(display);
404 	fputs(MkNum("SCREEN", Mscreen), tmpf);
405 
406 	fputs(MkNum("WIDTH", DisplayWidth(display,Mscreen)), tmpf);
407 	fputs(MkNum("HEIGHT", DisplayHeight(display,Mscreen)), tmpf);
408 
409 	screen = ScreenOfDisplay(display, Mscreen);
410 	fputs(MkNum(
411 		      "X_RESOLUTION",Resolution(screen->width,screen->mwidth)),
412 	      tmpf);
413 	fputs(MkNum(
414 		      "Y_RESOLUTION",Resolution(screen->height,screen->mheight)),
415 	      tmpf);
416 	fputs(MkNum("PLANES",DisplayPlanes(display, Mscreen)), tmpf);
417 
418 	visual = DefaultVisualOfScreen(screen);
419 	fputs(MkNum("BITS_PER_RGB", visual->bits_per_rgb), tmpf);
420 
421 	switch(visual->class)
422 	{
423 	case(StaticGray):
424 		vc = "StaticGray";
425 		break;
426 	case(GrayScale):
427 		vc = "GrayScale";
428 		break;
429 	case(StaticColor):
430 		vc = "StaticColor";
431 		break;
432 	case(PseudoColor):
433 		vc = "PseudoColor";
434 		break;
435 	case(TrueColor):
436 		vc = "TrueColor";
437 		break;
438 	case(DirectColor):
439 		vc = "DirectColor";
440 		break;
441 	default:
442 		vc = "NonStandard";
443 		break;
444 	}
445 	fputs(MkDef("CLASS", vc), tmpf);
446 
447 	switch(Pvisual->class)
448 	{
449 	case(StaticGray):
450 		vc = "StaticGray";
451 		break;
452 	case(GrayScale):
453 		vc = "GrayScale";
454 		break;
455 	case(StaticColor):
456 		vc = "StaticColor";
457 		break;
458 	case(PseudoColor):
459 		vc = "PseudoColor";
460 		break;
461 	case(TrueColor):
462 		vc = "TrueColor";
463 		break;
464 	case(DirectColor):
465 		vc = "DirectColor";
466 		break;
467 	default:
468 		vc = "NonStandard";
469 		break;
470 	}
471 	fputs(MkDef("FVWM_CLASS", vc), tmpf);
472 
473 	if (visual->class != StaticGray && visual->class != GrayScale)
474 		fputs(MkDef("COLOR", "Yes"), tmpf);
475 	else
476 		fputs(MkDef("COLOR", "No"), tmpf);
477 
478 	if (Pvisual->class != StaticGray && Pvisual->class != GrayScale)
479 		fputs(MkDef("FVWM_COLOR", "Yes"), tmpf);
480 	else
481 		fputs(MkDef("FVWM_COLOR", "No"), tmpf);
482 
483 	fputs(MkDef("FVWM_VERSION", VERSION), tmpf);
484 
485 	/* Add options together */
486 	*options = '\0';
487 	if (FHaveShapeExtension)
488 		strcat(options, "SHAPE ");
489 
490 	if (XpmSupport)
491 		strcat(options, "XPM ");
492 
493 	strcat(options, "M4 ");
494 
495 	fputs(MkDef("OPTIONS", options), tmpf);
496 
497 	fputs(MkDef("FVWM_MODULEDIR", FVWM_MODULEDIR), tmpf);
498 	fputs(MkDef("FVWM_DATADIR", FVWM_DATADIR), tmpf);
499 
500 	if ((vc = getenv("FVWM_USERDIR")))
501 		fputs(MkDef("FVWM_USERDIR", vc), tmpf);
502 
503 	if (SessionSupport && (vc = getenv("SESSION_MANAGER")))
504 		fputs(MkDef("SESSION_MANAGER", vc), tmpf);
505 
506 	/*
507 	 * At this point, we've sent the definitions to m4.  Just include
508 	 * the fvwmrc file now.
509 	 */
510 
511 	fprintf(tmpf, "%sinclude(%s%s%s)\n",
512 		(m4_prefix) ? "m4_": "",
513 		m4_startquote,
514 		config_file,
515 		m4_endquote);
516 
517 	pclose(tmpf);
518 	return(tmp_name);
519 }
520 
521 
522 /*
523  *
524  *  Procedure:
525  *      SIGPIPE handler - SIGPIPE means fvwm is dying
526  *
527  */
DeadPipe(int nonsense)528 RETSIGTYPE DeadPipe(int nonsense)
529 {
530   exit(0);
531   SIGNAL_RETURN;
532 }
533 
MkDef(char * name,char * def)534 static char *MkDef(char *name, char *def)
535 {
536   char *cp = NULL;
537   int n;
538 
539   /* Get space to hold everything, if needed */
540 
541   n = EXTRA + strlen(name) + strlen(def);
542   cp = safemalloc(n);
543 
544   if (m4_prefix)
545     strcpy(cp, "m4_define(");
546   else
547     strcpy(cp, "define(");
548 
549   if (m4_prefix_defines)
550     strcat(cp, "m4_");
551   strcat(cp, name);
552 
553   /* Tack on "," and 2 sets of starting quotes */
554   strcat(cp, ",");
555   strcat(cp, m4_startquote);
556   strcat(cp, m4_startquote);
557 
558   /* The definition itself */
559   strcat(cp, def);
560 
561   /* Add 2 sets of closing quotes */
562   strcat(cp, m4_endquote);
563   strcat(cp, m4_endquote);
564 
565   /* End the definition, appropriately */
566   strcat(cp, ")");
567   if (m4_prefix)
568     strcat(cp, "m4_");
569 
570   strcat(cp, "dnl\n");
571 
572   return(cp);
573 }
574 
MkNum(char * name,int def)575 static char *MkNum(char *name,int def)
576 {
577   char num[20];
578 
579   sprintf(num, "%d", def);
580 
581   return(MkDef(name, num));
582 }
583