~ctwm/ctwm/trunk

387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
1
/*
2
 * Parser -- M4 specific routines.  Some additional stuff from parse.c
3
 * should probably migrate here over time.
4
 */
5
395.1.1 by Matthew Fuller
Move ctwm.h to always be included first.
6
#include "ctwm.h"
7
429.1.19 by Matthew Fuller
Try a little harder to figure out the username.
8
#include <sys/types.h>
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
9
#include <stdio.h>
429.1.1 by Matthew Fuller
mkstemp() is technically in stdlib.h; we're getting it via pollution
10
#include <stdlib.h>
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
11
#include <unistd.h>
12
#include <netdb.h>
429.1.19 by Matthew Fuller
Try a little harder to figure out the username.
13
#include <pwd.h>
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
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, 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
{
29
	int fno;
30
	int fids[2];
431.1.3 by Matthew Fuller
Add some comments and whitespace around start_m4() to make the flow
31
	int fres;
431.1.4 by Matthew Fuller
Pull writing of the defs file out of the post-fork child, and be sure
32
	char *defs_file;
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
33
431.1.3 by Matthew Fuller
Add some comments and whitespace around start_m4() to make the flow
34
	/* We'll need to work in file descriptors, not stdio FILE's */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
35
	fno = fileno(fraw);
431.1.3 by Matthew Fuller
Add some comments and whitespace around start_m4() to make the flow
36
431.1.4 by Matthew Fuller
Pull writing of the defs file out of the post-fork child, and be sure
37
	/* Write our our standard definitions into a temp file */
38
	defs_file = m4_defs(dpy, CLarg.display_name);
39
431.1.3 by Matthew Fuller
Add some comments and whitespace around start_m4() to make the flow
40
	/* We'll read back m4's output over a pipe */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
41
	pipe(fids);
431.1.3 by Matthew Fuller
Add some comments and whitespace around start_m4() to make the flow
42
43
	/* Fork off m4 as a child */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
44
	fres = fork();
45
	if(fres < 0) {
46
		perror("Fork for " M4CMD " failed");
431.1.4 by Matthew Fuller
Pull writing of the defs file out of the post-fork child, and be sure
47
		unlink(defs_file);
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
48
		exit(23);
49
	}
431.1.3 by Matthew Fuller
Add some comments and whitespace around start_m4() to make the flow
50
51
	/*
52
	 * Child: setup and spawn m4, and have it write its output into one
53
	 * end of our pipe.
54
	 */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
55
	if(fres == 0) {
431.1.6 by Matthew Fuller
Comment.
56
		/* Setup file descriptors */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
57
		close(0);               /* stdin */
58
		close(1);               /* stdout */
59
		dup2(fno, 0);           /* stdin = fraw */
60
		dup2(fids[1], 1);       /* stdout = pipe to parent */
431.1.5 by Matthew Fuller
Spell out more explicitly what the m4 command is doing, so we don't
61
62
		/*
63
		 * Kick off m4, telling it both our file of definitions, and
64
		 * stdin (dup of the .[c]twmrc file descriptor above) as input.
65
		 * It writes to stdout (one end of our pipe).
66
		 */
431.1.4 by Matthew Fuller
Pull writing of the defs file out of the post-fork child, and be sure
67
		execlp(M4CMD, M4CMD, "-s", defs_file, "-", NULL);
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
68
69
		/* If we get here we are screwed... */
70
		perror("Can't execlp() " M4CMD);
431.1.4 by Matthew Fuller
Pull writing of the defs file out of the post-fork child, and be sure
71
		unlink(defs_file);
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
72
		exit(124);
73
	}
431.1.3 by Matthew Fuller
Add some comments and whitespace around start_m4() to make the flow
74
75
	/*
76
	 * Else we're the parent; hand back our reading end of the pipe.
77
	 */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
78
	close(fids[1]);
431.1.3 by Matthew Fuller
Add some comments and whitespace around start_m4() to make the flow
79
	return (fdopen(fids[0], "r"));
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
80
}
81
82
431.1.7 by Matthew Fuller
Sectionheaderize.
83
/*
431.1.8 by Matthew Fuller
Clean up and simplify MkDef() and MkNum().
84
 * Utils: Generate m4 definition lines for string or numeric values.
85
 * Just use a static buffer of a presumably crazy overspec size; it's
86
 * unlikely any lines will push more than 60 or 80 chars.
431.1.7 by Matthew Fuller
Sectionheaderize.
87
 */
431.1.9 by Matthew Fuller
Do a little extra so just in case we DO get error lines, they at least
88
#define MAXDEFLINE 4095
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
89
static const char *MkDef(const char *name, const char *def)
90
{
431.1.9 by Matthew Fuller
Do a little extra so just in case we DO get error lines, they at least
91
	static char cp[MAXDEFLINE + 1]; /* +1 for \n on error */
431.1.8 by Matthew Fuller
Clean up and simplify MkDef() and MkNum().
92
	int pret;
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
93
431.1.8 by Matthew Fuller
Clean up and simplify MkDef() and MkNum().
94
	/* Best response to an empty value is probably just nothing */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
95
	if(def == NULL) {
431.1.8 by Matthew Fuller
Clean up and simplify MkDef() and MkNum().
96
		return ("");
97
	}
98
99
	/* Put together */
100
	pret = snprintf(cp, MAXDEFLINE, "define(`%s', `%s')\n", name, def);
101
102
	/* Be gracefulish about ultra-long lines */
103
	if(pret >= MAXDEFLINE) {
431.1.9 by Matthew Fuller
Do a little extra so just in case we DO get error lines, they at least
104
		pret = snprintf(cp, MAXDEFLINE, "dnl Define for '%s' too long: %d "
431.1.11 by Matthew Fuller
make indent
105
		                "chars, limit %d\n", name, pret, MAXDEFLINE);
431.1.9 by Matthew Fuller
Do a little extra so just in case we DO get error lines, they at least
106
		if(pret >= MAXDEFLINE) {
107
			/* In case it was name that blew out the length */
108
			*(cp + MAXDEFLINE - 1) = '\n';
109
			*(cp + MAXDEFLINE)     = '\0';
110
		}
431.1.8 by Matthew Fuller
Clean up and simplify MkDef() and MkNum().
111
	}
112
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
113
	return(cp);
114
}
115
116
static const char *MkNum(const char *name, int def)
117
{
431.1.8 by Matthew Fuller
Clean up and simplify MkDef() and MkNum().
118
	char num[32];
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
119
431.1.8 by Matthew Fuller
Clean up and simplify MkDef() and MkNum().
120
	/*
121
	 * 2**64 is only 20 digits, so we've got plenty of headroom.  Don't
122
	 * even bother checking the size wasn't exceeded.
123
	 */
124
	snprintf(num, 32, "%d", def);
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
125
	return(MkDef(name, num));
126
}
127
128
429.1.5 by Matthew Fuller
Add a comment about how we're cheaping out here.
129
/* Technically should sysconf() this, but good enough for our purposes */
429.1.4 by Matthew Fuller
Move this #define down closer to where it's used.
130
#define MAXHOSTNAME 255
429.1.5 by Matthew Fuller
Add a comment about how we're cheaping out here.
131
429.1.6 by Matthew Fuller
Add a few gross flow comments.
132
/*
133
 * Writes out a temp file of all the m4 defs appropriate for this run,
134
 * and returns the file name
135
 */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
136
static char *m4_defs(Display *display, char *host)
137
{
138
	Screen *screen;
139
	Visual *visual;
140
	char client[MAXHOSTNAME], server[MAXHOSTNAME], *colon;
141
	struct hostent *hostname;
429.1.18 by Matthew Fuller
Build up color in the switch() that determines vc, rather than in a
142
	char *vc, *color;
431.1.2 by Matthew Fuller
We're ctwm, be temporarily proud about it.
143
	static char tmp_name[] = "/tmp/ctwmrcXXXXXX";
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
144
	int fd;
145
	FILE *tmpf;
146
	char *user;
147
429.1.6 by Matthew Fuller
Add a few gross flow comments.
148
	/* Create temp file */
429.1.1 by Matthew Fuller
mkstemp() is technically in stdlib.h; we're getting it via pollution
149
	fd = mkstemp(tmp_name);
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
150
	if(fd < 0) {
151
		perror("mkstemp failed in m4_defs");
152
		exit(377);
153
	}
429.1.3 by Matthew Fuller
fdopen() returns a FILE * already, don't bother casting.
154
	tmpf = fdopen(fd, "w+");
429.1.8 by Matthew Fuller
Shuffle around a little and comment up the code figuring out hostname
155
156
157
	/*
158
	 * Now start writing the defs into it.
159
	 */
160
#define WR_DEF(k, v) fputs(MkDef((k), (v)), tmpf)
161
#define WR_NUM(k, v) fputs(MkNum((k), (v)), tmpf)
162
163
	/*
164
	 * The machine running the window manager process (and, presumably,
165
	 * most of the other clients the user is running)
166
	 */
429.1.2 by Matthew Fuller
gethostname() is long defined in POSIX, so use it instead of
167
	if(gethostname(client, MAXHOSTNAME) < 0) {
168
		perror("gethostname failed in m4_defs");
169
		exit(1);
170
	}
429.1.8 by Matthew Fuller
Shuffle around a little and comment up the code figuring out hostname
171
	WR_DEF("CLIENTHOST", client);
429.1.6 by Matthew Fuller
Add a few gross flow comments.
172
173
	/*
429.1.8 by Matthew Fuller
Shuffle around a little and comment up the code figuring out hostname
174
	 * A guess at the machine running the X server.  We take the full
175
	 * $DISPLAY and chop off the screen specification.
429.1.6 by Matthew Fuller
Add a few gross flow comments.
176
	 */
429.1.8 by Matthew Fuller
Shuffle around a little and comment up the code figuring out hostname
177
	/* stpncpy() a better choice */
431.1.1 by Matthew Fuller
snprintf() already does size-1.
178
	snprintf(server, sizeof(server), "%s", XDisplayName(host));
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
179
	colon = strchr(server, ':');
180
	if(colon != NULL) {
181
		*colon = '\0';
182
	}
429.1.8 by Matthew Fuller
Shuffle around a little and comment up the code figuring out hostname
183
	/* :0 or unix socket connection means it's the same as CLIENTHOST */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
184
	if((server[0] == '\0') || (!strcmp(server, "unix"))) {
429.1.8 by Matthew Fuller
Shuffle around a little and comment up the code figuring out hostname
185
		strcpy(server, client);
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
186
	}
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
187
	WR_DEF("SERVERHOST", server);
429.1.8 by Matthew Fuller
Shuffle around a little and comment up the code figuring out hostname
188
189
	/* DNS (/NSS) lookup can't be the best way to do this, but... */
190
	hostname = gethostbyname(client);
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
191
	if(hostname) {
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
192
		WR_DEF("HOSTNAME", hostname->h_name);
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
193
	}
194
	else {
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
195
		WR_DEF("HOSTNAME", client);
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
196
	}
197
429.1.9 by Matthew Fuller
Slightly reorder defs so they're grouped with related bits, and
198
	/*
199
	 * Info about the user and their environment
200
	 */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
201
	if(!(user = getenv("USER")) && !(user = getenv("LOGNAME"))) {
429.1.19 by Matthew Fuller
Try a little harder to figure out the username.
202
		struct passwd *pwd = getpwuid(getuid());
203
		if(pwd) {
204
			user = pwd->pw_name;
205
		}
206
	}
207
	if(!user) {
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
208
		user = "unknown";
209
	}
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
210
	WR_DEF("USER", user);
211
	WR_DEF("HOME", getenv("HOME"));
429.1.9 by Matthew Fuller
Slightly reorder defs so they're grouped with related bits, and
212
213
	/*
214
	 * ctwm meta
215
	 */
216
	WR_DEF("TWM_TYPE", "ctwm");
217
	WR_DEF("TWM_VERSION", VersionNumber);
438.1.4 by Matthew Fuller
Add split out major/minor/patch/addl defines into the m4 output.
218
	WR_DEF("CTWM_VERSION_MAJOR", VersionNumber_major);
219
	WR_DEF("CTWM_VERSION_MINOR", VersionNumber_minor);
220
	WR_DEF("CTWM_VERSION_PATCH", VersionNumber_patch);
221
	WR_DEF("CTWM_VERSION_ADDL",  VersionNumber_addl);
429.1.9 by Matthew Fuller
Slightly reorder defs so they're grouped with related bits, and
222
223
	/*
224
	 * X server meta
225
	 */
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
226
	WR_NUM("VERSION", ProtocolVersion(display));
227
	WR_NUM("REVISION", ProtocolRevision(display));
228
	WR_DEF("VENDOR", ServerVendor(display));
229
	WR_NUM("RELEASE", VendorRelease(display));
429.1.9 by Matthew Fuller
Slightly reorder defs so they're grouped with related bits, and
230
231
	/*
232
	 * Information about the display
233
	 */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
234
	screen = ScreenOfDisplay(display, Scr->screen);
235
	visual = DefaultVisualOfScreen(screen);
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
236
	WR_NUM("WIDTH", screen->width);
237
	WR_NUM("HEIGHT", screen->height);
429.1.17 by Matthew Fuller
Move #define down to where it's used, and #undef it when we're done.
238
#define Resolution(pixels, mm)  ((((pixels) * 100000 / (mm)) + 50) / 100)
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
239
	WR_NUM("X_RESOLUTION", Resolution(screen->width, screen->mwidth));
240
	WR_NUM("Y_RESOLUTION", Resolution(screen->height, screen->mheight));
429.1.17 by Matthew Fuller
Move #define down to where it's used, and #undef it when we're done.
241
#undef Resolution
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
242
	WR_NUM("PLANES", DisplayPlanes(display, Scr->screen));
243
	WR_NUM("BITS_PER_RGB", visual->bits_per_rgb);
429.1.18 by Matthew Fuller
Build up color in the switch() that determines vc, rather than in a
244
	color = "Yes";
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
245
	switch(visual->class) {
246
		case(StaticGray):
247
			vc = "StaticGray";
429.1.18 by Matthew Fuller
Build up color in the switch() that determines vc, rather than in a
248
			color = "No";
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
249
			break;
250
		case(GrayScale):
251
			vc = "GrayScale";
429.1.18 by Matthew Fuller
Build up color in the switch() that determines vc, rather than in a
252
			color = "No";
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
253
			break;
254
		case(StaticColor):
255
			vc = "StaticColor";
256
			break;
257
		case(PseudoColor):
258
			vc = "PseudoColor";
259
			break;
260
		case(TrueColor):
261
			vc = "TrueColor";
262
			break;
263
		case(DirectColor):
264
			vc = "DirectColor";
265
			break;
266
		default:
267
			vc = "NonStandard";
268
			break;
269
	}
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
270
	WR_DEF("CLASS", vc);
429.1.18 by Matthew Fuller
Build up color in the switch() that determines vc, rather than in a
271
	WR_DEF("COLOR", color);
429.1.9 by Matthew Fuller
Slightly reorder defs so they're grouped with related bits, and
272
273
	/*
274
	 * Bits of "how this ctwm invocation is being run" data
275
	 */
276
	if(CLarg.is_captive && Scr->captivename) {
277
		WR_DEF("TWM_CAPTIVE", "Yes");
278
		WR_DEF("TWM_CAPTIVE_NAME", Scr->captivename);
279
	}
280
	else {
281
		WR_DEF("TWM_CAPTIVE", "No");
282
	}
283
284
	/*
285
	 * Various compile-time options.
286
	 */
287
#ifdef PIXMAP_DIRECTORY
288
	WR_DEF("PIXMAP_DIRECTORY", PIXMAP_DIRECTORY);
289
#endif
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
290
#ifdef XPM
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
291
	WR_DEF("XPM", "Yes");
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
292
#endif
293
#ifdef JPEG
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
294
	WR_DEF("JPEG", "Yes");
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
295
#endif
296
#ifdef SOUNDS
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
297
	WR_DEF("SOUNDS", "Yes");
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
298
#endif
429.1.16 by Matthew Fuller
Add a m4 def for whether ctwm is built with EWMH support.
299
#ifdef EWMH
300
	WR_DEF("EWMH", "Yes");
301
#endif
429.1.15 by Matthew Fuller
Make up the I18N def in the docs, and mark it in docs and code as to
302
	/* Since this is no longer an option, it should be removed in the future */
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
303
	WR_DEF("I18N", "Yes");
429.1.6 by Matthew Fuller
Add a few gross flow comments.
304
429.1.7 by Matthew Fuller
Factor out a bunch of repetititive code into some macros. No
305
#undef WR_NUM
306
#undef WR_DEF
429.1.6 by Matthew Fuller
Add a few gross flow comments.
307
429.1.9 by Matthew Fuller
Slightly reorder defs so they're grouped with related bits, and
308
429.1.6 by Matthew Fuller
Add a few gross flow comments.
309
	/*
310
	 * We might be keeping it, in which case tell the user where it is;
311
	 * this is mostly a debugging option.  Otherwise, delete it by
312
	 * telling m4 to do so when it reads it; this is fairly fugly, and I
313
	 * have more than half a mind to dike it out and properly clean up
314
	 * ourselves.
315
	 */
389.1.31 by Matthew Fuller
Switch these 3 M4-related command line args into the CLargs structure
316
	if(CLarg.KeepTmpFile) {
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
317
		fprintf(stderr, "Left file: %s\n", tmp_name);
318
	}
319
	else {
320
		fprintf(tmpf, "syscmd(/bin/rm %s)\n", tmp_name);
321
	}
429.1.6 by Matthew Fuller
Add a few gross flow comments.
322
323
324
	/* Close out and hand it back */
387.1.1 by Matthew Fuller
Bust out a lot of the m4 parsing handling from parse.c into a separate
325
	fclose(tmpf);
326
	return(tmp_name);
327
}