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