text: #include <stdlib.h>
  #include <stdio.h>
  #include <stdarg.h>
  #include <inttypes.h>
  #include <ctype.h>
  #include <string.h>
  #include <assert.h>
  #include <fcntl.h>
  #include <unistd.h>
  
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <sys/time.h>
  #include <sys/wait.h>
  
  #include "util.h"
  #include "parse.h"
  #include "mi.h"
  #include "asm.h"
  
  #include "../config.h"
  
  /* FIXME: move into one place...? */
  Node *file;
  char debugopt[128];
  int writeasm;
  int extracheck = 1;
  int p9asm;
  char *outfile;
  char *objdir;
  char **incpaths;
  char *localincpath;
  size_t nincpaths;
  Asmsyntax asmsyntax;
  
  static void
  usage(char *prog)
  {
  	printf("%s [-?|-h] [-o outfile] [-O dir] [-d[dbgopts]] inputs\n", prog);
  	printf("\t-?|-h\tPrint this help\n");
  	printf("\t-o\tOutput to outfile\n");
  	printf("\t-O dir\tOutput to dir\n");
  	printf("\t-S\tGenerate assembly source alongside object code\n");
  	printf("\t-T\tCompile in test mode\n");
  	printf("\t-c\tEnable additional (possibly flaky) checking\n");
  	printf("\t-I path\tAdd 'path' to use search path\n");
  	printf("\t-d\tPrint debug dumps. Recognized options: f r p i\n");
  	printf("\t-G\tGenerate asm in gas syntax\n");
  	printf("\t-9\tGenerate asm in plan 9 syntax\n");
  	printf("\t-d opts: additional debug logging. Options are listed below:\n");
  	printf("\t\tf: log folded trees\n");
  	printf("\t\tl: log lowered pre-cfg trees\n");
  	printf("\t\tT: log tree immediately\n");
  	printf("\t\tr: log register allocation activity\n");
  	printf("\t\ti: log instruction selection activity\n");
  	printf("\t\tu: log type unifications\n");
  }
  
  static void
  swapout(char* buf, size_t sz, char* suf) {
  	char* psuffix;
  	psuffix = strrchr(outfile, '.');
  	if (psuffix != NULL)
  		swapsuffix(buf, sz, outfile, psuffix, suf);
  	else
  		bprintf(buf, sz, "%s%s", outfile, suf);
  }
  
  static void
  assemble(char *asmsrc, char *path)
  {
  	char *asmcmd[] = Asmcmd;
  	char objfile[1024];
  	char *psuffix;
  	char **p, **cmd;
  	size_t ncmd, i;
  	int pid, status;
  
  	if (outfile != NULL)
  		strncpy(objfile, outfile, 1024);
  	else {
  		psuffix = strrchr(path, '+');
  		i = 0;
  		if (objdir)
  			i = bprintf(objfile, sizeof objfile, "%s/", objdir);
  		if (psuffix != NULL)
  			swapsuffix(objfile + i, sizeof objfile - i, path, psuffix, Objsuffix);
  		else
  			swapsuffix(objfile + i, sizeof objfile - i, path, ".myr", Objsuffix);
  	}
  	cmd = NULL;
  	ncmd = 0;
  	for (p = asmcmd; *p != NULL; p++)
  		lappend(&cmd, &ncmd, *p);
  	lappend(&cmd, &ncmd, objfile);
  	lappend(&cmd, &ncmd, asmsrc);
  	lappend(&cmd, &ncmd, NULL);
  
  	pid = fork();
  	if (pid == -1) {
  		die("couldn't fork");
  	} else if (pid == 0) {
  		if (execvp(cmd[0], cmd) == -1)
  			die("Couldn't exec assembler\n");
  	} else {
  		waitpid(pid, &status, 0);
  		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
  			die("Couldn't run assembler");
  	}
  	/*
  	We don't want to keep the asm source around, but it's useful
  	for deubgging without mapping to line numbers.
  	*/
  	if (asmsyntax != Plan9 && !writeasm)
  		unlink(asmsrc);
  }
  
  static char *
  dirname(char *path)
  {
  	char *p;
  
  	p = strrchr(path, '/');
  	if (p)
  		return strdupn(path, p - path);
  	else
  		return xstrdup(".");
  }
  
  static char *
  gentempfile(char *buf, size_t bufsz, char *path, char *suffix)
  {
  	char *tmpdir;
  	char *base;
  	struct timeval tv;
  
  	tmpdir = getenv("TMPDIR");
  	if (!tmpdir)
  		tmpdir = "/tmp";
  	base = strrchr(path, '/');
  	if (base)
  		base++;
  	else
  		base = path;
  	gettimeofday(&tv, NULL);
  	bprintf(buf, bufsz, "%s/tmp%lx%lx-%s%s", tmpdir, (long)tv.tv_sec, (long)tv.tv_usec, base, suffix);
  	return buf;
  }
  
  static int
  hasmain(Node *file)
  {
  	Node *n, *name;
  
  	name = mknsname(Zloc, NULL, "main");
  	n = getdcl(file->file.globls, name);
  	if (!n)
  		return 0;
  	n = n->decl.name;
  	if (n->name.ns)
  		return 0;
  	return 1;
  }
  
  static void
  genuse(char *path)
  {
  	FILE *f;
  	char buf[1024];
  	char *psuffix;
  	size_t i;
  
  	if (outfile != NULL)
  		swapout(buf, 1024, ".use");
  	else {
  		psuffix = strrchr(path, '+');
  		i = 0;
  		if (objdir)
  			i = bprintf(buf, sizeof buf, "%s/", objdir);
  		if (psuffix != NULL)
  			swapsuffix(buf + i, sizeof buf - i, path, psuffix, ".use");
  		else
  			swapsuffix(buf + i, sizeof buf - i, path, ".myr", ".use");
  	}
  	f = fopen(buf, "w");
  	if (!f) {
  		fprintf(stderr, "Could not open path %s\n", buf);
  		exit(1);
  	}
  	writeuse(f, file);
  	fclose(f);
  }
  
  int
  main(int argc, char **argv)
  {
  	char buf[1024];
  	Stab *globls;
  	Optctx ctx;
  	size_t i;
  
  	outfile = NULL;
  
  	optinit(&ctx, "cd:?hSo:I:9G:O:T", argv, argc);
  	asmsyntax = Defaultasm;
  	sizefn = size;
  	while (!optdone(&ctx)) {
  		switch (optnext(&ctx)) {
  		case 'O':
  			objdir = ctx.optarg;
  			break;
  		case 'o':
  			outfile = ctx.optarg;
  			break;
  		case 'S':
  			writeasm = 1;
  			break;
  		case '?':
  		case 'h':
  			usage(argv[0]);
  			exit(0);
  			break;
  		case 'c':
  			extracheck = 1;
  			break;
  		case 'd':
  			while (ctx.optarg && *ctx.optarg)
  				debugopt[*ctx.optarg++ & 0x7f]++;
  			break;
  		case '9':
  			asmsyntax = Plan9;
  			break;
  		case 'G':
  			if (!strcmp(ctx.optarg, "e"))
  				asmsyntax = Gnugaself;
  			else if (!strcmp(ctx.optarg, "m"))
  				asmsyntax = Gnugasmacho;
  			else
  				die("unknown gnu syntax flavor");
  			break;
  		case 'I':
  			lappend(&incpaths, &nincpaths, ctx.optarg);
  			break;
  		case 'T':
  			allowhidden++;
  			break;
  		default:
  			usage(argv[0]);
  			exit(0);
  			break;
  		}
  	}
  
  	/* Sysinit is arbitrary code defined by the configure script */
  	Sysinit;
  
  	lappend(&incpaths, &nincpaths, Instroot "/lib/myr");
  
  	if (ctx.nargs == 0) {
  		fprintf(stderr, "No input files given\n");
  		exit(1);
  	} else if (ctx.nargs > 1)
  		outfile = NULL;
  
  	for (i = 0; i < ctx.nargs; i++) {
  		if (outfile)
  			localincpath = dirname(outfile);
  		else
  			localincpath = dirname(ctx.args[i]);
  
  		globls = mkstab(0);
  		tyinit(globls);
  		tokinit(ctx.args[i]);
  		file = mkfile(ctx.args[i]);
  		file->file.globls = globls;
  		yyparse();
  
  		/* before we do anything to the parse */
  		if (debugopt['T'])
  			dump(file, stdout);
  		loaduses();
  		if (hasmain(file))
  			geninit();
  		infer();
  		tagexports(0);
  		/* after all type inference */
  		if (debugopt['t'])
  			dump(file, stdout);
  
  		if (writeasm) {
  			if (outfile != NULL)
  				swapout(buf, sizeof buf, ".s");
  			else
  				swapsuffix(buf, sizeof buf, ctx.args[i], ".myr", ".s");
  		} else {
  			gentempfile(buf, sizeof buf, ctx.args[i], ".s");
  		}
  		genuse(ctx.args[i]);
  		gen(file, buf);
  		assemble(buf, ctx.args[i]);
  
  		free(localincpath);
  	}
  
  	return 0;
  }
  