Subversion Repositories DevTools

Rev

Rev 313 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
313 dpurdie 1
/* -*- mode: c; tabs: 4; -*- ************************************************
2
* Module name   : cmdfile.c
3
* Module type   : CMDFILE source file
4
* Environment(s): n/a
5
*
6
* Description:
7
*
8
    CMDFILE --- command line file builder
9
*
10
* Version   Who     Date        Description
11
            APY     12/99       Created
12
            APY     14/03/00    When '-o' switch, use temp output and
13
                                    rename on success ...
14
                                Additional command line help
15
                                v1.1
16
            APY     16/04/02    realpath macro
17
            APY     08/12/03    - MRI kludges (k2 and r?) (v1.2)
18
                                - CMDFILE env variable support
19
                    26/04/04    - shortpath
20
                                - macro argument single and double quoted
21
                                    strings processing.
22
                                - realpath2
23
                                - url2path
24
                                - path2url
25
                                - 1.21
26
                    05/05/04    - 'w', 'd' and 'm' switches
27
                                - 1.22
28
                    10/05/04    - handle leading white space within paths.
29
                                - 1.23
30
                    17/05/04    - vlibgcc
31
                                - 1.24
32
                    23/06/04    - Whitespace handling -W switch
33
                                - 1.25
34
                    23/06/04    - Fixed macro buffer overflow
35
            DP      23/09/04    - vpath() generates more readable error
36
                                    messages when a library file cannot be
37
                                    found in vpath.
38
                                - 1.26
39
            APY     05/10/04    vpath3, vlib3 and vglob3
40
                                Path arguments support embedded spaces.
41
                                Extended usage
42
                                @vhost and @vsep
43
                                @vlint
44
                                - 1.30
45
*
46
* $Source: /cvsroot/device/DEVL/UTILS/CMDFILE/cmdfile.c,v $
47
* $Revision: 1.11 $ $Date: 2004/11/05 07:51:39 $ $State: Exp $
48
* $Author: ayoung $ $Locker:  $
49
*.........................................................................*/
50
 
51
#include <sys/stat.h>
52
#include <sys/types.h>
53
#include <stdarg.h>
54
#include <stdio.h>
55
#include <string.h>
56
#include <stdlib.h>
57
#include <fcntl.h>
58
#include <unistd.h>
59
#include "cmdfile.h"
60
#include "longopt.h"
61
 
62
#define NEWLINE     -2
63
#define SEPERATOR   -3
64
 
65
const char *        version_string      = "1.31";
66
char *              program_name        = "";
67
 
68
char                tmp_buf[15]         = TMPNAME;
69
char *              tmp_name            = tmp_buf;
70
int                 tmp_fd              = -1;
71
FILE *              output              = NULL;
72
 
73
char                mflg                = '@';
74
char                dflg[2]             = { '(', ')' };
75
 
76
int                 cflg                = 0;
77
int                 eflg                = 1;
78
int                 kflg                = 0;
79
int                 k2flg               = 0;
80
int                 Mflg                = 0;
81
int                 nflg                = 1;
82
int                 nlflg               = 0;
83
const char *        oflg                = NULL;
84
int                 rflg                = 0;
85
char                rtags[10]           = { 0 };
86
int                 tflg                = 0;
87
int                 vflg                = 0;
88
int                 wflg                = 0;
89
int                 whitespace          = WS_UNKNOWN;
317 dpurdie 90
int                 nl = FALSE;
313 dpurdie 91
 
92
static int          linelen = 0;
93
static char         linebuffer[ 32*1024 ];      /* 32k line buffer */
94
 
95
 
96
static void         PUTC(int c);
97
static void         PUTS(const char *s);
98
static void         FLUSH(void);
99
 
100
static int          inline_realpath(char *line, int *length);
101
static void         do_padkludge(void);
102
static void         usage(int status);
103
static void         cleanup(void);
104
 
105
 
106
/* Print the words in LIST to standard output.  If the first word
107
    *   is '-n', then don't print a trailing newline.  We also
108
    *   support the echo syntax from Version 9 unix systems.
109
    */
110
int
111
#if defined(WIN32)
112
__cdecl
113
#endif
114
main(int argc, char **argv)
115
{
116
    output = stdout;
117
 
118
    if ((program_name = strrchr(argv[0], '/')) == NULL &&
119
            (program_name = strrchr(argv[0], '\\')) == NULL)
120
        program_name = argv[0];
121
    else program_name++;
122
 
123
#if defined(WIN32) && (0)
124
    rflg = -1;                                  /* MRI kludge (v2)*/
125
    strcpy(rtags, "IJ");
126
#endif
127
 
128
/* Handle command file */
129
    if (argc == 2 && argv[1][0] == '@')
130
    {
131
        struct  stat ast;
132
        int     afd, len;
133
        char    *args;
134
        char    **nargv;
135
        int     nargc;
136
        char    quotechar = '\0';
137
        char    *p;
138
 
139
        nargc = 1;
140
#if defined(MSDOS) || defined(WIN32)
141
        if ((afd = open(argv[1]+1, O_RDONLY|O_BINARY)) < 0)
142
#else
143
        if ((afd = open(argv[1]+1, O_RDONLY)) < 0)
144
#endif
145
            fatalerr("Cannot open \"%s\"", argv[1]+1);
146
        fstat(afd, &ast);
147
        if ((args = (char *)malloc(ast.st_size + 1)) == NULL)
148
            fatalerr("Cannot allocate read buffer");
149
        args[ ast.st_size ] = '\0';
150
        do {
151
            if ((len = read(afd, args, ast.st_size)) < 0)
152
                fatalerr("Failed to read %s", argv[1]+1);
153
        } while ((ast.st_size -= len) > 0);
154
 
155
        close(afd);
156
        for (p = args; *p; p++)
157
        {
158
            if (quotechar)
159
            {
160
                if (quotechar == '\\' ||
161
                            (*p == quotechar && p[-1] != '\\'))
162
                        quotechar = '\0';
163
                    continue;
164
            }
165
            switch (*p) {
166
            case '\\':
167
            case '"':
168
            case '\'':
169
                quotechar = *p;
170
                break;
171
            case ' ':
172
            case '\n':
173
               *p = '\0';
174
               if (p > args && p[-1])
175
                  nargc++;
176
               break;
177
            case '\r':
178
               *p = '\0';
179
               if (p > args && p[-1])
180
                  nargc++;
181
               if (p[1] == '\n')
182
                  *++p = '\0';
183
               break;
184
            }
185
        }
186
        if (p[-1])
187
            nargc++;
188
        nargv = (char **)malloc(nargc * sizeof(char *));
189
        nargv[0] = argv[0];
190
        argc = 1;
191
        for (p = args; argc < nargc; p += strlen(p) + 1)
192
            if (*p)
193
                nargv[argc++] = p;
194
        argv = nargv;
195
    }
196
 
197
/* Long options (specials),
198
    *   --help
199
    *   --version
200
    */
201
    parse_long_options( argc, argv, "cmdfile", version_string, usage );
202
    argc--, argv++;
203
 
204
/* Import CMDFILE environment variable */
205
    if (getenv("CMDFILE"))
206
    {
317 dpurdie 207
printf( "PARSE ENV OPTS\n" );
313 dpurdie 208
        parse_short_argument( getenv("CMDFILE" ) );
209
    }
210
 
211
/* Standard options (only allowed as first command) */
212
    if (argc > 0 && *argv[0] == '-')
213
        if (parse_short_argument( argv[0]+1 ) > 0)
214
        {
215
            if (oflg)
216
            {
217
                if ((tmp_fd = mkstemp( tmp_name )) == -1)
218
                    fatalerr( "Cannot build temp file name \"%s\" : %d (%s)",
219
                        tmp_name, errno, strerror(errno));
220
 
221
                atexit( cleanup );              /* temp name cleanup */
222
 
223
                if ((output = fdopen( tmp_fd, "wb+" )) == NULL)
224
                    fatalerr( "Cannot fdopen \"%s\" : %d (%s)",
225
                        tmp_name, errno, strerror(errno));
226
            }
227
 
228
            argc--, argv++;                     /* remove argument */
229
        }
230
 
231
    if (vflg > 2) 
232
    {
233
        verbose( "Options:" );
234
        if (cflg)   verbose( "  Redirecting errors to stdout." );
235
        if (nflg)   verbose( "  No trailing newline" );
236
        if (nlflg)  verbose( "  Newline seperator" );
237
        if (wflg)   verbose( "  Consuming leading whitespace" );
238
        if (!Mflg)  verbose( "  Procesing macros" );
239
        if (!eflg)  verbose( "  Not processing escape sequences" );
240
        if (eflg)   verbose( "  Level %d escape sequence processing", eflg );
241
    }
242
 
243
/* Process remaining */
244
    if (argc > 0)
245
    {
246
        int argi;
317 dpurdie 247
        nl = FALSE;
313 dpurdie 248
 
249
        for (argi = 0; argc > 0; argi++)
250
        {
251
            if (vflg > 2)
317 dpurdie 252
                verbose( "ARGV[%d]%s", argi, argv[0] );
313 dpurdie 253
 
317 dpurdie 254
            process_string ( argv[0] );
313 dpurdie 255
 
256
            argc--, argv++;                     /* next argument */
257
 
258
            if (argc > 0 && nl == FALSE)
259
            {                                   /* seperator */
260
                PUTC(SEPERATOR);
261
                if (nlflg) nl = TRUE;
262
            }
263
        }
264
    }
265
 
266
    if (nflg)
267
        PUTC(NEWLINE);
268
 
269
    FLUSH();
270
 
271
/* Rename 'temp' */
272
    if (oflg)
273
    {
274
        if (k2flg)
275
            do_padkludge();
276
 
277
        fclose(output);
278
        tmp_fd = -1;
279
 
280
        (void) remove(oflg);
281
        verbose( " renaming(%s to %s)", tmp_name, oflg );
282
        if (rename(tmp_name, oflg) == -1)
283
            fatalerr("Cannot rename temp file \"%s\" to \"%s\" : %d (%s)",
284
                tmp_name, oflg, errno, strerror(errno));
285
        tmp_name[0] = '\0';                     /* stop cleanup of tmp_name */
286
    }
287
    return (0);
288
}
289
 
317 dpurdie 290
/*----------------------------------------------------------------------------
291
** FUNCTION           : process_string
292
**
293
** DESCRIPTION        : Process a string
294
**                      It may be from an argument, it may be from a macro
295
**
296
**
297
** INPUTS             : s               - String to process
298
**
299
** RETURNS            : Nothing
300
**
301
----------------------------------------------------------------------------*/
313 dpurdie 302
 
317 dpurdie 303
void process_string (register char *s )
304
{
305
    register int c = '\0';
306
 
307
    while ((c = *s++) != '\0')
308
    {
309
        /*
310
         *  Escape processing ...
311
         */
312
        if (eflg && c == '\\' && *s)
313
        {
314
            switch (c = *s++)
315
            {
316
            case '\n':                  /* concat line */
317
                nl = TRUE, c = '\0';
318
                break;
319
            case 'n':                   /* \n */
320
                c = NEWLINE;
321
                break;
322
            case '\\':                  /* backslash */
323
                break;
324
            default:
325
                if (eflg >= 2) {        /* extended */
326
                    switch (c)
327
                    {
328
                    case 'a': c = '\007'; break;
329
                    case 'b': c = '\b'; break;
330
                    case 'c': nflg = 0; break;
331
                    case 'f': c = '\f'; break;
332
                    case 'r': c = '\r'; break;
333
                    case 't': c = '\t'; break;
334
                    case 'v': c = (int) 0x0B; break;
335
                    case '0': case '1': case '2': case '3':
336
                    case '4': case '5': case '6': case '7':
337
                        c -= '0';
338
                        if (*s >= '0' && *s <= '7')
339
                            c = c * 8 + (*s++ - '0');
340
                        if (*s >= '0' && *s <= '7')
341
                            c = c * 8 + (*s++ - '0');
342
                        break;
343
                    default:
344
                        PUTC('\\');     /* unknown echo */
345
                        break;
346
                    }
347
                } else {
348
                    PUTC('\\');         /* unknown echo */
349
                }
350
                break;
351
            }
352
        }
353
 
354
        /*
355
         *  Macros ...
356
         */
357
        else if (Mflg == 0 && c == MACROCHAR && *s)
358
        {
359
            const char *str;
360
            String_t     mstr;
361
 
362
            StringInit( &mstr, NULL, MACRO_MAX );       /* macro resource */
363
            s += macro(&mstr, s);
364
            if ((str = StringData(&mstr)) != NULL && str[0]) {
365
                PUTS(str);
366
                nl = FALSE;
367
            }
368
            free( StringData(&mstr) );
369
            c = '\0';
370
        }
371
 
372
        /*
373
         *  Default output ...
374
         */
375
        if (c != '\0')
376
        {
377
            if (nl && (c == ' ' || c == '\t') && wflg)
378
                continue;               /* eat leading white space */
379
 
380
            if (c == '\n' || c == NEWLINE)
381
                nl = TRUE;
382
            else nl = FALSE;
383
 
384
            PUTC(c);                    /* output character */
385
        }
386
    }
387
 
388
}
389
 
313 dpurdie 390
static void
391
__PUTC(int c)
392
{
393
    int     flushit = 0;
394
 
395
    if (c == NEWLINE || c == '\r' || c == '\n') /* flush on newline */
396
        flushit = 1;
397
 
398
    if (flushit && linelen > 2)
399
        if (rflg && linebuffer[0] == '-')       /* inline realpath */
400
        {
401
            inline_realpath( linebuffer, &linelen );
402
        }
403
 
404
    if (c == NEWLINE) {
405
        if (kflg)
406
            linebuffer[ linelen++ ] = '\r';
407
        linebuffer[ linelen++ ] = '\n';
408
    } else {
409
        linebuffer[ linelen++ ] = c;            /* append character */
410
    }
411
 
412
    if (flushit)
413
        FLUSH();
414
}
415
 
416
 
417
static void
418
PUTC(int c)
419
{
420
    if (vflg > 2) {
421
        if (c == NEWLINE)
422
            verbose( "PUTC[NEWLINE]" );
423
        else if (c == SEPERATOR)
424
            verbose( "PUTC[SEPERATOR]" );
425
        else {
426
            verbose( "PUTC[]%c", c );
427
        }
428
    }
429
    if (c == SEPERATOR)
430
        c = (nlflg ? NEWLINE : ' ');
431
    __PUTC(c);
432
}
433
 
434
 
435
static void
436
PUTS(const char *s)
437
{
438
    if (vflg > 2)
439
        verbose( "PUTS[]%s", s );
440
    while (*s)
441
        __PUTC(*s++);
442
}
443
 
444
 
445
static void
446
FLUSH(void)
447
{
448
    if (linelen)
449
    {
450
        register const char *p = linebuffer;
451
 
452
        linebuffer[ linelen ] = '\0';
453
        while (*p)                              /* flush line buffer */
454
            putc( *p++, output );
455
        linelen = 0;
456
    }
457
}
458
 
459
 
460
static int
461
inline_realpath(char *line, int *length)
462
{
463
    const   char *s = line;
464
    char    buf[ PATH_MAX ], *pbuf;
465
    int     space = 0;
466
    char    tag, sep;
467
    int     i;
468
 
469
    sep = *s++;
470
    tag = *s++;
471
    if (strchr(rtags, tag) == 0)                /* known tag? */
472
        return (0);
473
 
474
    while (s[0] == ' ' || s[0] == '\t' )
475
        space++, s++;
476
 
477
    line[ *length ] = '\0';                     /* terminate line */
478
 
479
    if (s[0] == '\0' ||
480
            Realpath(s, buf) == NULL)           /* expand */
481
        return (-1);
482
 
483
    verbose( " inline realpath(%s) = '-%c%s'", s, tag, buf );
484
 
485
    i = 0;
486
    line[ i++ ] = sep;
487
    line[ i++ ] = tag;
488
    if (space)
489
        line[ i++ ] = ' ';
490
    for (pbuf = buf; (line[ i++ ] = *pbuf) != '\0'; pbuf++)
491
        /*continue*/;
492
 
493
    *length = i-1;
494
 
495
    return (i);
496
}
497
 
498
 
499
static void
500
do_padkludge(void)
501
{
502
    long    flen, pad = 0;
503
 
504
    fflush(output);
505
    flen = ftell(output);
506
    if (flen > 440 && flen < 500)
507
        pad = 540 - flen;
508
    else if (flen > 740 && flen < 800)
509
        pad = 840 - flen;
510
 
511
    if (pad)
512
    {
513
        char    *image;                         /* current file image */
514
 
515
        verbose(" padding output (%d --> %d)", flen, flen+pad );
516
 
517
        if ((image = (void *)calloc(flen, 1)) == NULL)
518
            fatalerr("Memory allocation error");
519
        if (fseek(output, 0L, SEEK_SET) == -1 ||
520
                (int)fread(image, sizeof(char), flen, output) != flen)
521
            fatalerr("reading : %d (%s)", errno, strerror(errno));
522
 
523
        fseek(output, 0L, SEEK_SET);
524
        fputs("-DPADKLUDGE", output);
525
        if ((pad -= (12 + (kflg ? 2 : 1))) > 0)
526
        {
527
            fputs("=", output);
528
            while (pad-- > 0)
529
                fputc( "1234567890"[pad%10], output);
530
        }
531
        if (kflg)
532
            fputc('\r', output);
533
        fputc('\n', output);
534
 
535
        if ((int)fwrite(image, sizeof(char), flen, output) != flen)
536
            fatalerr("rewriting : %d (%s)", errno, strerror(errno));
537
        free(image);
538
    }
539
}
540
 
541
 
542
static void
543
usage(int status)
544
{
545
    if (status != 0) {
546
        fprintf(stderr, "Try `%s --help' for more information.\n",
547
            program_name);
548
        exit(status);
549
    }
550
 
551
printf("Usage: %s [-OPTION[OPTION]]... [STRING]...\n", program_name);
552
printf("Usage: %s @cmdfile\n", program_name);
553
printf("\n\
554
Options, all must be encoded as the first argument.\n\
555
\n\
556
  -n                   Do not output the trailing newline.\n\
557
  -N                   Use newline as argument seperator (default space).\n\
558
  -m?                  Alternative macro identifier (eg -m$, default '@').\n\
559
  -d?                  Alternative macro delimitor set (eg -m[, default '(').\n\
560
                        '[' implies the delimitor set of \"[]\", '(' = \"()\",\n\
561
                        '{' = \"{}\" and '<' implies \"<>\".\n\
562
  -k|-k1               DOS lf/cr kludge.\n\
563
  -k2                  MRI file padding Kludge.\n\
564
  -w                   Trim leading white after newlines.\n\
565
  -W{method}           Method of handling embedded whitespace within paths.\n\
566
    0,-                 - Disable warnings.\n\
567
    1,e                 - Escape (eg /Embedded\\ Space/x.h).\n\
568
    2,q                 - Quote the entire path (eg \"/Embedded Space/x.h\").\n\
569
  -r?                  Realpath expansion kludge (eg -rI, expands -Ipath)\n\
570
  -v                   Verbose output.\n\
571
  -c                   Redirect errors to stdout.\n\
572
  -E                   Disable interpolation of some sequences in STRINGs.\n\
573
  -e                   Enable extended interpolation.\n\
574
  -M                   Disable macros.\n\
575
  -t                   Dont remove temporary working files.\n\
576
  -o<file>             Output file (must be the last option).\n");
577
printf("\n\
578
\n\
579
The follow options must be specified alone;\n\
580
\n\
581
  --help               Display this help and exit\n\
582
  --version            Output version information and exit\n");
583
printf("\n\
584
Without -E, the following sequences are recognized and interpolated:\n\
585
\n\
586
  \\n                   New line.\n\
587
  \\\\                   Backslash.\n");
588
printf("\n\
589
If the -e option is given, the following additional sequences are\n\
590
recognized and interpolated:\n\
591
\n\
592
  \\a                   Alert (bell).\n\
593
  \\b                   Backspace.\n\
594
  \\c                   Suppress trailing newline (same as -n).\n\
595
  \\f                   Form feed.\n\
596
  \\r                   Carriage return.\n\
597
  \\t                   Horizontal tab.\n\
598
  \\v                   Vertical tab.\n\
599
  \\NNN                 The character whose ASCII code is NNN (octal).\n");
600
printf("\n\
601
Without -M, the following macros are recognized and executed:\n\
602
\n\
317 dpurdie 603
  @(env,var)           Get text from named EnvVar\n\
604
  @(envmacro,var)      Process text from named EnvVar\n\
313 dpurdie 605
  @(dosify,path)       Convert the specify path to DOS conventions (8.3)\n\
606
  @(realpath,path)     Determine the absolute path, removing . and ..\n\
607
                       and resolving relative references\n\
608
  @(shortpath,path)    Convert the path into a short path (WIN32 specific).\n\
609
  @(vhost,ident)       Override the default host (Unix or WIN32).\n\
610
  @(vsep,ident)        Override the default path sep(colon or semicolon).\n\
611
  @(sep,c,text)        Use path element sep characacter and clean text.\n\
612
  @(vlibgcc,path)      Invoke the specific gcc version to determine the\n\
613
                       default library search directories.\n\
614
  @(vpath[23],n,path,[sep])  Resolve 'name' using search 'path'.\n\
615
  @(vlib[23],n,path,[sep])   Resolve library 'name' using the search 'path',\n\
616
  @(vlint[23],n,path,[sep])  Resolve lint resource 'n' using the search 'path'.\n\
617
  @(vglob[23],p,path,[sep])  Match the pattern 'p' using the search 'path'.\n\
618
\n\
619
The vpath, vlib, vglob and vlint macros have 3 forms that determine the\n\
620
handling of a \"not found\" condition.\n\
621
\n\
622
  1 (default)          Search name/pattern is quoted as specified.\n\
623
  2                    Fatal exit, stating target name/pattern not found.\n\
624
  3                    Outputs nothing.\n\
625
\n\
626
The vpath, vlib, vglob and vlint macros take an optional 3rd argument\n\
627
This is a single character that will be used to seperate the elements\n\
628
in the output path. Normally determined from the content 'path' argument.\n\
629
\n");
630
 
631
#if defined(DEFUNC)
632
printf("\n\
633
  @(url2path,url)      Convert the URL encoded path to a native path.\n\
634
  @(path2url,url)      Convert the path using URL encoding rules.\n\
635
\n");
636
#endif
637
 
638
    exit (status);
639
}
640
 
641
void
642
message(const char *msg, ...)
643
{
644
    va_list args;
645
    FILE    *out = cflg ? stderr : stdout;
646
 
647
    va_start(args, msg);
648
    vfprintf(out, msg, args);
649
    va_end(args);
650
}
651
 
652
 
653
void
654
fatalerr(const char *msg, ...)
655
{
656
    va_list args;
657
    FILE    *out = cflg ? stderr : stdout;
658
 
659
    fprintf(out, "%s: *** Error: ", program_name);
660
    va_start(args, msg);
661
    vfprintf(out, msg, args);
662
    fprintf(out, ". Stop\n");
663
    va_end(args);
664
    exit(1);
665
}
666
 
667
 
668
void
669
warning(const char *msg, ...)
670
{
671
    va_list args;
672
    FILE    *out = cflg ? stderr : stdout;
673
 
674
    fprintf(out, "%s: *** Warning: ", program_name);
675
    va_start(args, msg);
676
    vfprintf(out, msg, args);
677
    fprintf(out, ".\n");
678
    va_end(args);
679
}
680
 
681
 
682
void
683
verbose(const char *msg, ...)
684
{
685
    va_list args;
686
 
687
    if (!vflg)
688
        return;
689
 
690
    va_start(args, msg);
691
    vprintf(msg, args);
692
    va_end(args);
693
 
694
    printf(".\n");
695
    if (vflg >= 2)
696
        fflush(stdout);
697
}
698
 
699
 
700
static void
701
cleanup(void)
702
{
703
    if (oflg && tmp_name[0])
704
    {
705
        verbose( " removing(%s)", tmp_name );
706
        if (tmp_fd != -1)
707
            close(tmp_fd);
708
        if (!tflg)
709
        remove(tmp_name);
710
    }
711
}
712