Rev 313 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/* -*- 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 DescriptionAPY 12/99 CreatedAPY 14/03/00 When '-o' switch, use temp output andrename on success ...Additional command line helpv1.1APY 16/04/02 realpath macroAPY 08/12/03 - MRI kludges (k2 and r?) (v1.2)- CMDFILE env variable support26/04/04 - shortpath- macro argument single and double quotedstrings processing.- realpath2- url2path- path2url- 1.2105/05/04 - 'w', 'd' and 'm' switches- 1.2210/05/04 - handle leading white space within paths.- 1.2317/05/04 - vlibgcc- 1.2423/06/04 - Whitespace handling -W switch- 1.2523/06/04 - Fixed macro buffer overflowDP 23/09/04 - vpath() generates more readable errormessages when a library file cannot befound in vpath.- 1.26APY 05/10/04 vpath3, vlib3 and vglob3Path 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 <sys/stat.h>#include <sys/types.h>#include <stdarg.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include "cmdfile.h"#include "longopt.h"#define NEWLINE -2#define SEPERATOR -3const 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#endifmain(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)#elseif ((afd = open(argv[1]+1, O_RDONLY)) < 0)#endiffatalerr("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 voidPUTC(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 voidPUTS(const char *s){if (vflg > 2)verbose( "PUTS[]%s", s );while (*s)__PUTC(*s++);}static voidFLUSH(void){if (linelen){register const char *p = linebuffer;linebuffer[ linelen ] = '\0';while (*p) /* flush line buffer */putc( *p++, output );linelen = 0;}}static intinline_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 voiddo_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 voidusage(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<file> 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");#endifexit (status);}voidmessage(const char *msg, ...){va_list args;FILE *out = cflg ? stderr : stdout;va_start(args, msg);vfprintf(out, msg, args);va_end(args);}voidfatalerr(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);}voidwarning(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);}voidverbose(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 voidcleanup(void){if (oflg && tmp_name[0]){verbose( " removing(%s)", tmp_name );if (tmp_fd != -1)close(tmp_fd);if (!tflg)remove(tmp_name);}}