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 
21 static char *m4_defs(Display *display, const char *host);
22 
23 
24 /*
25  * Primary entry point to do m4 parsing of a startup file
26  */
27 FILE *
start_m4(FILE * fraw)28 start_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  */
90 static char *
m4_defs(Display * display,const char * host)91 m4_defs(Display *display, const char *host)
92 {
93 	Screen *screen;
94 	Visual *visual;
95 	char client[MAXHOSTNAME];
96 	char *vc, *color;
97 	char *tmp_name;
98 	FILE *tmpf;
99 	char *user;
100 
101 	/* Create temp file */
102 	{
103 		char *td = getenv("TMPDIR");
104 		if(!td || strlen(td) < 2 || *td != '/') {
105 			td = "/tmp";
106 		}
107 		asprintf(&tmp_name, "%s/ctwmrc.XXXXXXXX", td);
108 		if(!tmp_name) {
109 			perror("asprintf failed in m4_defs");
110 			exit(1);
111 		}
112 
113 		int fd = mkstemp(tmp_name);
114 		if(fd < 0) {
115 			perror("mkstemp failed in m4_defs");
116 			exit(377);
117 		}
118 		tmpf = fdopen(fd, "w+");
119 	}
120 
121 
122 	/*
123 	 * Now start writing the defs into it.
124 	 */
125 #define WR_DEF(k, v) fprintf(tmpf, "define(`%s', `%s')\n", (k), (v))
126 #define WR_NUM(k, v) fprintf(tmpf, "define(`%s', `%d')\n", (k), (v))
127 
128 	/*
129 	 * The machine running the window manager process (and, presumably,
130 	 * most of the other clients the user is running)
131 	 */
132 	if(gethostname(client, MAXHOSTNAME) < 0) {
133 		perror("gethostname failed in m4_defs");
134 		exit(1);
135 	}
136 	WR_DEF("CLIENTHOST", client);
137 
138 	/*
139 	 * A guess at the machine running the X server.  We take the full
140 	 * $DISPLAY and chop off the screen specification.
141 	 */
142 	{
143 		char *server, *colon;
144 
145 		server = strdup(XDisplayName(host));
146 		if(!server) {
147 			server = strdup("unknown");
148 		}
149 		colon = strchr(server, ':');
150 		if(colon != NULL) {
151 			*colon = '\0';
152 		}
153 
154 		/* :0 or unix socket connection means it's the same as CLIENTHOST */
155 		if((server[0] == '\0') || (!strcmp(server, "unix"))) {
156 			free(server);
157 			server = strdup(client);
158 		}
159 		WR_DEF("SERVERHOST", server);
160 
161 		free(server);
162 	}
163 
164 #ifdef HISTORICAL_HOSTNAME_IMPL
165 	/*
166 	 * Historical attempt to use DNS to figure a canonical name.  This is
167 	 * left inside this #ifdef for easy restoration if somebody finds a
168 	 * need; enabling it is not supported or documented.  Unless somebody
169 	 * comes up with a good reason to revive it, it will be removed after
170 	 * 4.0.2.
171 	 */
172 	{
173 		struct hostent *hostname = gethostbyname(client);
174 		if(hostname) {
175 			WR_DEF("HOSTNAME", hostname->h_name);
176 		}
177 		else {
178 			WR_DEF("HOSTNAME", client);
179 		}
180 	}
181 #else
182 	/*
183 	 * Just leave HOSTNAME as a copy of CLIENTHOST for backward
184 	 * compat.
185 	 */
186 	WR_DEF("HOSTNAME", client);
187 #endif
188 
189 	/*
190 	 * Info about the user and their environment
191 	 */
192 	if(!(user = getenv("USER")) && !(user = getenv("LOGNAME"))) {
193 		struct passwd *pwd = getpwuid(getuid());
194 		if(pwd) {
195 			user = pwd->pw_name;
196 		}
197 	}
198 	if(!user) {
199 		user = "unknown";
200 	}
201 	WR_DEF("USER", user);
202 	WR_DEF("HOME", Home);
203 
204 	/*
205 	 * ctwm meta
206 	 */
207 	WR_DEF("TWM_TYPE", "ctwm");
208 	WR_DEF("TWM_VERSION", VersionNumber);
209 	WR_DEF("CTWM_VERSION_MAJOR", VersionNumber_major);
210 	WR_DEF("CTWM_VERSION_MINOR", VersionNumber_minor);
211 	WR_DEF("CTWM_VERSION_PATCH", VersionNumber_patch);
212 	WR_DEF("CTWM_VERSION_ADDL",  VersionNumber_addl);
213 
214 	/*
215 	 * X server meta
216 	 */
217 	WR_NUM("VERSION", ProtocolVersion(display));
218 	WR_NUM("REVISION", ProtocolRevision(display));
219 	WR_DEF("VENDOR", ServerVendor(display));
220 	WR_NUM("RELEASE", VendorRelease(display));
221 
222 	/*
223 	 * Information about the display
224 	 */
225 	screen = ScreenOfDisplay(display, Scr->screen);
226 	visual = DefaultVisualOfScreen(screen);
227 	WR_NUM("WIDTH", screen->width);
228 	WR_NUM("HEIGHT", screen->height);
229 #define Resolution(pixels, mm)  ((((pixels) * 100000 / (mm)) + 50) / 100)
230 	WR_NUM("X_RESOLUTION", Resolution(screen->width, screen->mwidth));
231 	WR_NUM("Y_RESOLUTION", Resolution(screen->height, screen->mheight));
232 #undef Resolution
233 	WR_NUM("PLANES", DisplayPlanes(display, Scr->screen));
234 	WR_NUM("BITS_PER_RGB", visual->bits_per_rgb);
235 	color = "Yes";
236 	switch(visual->class) {
237 		case(StaticGray):
238 			vc = "StaticGray";
239 			color = "No";
240 			break;
241 		case(GrayScale):
242 			vc = "GrayScale";
243 			color = "No";
244 			break;
245 		case(StaticColor):
246 			vc = "StaticColor";
247 			break;
248 		case(PseudoColor):
249 			vc = "PseudoColor";
250 			break;
251 		case(TrueColor):
252 			vc = "TrueColor";
253 			break;
254 		case(DirectColor):
255 			vc = "DirectColor";
256 			break;
257 		default:
258 			vc = "NonStandard";
259 			break;
260 	}
261 	WR_DEF("CLASS", vc);
262 	WR_DEF("COLOR", color);
263 
264 	/*
265 	 * Bits of "how this ctwm invocation is being run" data
266 	 */
267 	if(CLarg.is_captive && Scr->captivename) {
268 		WR_DEF("TWM_CAPTIVE", "Yes");
269 		WR_DEF("TWM_CAPTIVE_NAME", Scr->captivename);
270 	}
271 	else {
272 		WR_DEF("TWM_CAPTIVE", "No");
273 	}
274 
275 	/*
276 	 * Various compile-time options.
277 	 */
278 #ifdef PIXMAP_DIRECTORY
279 	WR_DEF("PIXMAP_DIRECTORY", PIXMAP_DIRECTORY);
280 #endif
281 #ifdef XPM
282 	WR_DEF("XPM", "Yes");
283 #endif
284 #ifdef JPEG
285 	WR_DEF("JPEG", "Yes");
286 #endif
287 #ifdef SOUNDS
288 	WR_DEF("SOUNDS", "Yes");
289 #endif
290 #ifdef EWMH
291 	WR_DEF("EWMH", "Yes");
292 #endif
293 	/* Since this is no longer an option, it should be removed in the future */
294 	WR_DEF("I18N", "Yes");
295 
296 #undef WR_NUM
297 #undef WR_DEF
298 
299 
300 	/*
301 	 * We might be keeping it, in which case tell the user where it is;
302 	 * this is mostly a debugging option.  Otherwise, delete it by
303 	 * telling m4 to do so when it reads it; this is fairly fugly, and I
304 	 * have more than half a mind to dike it out and properly clean up
305 	 * ourselves.
306 	 */
307 	if(CLarg.KeepTmpFile) {
308 		fprintf(stderr, "Left file: %s\n", tmp_name);
309 	}
310 	else {
311 		fprintf(tmpf, "syscmd(/bin/rm %s)\n", tmp_name);
312 	}
313 
314 
315 	/* Close out and hand it back */
316 	fclose(tmpf);
317 	return(tmp_name);
318 }
319