/* -*- mode: c; tabs: 4; -*- ************************************************ * Module name : cmdfile.c * Module type : CMDFILE source file * Environment(s): n/a * * Description: * CMDFILE --- command line file builder * * Version Who Date Description APY 12/99 Created APY 14/03/00 When '-o' switch, use temp output and rename on success ... Additional command line help v1.1 APY 16/04/02 realpath macro APY 08/12/03 - MRI kludges (k2 and r?) (v1.2) - CMDFILE env variable support 26/04/04 - shortpath - macro argument single and double quoted strings processing. - realpath2 - url2path - path2url - 1.21 05/05/04 - 'w', 'd' and 'm' switches - 1.22 10/05/04 - handle leading white space within paths. - 1.23 17/05/04 - vlibgcc - 1.24 23/06/04 - Whitespace handling -W switch - 1.25 23/06/04 - Fixed macro buffer overflow DP 23/09/04 - vpath() generates more readable error messages when a library file cannot be found in vpath. - 1.26 APY 05/10/04 vpath3, vlib3 and vglob3 Path arguments support embedded spaces. Extended usage @vhost and @vsep @vlint - 1.30 * * $Source: /cvsroot/device/DEVL/UTILS/CMDFILE/cmdfile.c,v $ * $Revision: 1.11 $ $Date: 2004/11/05 07:51:39 $ $State: Exp $ * $Author: ayoung $ $Locker: $ *.........................................................................*/ #include #include #include #include #include #include #include #include #include "cmdfile.h" #include "longopt.h" #define NEWLINE -2 #define SEPERATOR -3 const char * version_string = "1.31"; char * program_name = ""; char tmp_buf[15] = TMPNAME; char * tmp_name = tmp_buf; int tmp_fd = -1; FILE * output = NULL; char mflg = '@'; char dflg[2] = { '(', ')' }; int cflg = 0; int eflg = 1; int kflg = 0; int k2flg = 0; int Mflg = 0; int nflg = 1; int nlflg = 0; const char * oflg = NULL; int rflg = 0; char rtags[10] = { 0 }; int tflg = 0; int vflg = 0; int wflg = 0; int whitespace = WS_UNKNOWN; int nl = FALSE; static int linelen = 0; static char linebuffer[ 32*1024 ]; /* 32k line buffer */ static void PUTC(int c); static void PUTS(const char *s); static void FLUSH(void); static int inline_realpath(char *line, int *length); static void do_padkludge(void); static void usage(int status); static void cleanup(void); /* Print the words in LIST to standard output. If the first word * is '-n', then don't print a trailing newline. We also * support the echo syntax from Version 9 unix systems. */ int #if defined(WIN32) __cdecl #endif main(int argc, char **argv) { output = stdout; if ((program_name = strrchr(argv[0], '/')) == NULL && (program_name = strrchr(argv[0], '\\')) == NULL) program_name = argv[0]; else program_name++; #if defined(WIN32) && (0) rflg = -1; /* MRI kludge (v2)*/ strcpy(rtags, "IJ"); #endif /* Handle command file */ if (argc == 2 && argv[1][0] == '@') { struct stat ast; int afd, len; char *args; char **nargv; int nargc; char quotechar = '\0'; char *p; nargc = 1; #if defined(MSDOS) || defined(WIN32) if ((afd = open(argv[1]+1, O_RDONLY|O_BINARY)) < 0) #else if ((afd = open(argv[1]+1, O_RDONLY)) < 0) #endif fatalerr("Cannot open \"%s\"", argv[1]+1); fstat(afd, &ast); if ((args = (char *)malloc(ast.st_size + 1)) == NULL) fatalerr("Cannot allocate read buffer"); args[ ast.st_size ] = '\0'; do { if ((len = read(afd, args, ast.st_size)) < 0) fatalerr("Failed to read %s", argv[1]+1); } while ((ast.st_size -= len) > 0); close(afd); for (p = args; *p; p++) { if (quotechar) { if (quotechar == '\\' || (*p == quotechar && p[-1] != '\\')) quotechar = '\0'; continue; } switch (*p) { case '\\': case '"': case '\'': quotechar = *p; break; case ' ': case '\n': *p = '\0'; if (p > args && p[-1]) nargc++; break; case '\r': *p = '\0'; if (p > args && p[-1]) nargc++; if (p[1] == '\n') *++p = '\0'; break; } } if (p[-1]) nargc++; nargv = (char **)malloc(nargc * sizeof(char *)); nargv[0] = argv[0]; argc = 1; for (p = args; argc < nargc; p += strlen(p) + 1) if (*p) nargv[argc++] = p; argv = nargv; } /* Long options (specials), * --help * --version */ parse_long_options( argc, argv, "cmdfile", version_string, usage ); argc--, argv++; /* Import CMDFILE environment variable */ if (getenv("CMDFILE")) { printf( "PARSE ENV OPTS\n" ); parse_short_argument( getenv("CMDFILE" ) ); } /* Standard options (only allowed as first command) */ if (argc > 0 && *argv[0] == '-') if (parse_short_argument( argv[0]+1 ) > 0) { if (oflg) { if ((tmp_fd = mkstemp( tmp_name )) == -1) fatalerr( "Cannot build temp file name \"%s\" : %d (%s)", tmp_name, errno, strerror(errno)); atexit( cleanup ); /* temp name cleanup */ if ((output = fdopen( tmp_fd, "wb+" )) == NULL) fatalerr( "Cannot fdopen \"%s\" : %d (%s)", tmp_name, errno, strerror(errno)); } argc--, argv++; /* remove argument */ } if (vflg > 2) { verbose( "Options:" ); if (cflg) verbose( " Redirecting errors to stdout." ); if (nflg) verbose( " No trailing newline" ); if (nlflg) verbose( " Newline seperator" ); if (wflg) verbose( " Consuming leading whitespace" ); if (!Mflg) verbose( " Procesing macros" ); if (!eflg) verbose( " Not processing escape sequences" ); if (eflg) verbose( " Level %d escape sequence processing", eflg ); } /* Process remaining */ if (argc > 0) { int argi; nl = FALSE; for (argi = 0; argc > 0; argi++) { if (vflg > 2) verbose( "ARGV[%d]%s", argi, argv[0] ); process_string ( argv[0] ); argc--, argv++; /* next argument */ if (argc > 0 && nl == FALSE) { /* seperator */ PUTC(SEPERATOR); if (nlflg) nl = TRUE; } } } if (nflg) PUTC(NEWLINE); FLUSH(); /* Rename 'temp' */ if (oflg) { if (k2flg) do_padkludge(); fclose(output); tmp_fd = -1; (void) remove(oflg); verbose( " renaming(%s to %s)", tmp_name, oflg ); if (rename(tmp_name, oflg) == -1) fatalerr("Cannot rename temp file \"%s\" to \"%s\" : %d (%s)", tmp_name, oflg, errno, strerror(errno)); tmp_name[0] = '\0'; /* stop cleanup of tmp_name */ } return (0); } /*---------------------------------------------------------------------------- ** FUNCTION : process_string ** ** DESCRIPTION : Process a string ** It may be from an argument, it may be from a macro ** ** ** INPUTS : s - String to process ** ** RETURNS : Nothing ** ----------------------------------------------------------------------------*/ void process_string (register char *s ) { register int c = '\0'; while ((c = *s++) != '\0') { /* * Escape processing ... */ if (eflg && c == '\\' && *s) { switch (c = *s++) { case '\n': /* concat line */ nl = TRUE, c = '\0'; break; case 'n': /* \n */ c = NEWLINE; break; case '\\': /* backslash */ break; default: if (eflg >= 2) { /* extended */ switch (c) { case 'a': c = '\007'; break; case 'b': c = '\b'; break; case 'c': nflg = 0; break; case 'f': c = '\f'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = (int) 0x0B; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c -= '0'; if (*s >= '0' && *s <= '7') c = c * 8 + (*s++ - '0'); if (*s >= '0' && *s <= '7') c = c * 8 + (*s++ - '0'); break; default: PUTC('\\'); /* unknown echo */ break; } } else { PUTC('\\'); /* unknown echo */ } break; } } /* * Macros ... */ else if (Mflg == 0 && c == MACROCHAR && *s) { const char *str; String_t mstr; StringInit( &mstr, NULL, MACRO_MAX ); /* macro resource */ s += macro(&mstr, s); if ((str = StringData(&mstr)) != NULL && str[0]) { PUTS(str); nl = FALSE; } free( StringData(&mstr) ); c = '\0'; } /* * Default output ... */ if (c != '\0') { if (nl && (c == ' ' || c == '\t') && wflg) continue; /* eat leading white space */ if (c == '\n' || c == NEWLINE) nl = TRUE; else nl = FALSE; PUTC(c); /* output character */ } } } static void __PUTC(int c) { int flushit = 0; if (c == NEWLINE || c == '\r' || c == '\n') /* flush on newline */ flushit = 1; if (flushit && linelen > 2) if (rflg && linebuffer[0] == '-') /* inline realpath */ { inline_realpath( linebuffer, &linelen ); } if (c == NEWLINE) { if (kflg) linebuffer[ linelen++ ] = '\r'; linebuffer[ linelen++ ] = '\n'; } else { linebuffer[ linelen++ ] = c; /* append character */ } if (flushit) FLUSH(); } static void PUTC(int c) { if (vflg > 2) { if (c == NEWLINE) verbose( "PUTC[NEWLINE]" ); else if (c == SEPERATOR) verbose( "PUTC[SEPERATOR]" ); else { verbose( "PUTC[]%c", c ); } } if (c == SEPERATOR) c = (nlflg ? NEWLINE : ' '); __PUTC(c); } static void PUTS(const char *s) { if (vflg > 2) verbose( "PUTS[]%s", s ); while (*s) __PUTC(*s++); } static void FLUSH(void) { if (linelen) { register const char *p = linebuffer; linebuffer[ linelen ] = '\0'; while (*p) /* flush line buffer */ putc( *p++, output ); linelen = 0; } } static int inline_realpath(char *line, int *length) { const char *s = line; char buf[ PATH_MAX ], *pbuf; int space = 0; char tag, sep; int i; sep = *s++; tag = *s++; if (strchr(rtags, tag) == 0) /* known tag? */ return (0); while (s[0] == ' ' || s[0] == '\t' ) space++, s++; line[ *length ] = '\0'; /* terminate line */ if (s[0] == '\0' || Realpath(s, buf) == NULL) /* expand */ return (-1); verbose( " inline realpath(%s) = '-%c%s'", s, tag, buf ); i = 0; line[ i++ ] = sep; line[ i++ ] = tag; if (space) line[ i++ ] = ' '; for (pbuf = buf; (line[ i++ ] = *pbuf) != '\0'; pbuf++) /*continue*/; *length = i-1; return (i); } static void do_padkludge(void) { long flen, pad = 0; fflush(output); flen = ftell(output); if (flen > 440 && flen < 500) pad = 540 - flen; else if (flen > 740 && flen < 800) pad = 840 - flen; if (pad) { char *image; /* current file image */ verbose(" padding output (%d --> %d)", flen, flen+pad ); if ((image = (void *)calloc(flen, 1)) == NULL) fatalerr("Memory allocation error"); if (fseek(output, 0L, SEEK_SET) == -1 || (int)fread(image, sizeof(char), flen, output) != flen) fatalerr("reading : %d (%s)", errno, strerror(errno)); fseek(output, 0L, SEEK_SET); fputs("-DPADKLUDGE", output); if ((pad -= (12 + (kflg ? 2 : 1))) > 0) { fputs("=", output); while (pad-- > 0) fputc( "1234567890"[pad%10], output); } if (kflg) fputc('\r', output); fputc('\n', output); if ((int)fwrite(image, sizeof(char), flen, output) != flen) fatalerr("rewriting : %d (%s)", errno, strerror(errno)); free(image); } } static void usage(int status) { if (status != 0) { fprintf(stderr, "Try `%s --help' for more information.\n", program_name); exit(status); } printf("Usage: %s [-OPTION[OPTION]]... [STRING]...\n", program_name); printf("Usage: %s @cmdfile\n", program_name); printf("\n\ Options, all must be encoded as the first argument.\n\ \n\ -n Do not output the trailing newline.\n\ -N Use newline as argument seperator (default space).\n\ -m? Alternative macro identifier (eg -m$, default '@').\n\ -d? Alternative macro delimitor set (eg -m[, default '(').\n\ '[' implies the delimitor set of \"[]\", '(' = \"()\",\n\ '{' = \"{}\" and '<' implies \"<>\".\n\ -k|-k1 DOS lf/cr kludge.\n\ -k2 MRI file padding Kludge.\n\ -w Trim leading white after newlines.\n\ -W{method} Method of handling embedded whitespace within paths.\n\ 0,- - Disable warnings.\n\ 1,e - Escape (eg /Embedded\\ Space/x.h).\n\ 2,q - Quote the entire path (eg \"/Embedded Space/x.h\").\n\ -r? Realpath expansion kludge (eg -rI, expands -Ipath)\n\ -v Verbose output.\n\ -c Redirect errors to stdout.\n\ -E Disable interpolation of some sequences in STRINGs.\n\ -e Enable extended interpolation.\n\ -M Disable macros.\n\ -t Dont remove temporary working files.\n\ -o Output file (must be the last option).\n"); printf("\n\ \n\ The follow options must be specified alone;\n\ \n\ --help Display this help and exit\n\ --version Output version information and exit\n"); printf("\n\ Without -E, the following sequences are recognized and interpolated:\n\ \n\ \\n New line.\n\ \\\\ Backslash.\n"); printf("\n\ If the -e option is given, the following additional sequences are\n\ recognized and interpolated:\n\ \n\ \\a Alert (bell).\n\ \\b Backspace.\n\ \\c Suppress trailing newline (same as -n).\n\ \\f Form feed.\n\ \\r Carriage return.\n\ \\t Horizontal tab.\n\ \\v Vertical tab.\n\ \\NNN The character whose ASCII code is NNN (octal).\n"); printf("\n\ Without -M, the following macros are recognized and executed:\n\ \n\ @(env,var) Get text from named EnvVar\n\ @(envmacro,var) Process text from named EnvVar\n\ @(dosify,path) Convert the specify path to DOS conventions (8.3)\n\ @(realpath,path) Determine the absolute path, removing . and ..\n\ and resolving relative references\n\ @(shortpath,path) Convert the path into a short path (WIN32 specific).\n\ @(vhost,ident) Override the default host (Unix or WIN32).\n\ @(vsep,ident) Override the default path sep(colon or semicolon).\n\ @(sep,c,text) Use path element sep characacter and clean text.\n\ @(vlibgcc,path) Invoke the specific gcc version to determine the\n\ default library search directories.\n\ @(vpath[23],n,path,[sep]) Resolve 'name' using search 'path'.\n\ @(vlib[23],n,path,[sep]) Resolve library 'name' using the search 'path',\n\ @(vlint[23],n,path,[sep]) Resolve lint resource 'n' using the search 'path'.\n\ @(vglob[23],p,path,[sep]) Match the pattern 'p' using the search 'path'.\n\ \n\ The vpath, vlib, vglob and vlint macros have 3 forms that determine the\n\ handling of a \"not found\" condition.\n\ \n\ 1 (default) Search name/pattern is quoted as specified.\n\ 2 Fatal exit, stating target name/pattern not found.\n\ 3 Outputs nothing.\n\ \n\ The vpath, vlib, vglob and vlint macros take an optional 3rd argument\n\ This is a single character that will be used to seperate the elements\n\ in the output path. Normally determined from the content 'path' argument.\n\ \n"); #if defined(DEFUNC) printf("\n\ @(url2path,url) Convert the URL encoded path to a native path.\n\ @(path2url,url) Convert the path using URL encoding rules.\n\ \n"); #endif exit (status); } void message(const char *msg, ...) { va_list args; FILE *out = cflg ? stderr : stdout; va_start(args, msg); vfprintf(out, msg, args); va_end(args); } void fatalerr(const char *msg, ...) { va_list args; FILE *out = cflg ? stderr : stdout; fprintf(out, "%s: *** Error: ", program_name); va_start(args, msg); vfprintf(out, msg, args); fprintf(out, ". Stop\n"); va_end(args); exit(1); } void warning(const char *msg, ...) { va_list args; FILE *out = cflg ? stderr : stdout; fprintf(out, "%s: *** Warning: ", program_name); va_start(args, msg); vfprintf(out, msg, args); fprintf(out, ".\n"); va_end(args); } void verbose(const char *msg, ...) { va_list args; if (!vflg) return; va_start(args, msg); vprintf(msg, args); va_end(args); printf(".\n"); if (vflg >= 2) fflush(stdout); } static void cleanup(void) { if (oflg && tmp_name[0]) { verbose( " removing(%s)", tmp_name ); if (tmp_fd != -1) close(tmp_fd); if (!tflg) remove(tmp_name); } }