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