/* $Id: emit.c,v 8.26 2012/09/12 22:55:16 ksb Exp $ * Emit some of the character to the output C program, all the * output to the C file should be in here. */ #include #include #include #include #include "machine.h" #include "main.h" #include "type.h" #include "option.h" #include "scan.h" #include "parser.h" #include "list.h" #include "mkcmd.h" #include "key.h" #include "routine.h" #include "emit.h" #include "check.h" #include "atoc.h" #if USE_STRINGS #include #else #include #endif #if USE_STDLIB #include #else #if USE_MALLOC_H #include #else #if NEED_MALLOC_EXTERN extern char *malloc(), *calloc(), *realloc(); #endif #endif #endif ATTR wEmitMask = EMIT_AUTO_IND; /* implicit emit options */ char acNoOpt[] = "\'\\000\'", acCurOpt[] = "u_curopt", acEnvTemp[] = "u_pcEnv", *pcCurOpt = acNoOpt, *pcJustEscape = nil; static char *pcOptList = "sbOpt", acIndent[] = "\t\t\t\t\t\t\t\t\t ", acVersion[32]; static struct tm tmNow; /* interface to strftime */ /* make a spelling for a character that C will like (ksb) * and the human can read */ char * CSpell(pcOut, c, fQuote) char *pcOut; int c, fQuote; { register char *pcRet; pcRet = pcOut; if (EOF == c) { sprintf(pcOut, "EOF"); return pcOut; } if (fQuote) { *pcOut++ = '\''; } if (! isgraph(c)) { pcOut[0] = '\\'; pcOut[2] = '\000'; switch (c) { case ' ': /* don't do this to spaces */ pcOut[0] = ' '; pcOut[1] = '\000'; break; case '\b': pcOut[1] = 'b'; break; case '\r': pcOut[1] = 'r'; break; case '\f': pcOut[1] = 'f'; break; case '\n': pcOut[1] = 'n'; break; case '\t': pcOut[1] = 't'; break; default: sprintf(pcOut+1, "%03o", c); break; } } else if ('\\' == c || '\'' == c || '\"' == c) { sprintf(pcOut, "\\%c", c); } else { sprintf(pcOut, "%c", c); } if (fQuote) { (void)strcat(pcOut, "\'"); } return pcRet; } /* make the default name for an option/action/special (ksb) */ char * mkdefid(pOR) OPTION *pOR; { static char acTemp[16]; if (ISVARIABLE(pOR)) { return pOR->pchname; } switch (pOR->chname) { case '?': return "ch"; case '*': return "arg"; case ':': return "def"; case '#': return "number"; case '+': return "escape"; default: if (ISSACT(pOR)) { return sactstr(pOR->chname); } if (isdigit(pOR->chname)) { acTemp[0] = 'd'; acTemp[1] = pOR->chname; acTemp[2] = '\000'; } else if (isalnum(pOR->chname)) { acTemp[0] = pOR->chname; acTemp[1] = '\000'; } else { sprintf(acTemp, "x%x", pOR->chname); } break; } return acTemp; } /* a option -> C identifier (ksb) * (that is, convert it to a string the user can understand better) */ char * mkid(pOR, pcDst) OPTION *pOR; char *pcDst; { register char *pch; static char acFallback[32]; if (nil == pcDst) { pcDst = acFallback; } while (ISALIAS(pOR)) { pOR = pOR->pORali; } if (nil == (pch = pOR->pchname)) { register char *pcFrom, *pcPlate; if (CHDECL == pOR->chname) { fprintf(stderr, "%s: globals must have names\n", progname); iExit |= MER_SEMANTIC; } pcFrom = OPACTION(pOR) ? pchProto : pchTempl; pcPlate = OPACTION(pOR) ? "prototype" : "template"; (void)sprintf(pcDst, pcFrom, mkdefid(pOR)); if ('\000' == pcDst[0]) { fprintf(stderr, "%s: %s: %s \"%s\" produced the empty string\n", progname, usersees(pOR, nil), pcPlate, pcFrom); iExit |= MER_SEMANTIC; (void)strcpy(pcDst, pcPlate); } } else if ('\000' == pch[0]) { fprintf(stderr, "%s: %s: identifiers may not be the empty string\n", progname, usersees(pOR, nil)); iExit |= MER_SEMANTIC; (void)strcpy(pcDst, ""); } else { (void)strcpy(pcDst, pch); } return pcDst; } static EMIT_MAP aERTop[] = { {'a', "argument", "special contexts argument"}, {'A', "getarg", "a C expression to get the next argument"}, {'b', "basename", "the basename of the running application"}, {'B', "keyword", "the keyword found after a key basename"}, {'c', "comment", "the comment continuation for block comments"}, {'D', "value", "expand WRT the following string as a value..."}, {'e', "expand", (char *)0}, {'E', "strerror", "C expression producing a string for errno"}, {'f', "main", "the name of the function we are building"}, {'F', "output", "-n command line parameter"}, {'G', "argkey", "use special argument as a key name..."}, {'H', "hold", "change the value of %a"}, /* 'I' is available for interface modules (output.c) */ {'J', "pctemp", "temporary character pointer variable"}, {'k', "mkdelim", "mk's marker character"}, {'K', "key", "expand API key values..."}, {'o', "optopt", "getopt's record of the option presented"}, {'O', "getopt", "C expression to call getopt for next option"}, {'P', "altparam", "alternate parameter in special contexts"}, {'q', "quote", "output the expansion of this quoted for C strings"}, {'Q', "megaquote", "output remainder of line quoted for C strings"}, {'s', "broken", "if we see this we believe unquoted a printf-percent"}, {'t', "terse", "global terse usage variable, prefix with an 'a' for array of all of them"}, {'T', "update", "(deprecated) conversion for option"}, {'u', "usage", "global usage function name"}, {'v', "vector", "global usage vector name (array of strings)"}, {'V', "version", "mkcmd's version identifier"}, {'w', "which", "C expression for the alias that caused this conversion"}, {'W', "mkcmd", "what name was mkcmd called?"}, {'X', "expand", "force re-expansion of this result"}, {'y', "type", "lookup a type by name..."}, {'Y', "time", "call strftime on time of compilation..."}, {'z', "error", "force an internal error"}, {'Z', "option", "force next down to option level"}, {':', "optlist", "name of local C variable holding getopt parameter"}, {'{', "lcurly", "open curly brace with no C formatting"}, {'}', "rcurly", "close curly brace with no C formatting"}, {';', "semicolon", "semicolon with no C formatting"}, {'\n', "newline", (char *)0}, {'+', "newline", "a literal newline character"}, {'\\', "escape", "the 4th parameter to getopt, based on the escape trigger"}, {'#', "remainder", "a C expression for the number of parameters remaining"}, {'@', "params", "a C expression for the vector of parameters remaining"}, {'%', "percent", "a percent sign"}, }; EMIT_HELP EHTop = { "top level expander", (char *)0, sizeof(aERTop)/sizeof(EMIT_MAP), aERTop, (char *)0, (EMIT_HELP *)0 }; /* provide clues about the expander described, to the user (ksb) * OK. Complex, but not too strange. Some escapes are covered * by the level above (OptEsc A covered by TopLevel A). In the * help output we should mark the blocked ones with a "*" and footnote * that they are blocked from above. Some escape in the level above * should force expansion to the level below (%Z, %