1/*
2 * Parser -- M4 specific routines.  Some additional stuff from parse.c
3 * should probably migrate here over time.
4 */
5
6#include "ctwm.h"
7
8#include <sys/types.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <unistd.h>
12#include <netdb.h>
13#include <pwd.h>
14
15#include "screen.h"
16#include "parse.h"
17#include "parse_int.h"
18#include "version.h"
19
20
21static char *m4_defs(Display *display, const char *host);
22
23
24/*
25 * Primary entry point to do m4 parsing of a startup file
26 */
27FILE *
28start_m4(FILE *fraw)
29{
30	int fids[2];
31	int fres;
32	char *defs_file;
33
34	/* Write our our standard definitions into a temp file */
35	defs_file = m4_defs(dpy, CLarg.display_name);
36
37	/* We'll read back m4's output over a pipe */
38	pipe(fids);
39
40	/* Fork off m4 as a child */
41	fres = fork();
42	if(fres < 0) {
43		perror("Fork for " M4CMD " failed");
44		unlink(defs_file);
45		free(defs_file);
46		exit(23);
47	}
48
49	/*
50	 * Child: setup and spawn m4, and have it write its output into one
51	 * end of our pipe.
52	 */
53	if(fres == 0) {
54		/* Setup file descriptors */
55		close(0);               /* stdin */
56		close(1);               /* stdout */
57		dup2(fileno(fraw), 0);  /* stdin = fraw */
58		dup2(fids[1], 1);       /* stdout = pipe to parent */
59
60		/*
61		 * Kick off m4, telling it both our file of definitions, and
62		 * stdin (dup of the .[c]twmrc file descriptor above) as input.
63		 * It writes to stdout (one end of our pipe).
64		 */
65		execlp(M4CMD, M4CMD, "-s", defs_file, "-", NULL);
66
67		/* If we get here we are screwed... */
68		perror("Can't execlp() " M4CMD);
69		unlink(defs_file);
70		free(defs_file);
71		exit(124);
72	}
73
74	/*
75	 * Else we're the parent; hand back our reading end of the pipe.
76	 */
77	close(fids[1]);
78	free(defs_file);
79	return (fdopen(fids[0], "r"));
80}
81
82
83/* Technically should sysconf() this, but good enough for our purposes */
84#define MAXHOSTNAME 255
85
86/*
87 * Writes out a temp file of all the m4 defs appropriate for this run,
88 * and returns the file name
89 */
90static char *
91m4_defs(Display *display, const char *host)
92{
93	char client[MAXHOSTNAME];
94	char *vc, *color;
95	char *tmp_name;
96	FILE *tmpf;
97	char *user;
98
99	/* Create temp file */
100	{
101		char *td = getenv("TMPDIR");
102		if(!td || strlen(td) < 2 || *td != '/') {
103			td = "/tmp";
104		}
105		asprintf(&tmp_name, "%s/ctwmrc.XXXXXXXX", td);
106		if(!tmp_name) {
107			perror("asprintf failed in m4_defs");
108			exit(1);
109		}
110
111		int fd = mkstemp(tmp_name);
112		if(fd < 0) {
113			perror("mkstemp failed in m4_defs");
114			exit(377);
115		}
116		tmpf = fdopen(fd, "w+");
117	}
118
119
120	/*
121	 * Now start writing the defs into it.
122	 */
123#define WR_DEF(k, v) fprintf(tmpf, "define(`%s', `%s')\n", (k), (v))
124#define WR_NUM(k, v) fprintf(tmpf, "define(`%s', `%d')\n", (k), (v))
125
126	/*
127	 * The machine running the window manager process (and, presumably,
128	 * most of the other clients the user is running)
129	 */
130	if(gethostname(client, MAXHOSTNAME) < 0) {
131		perror("gethostname failed in m4_defs");
132		exit(1);
133	}
134	WR_DEF("CLIENTHOST", client);
135
136	/*
137	 * A guess at the machine running the X server.  We take the full
138	 * $DISPLAY and chop off the screen specification.
139	 */
140	{
141		char *server, *colon;
142
143		server = strdup(XDisplayName(host));
144		if(!server) {
145			server = strdup("unknown");
146		}
147		colon = strchr(server, ':');
148		if(colon != NULL) {
149			*colon = '\0';
150		}
151
152		/* :0 or unix socket connection means it's the same as CLIENTHOST */
153		if((server[0] == '\0') || (!strcmp(server, "unix"))) {
154			free(server);
155			server = strdup(client);
156		}
157		WR_DEF("SERVERHOST", server);
158
159		free(server);
160	}
161
162#ifdef HISTORICAL_HOSTNAME_IMPL
163	/*
164	 * Historical attempt to use DNS to figure a canonical name.  This is
165	 * left inside this #ifdef for easy restoration if somebody finds a
166	 * need; enabling it is not supported or documented.  Unless somebody
167	 * comes up with a good reason to revive it, it will be removed after
168	 * 4.0.2.
169	 */
170	{
171		struct hostent *hostname = gethostbyname(client);
172		if(hostname) {
173			WR_DEF("HOSTNAME", hostname->h_name);
174		}
175		else {
176			WR_DEF("HOSTNAME", client);
177		}
178	}
179#else
180	/*
181	 * Just leave HOSTNAME as a copy of CLIENTHOST for backward
182	 * compat.
183	 */
184	WR_DEF("HOSTNAME", client);
185#endif
186
187	/*
188	 * Info about the user and their environment
189	 */
190	if(!(user = getenv("USER")) && !(user = getenv("LOGNAME"))) {
191		struct passwd *pwd = getpwuid(getuid());
192		if(pwd) {
193			user = pwd->pw_name;
194		}
195	}
196	if(!user) {
197		user = "unknown";
198	}
199	WR_DEF("USER", user);
200	WR_DEF("HOME", Home);
201
202	/*
203	 * ctwm meta
204	 */
205	WR_DEF("TWM_TYPE", "ctwm");
206	WR_DEF("TWM_VERSION", VersionNumber);
207	WR_DEF("CTWM_VERSION_MAJOR", VersionNumber_major);
208	WR_DEF("CTWM_VERSION_MINOR", VersionNumber_minor);
209	WR_DEF("CTWM_VERSION_PATCH", VersionNumber_patch);
210	WR_DEF("CTWM_VERSION_ADDL",  VersionNumber_addl);
211
212	/*
213	 * X server meta
214	 */
215	if(display) {
216		WR_NUM("VERSION", ProtocolVersion(display));
217		WR_NUM("REVISION", ProtocolRevision(display));
218		WR_DEF("VENDOR", ServerVendor(display));
219		WR_NUM("RELEASE", VendorRelease(display));
220	}
221	else {
222		// Standin numbers
223		WR_NUM("VERSION", 11);
224		WR_NUM("REVISION", 0);
225		WR_DEF("VENDOR", "Your Friendly Local Ctwm");
226		WR_NUM("RELEASE", 123456789);
227	}
228
229	/*
230	 * Information about the display
231	 */
232	WR_NUM("WIDTH", Scr->rootw);
233	WR_NUM("HEIGHT", Scr->rooth);
234#define Resolution(pixels, mm)  ((((pixels) * 100000 / (mm)) + 50) / 100)
235	WR_NUM("X_RESOLUTION", Resolution(Scr->rootw, Scr->mm_w));
236	WR_NUM("Y_RESOLUTION", Resolution(Scr->rooth, Scr->mm_h));
237#undef Resolution
238	WR_NUM("PLANES", Scr->d_depth);
239	WR_NUM("BITS_PER_RGB", Scr->d_visual->bits_per_rgb);
240	color = "Yes";
241	switch(Scr->d_visual->class) {
242		case(StaticGray):
243			vc = "StaticGray";
244			color = "No";
245			break;
246		case(GrayScale):
247			vc = "GrayScale";
248			color = "No";
249			break;
250		case(StaticColor):
251			vc = "StaticColor";
252			break;
253		case(PseudoColor):
254			vc = "PseudoColor";
255			break;
256		case(TrueColor):
257			vc = "TrueColor";
258			break;
259		case(DirectColor):
260			vc = "DirectColor";
261			break;
262		default:
263			vc = "NonStandard";
264			break;
265	}
266	WR_DEF("CLASS", vc);
267	WR_DEF("COLOR", color);
268
269	/*
270	 * Bits of "how this ctwm invocation is being run" data
271	 */
272	if(0) {
273		// Dummy
274	}
275#ifdef CAPTIVE
276	else if(CLarg.is_captive && Scr->captivename) {
277		WR_DEF("TWM_CAPTIVE", "Yes");
278		WR_DEF("TWM_CAPTIVE_NAME", Scr->captivename);
279	}
280#endif
281	else {
282		WR_DEF("TWM_CAPTIVE", "No");
283	}
284
285	/*
286	 * Various compile-time options.
287	 */
288#ifdef PIXMAP_DIRECTORY
289	WR_DEF("PIXMAP_DIRECTORY", PIXMAP_DIRECTORY);
290#endif
291#ifdef XPM
292	WR_DEF("XPM", "Yes");
293#endif
294#ifdef JPEG
295	WR_DEF("JPEG", "Yes");
296#endif
297#ifdef SOUNDS
298	WR_DEF("SOUNDS", "Yes");
299#endif
300#ifdef EWMH
301	WR_DEF("EWMH", "Yes");
302#endif
303#ifdef XRANDR
304	WR_DEF("XRANDR", "Yes");
305#endif
306	/* Since this is no longer an option, it should be removed in the future */
307	WR_DEF("I18N", "Yes");
308
309#undef WR_NUM
310#undef WR_DEF
311
312
313	/*
314	 * We might be keeping it, in which case tell the user where it is;
315	 * this is mostly a debugging option.  Otherwise, delete it by
316	 * telling m4 to do so when it reads it; this is fairly fugly, and I
317	 * have more than half a mind to dike it out and properly clean up
318	 * ourselves.
319	 */
320	if(CLarg.KeepTmpFile) {
321		fprintf(stderr, "Left file: %s\n", tmp_name);
322	}
323	else {
324		fprintf(tmpf, "syscmd(/bin/rm %s)\n", tmp_name);
325	}
326
327
328	/* Close out and hand it back */
329	fclose(tmpf);
330	return(tmp_name);
331}
332