Subversion Repositories DevTools

Rev

Go to most recent revision | Details | 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;
90
 
91
static int          linelen = 0;
92
static char         linebuffer[ 32*1024 ];      /* 32k line buffer */
93
 
94
static String_t     mstr;
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
    {
207
        parse_short_argument( getenv("CMDFILE" ) );
208
    }
209
 
210
/* Standard options (only allowed as first command) */
211
    if (argc > 0 && *argv[0] == '-')
212
        if (parse_short_argument( argv[0]+1 ) > 0)
213
        {
214
            if (oflg)
215
            {
216
                if ((tmp_fd = mkstemp( tmp_name )) == -1)
217
                    fatalerr( "Cannot build temp file name \"%s\" : %d (%s)",
218
                        tmp_name, errno, strerror(errno));
219
 
220
                atexit( cleanup );              /* temp name cleanup */
221
 
222
                if ((output = fdopen( tmp_fd, "wb+" )) == NULL)
223
                    fatalerr( "Cannot fdopen \"%s\" : %d (%s)",
224
                        tmp_name, errno, strerror(errno));
225
            }
226
 
227
            argc--, argv++;                     /* remove argument */
228
        }
229
 
230
    if (vflg > 2) 
231
    {
232
        verbose( "Options:" );
233
        if (cflg)   verbose( "  Redirecting errors to stdout." );
234
        if (nflg)   verbose( "  No trailing newline" );
235
        if (nlflg)  verbose( "  Newline seperator" );
236
        if (wflg)   verbose( "  Consuming leading whitespace" );
237
        if (!Mflg)  verbose( "  Procesing macros" );
238
        if (!eflg)  verbose( "  Not processing escape sequences" );
239
        if (eflg)   verbose( "  Level %d escape sequence processing", eflg );
240
    }
241
 
242
/* Process remaining */
243
    StringInit( &mstr, NULL, MACRO_MAX );       /* macro resource */
244
    if (argc > 0)
245
    {
246
        int nl = FALSE;
247
        int argi;
248
 
249
        for (argi = 0; argc > 0; argi++)
250
        {
251
            register char *s = argv[0];
252
            register int c = '\0';
253
 
254
            if (vflg > 2)
255
                verbose( "ARGV[%d]%s", argi, s );
256
 
257
            while ((c = *s++) != '\0')
258
            {
259
                /*
260
                 *  Escape processing ...
261
                 */
262
                if (eflg && c == '\\' && *s)
263
                {
264
                    switch (c = *s++)
265
                    {
266
                    case '\n':                  /* concat line */
267
                        nl = TRUE, c = '\0';
268
                        break;
269
                    case 'n':                   /* \n */
270
                        c = NEWLINE;
271
                        break;
272
                    case '\\':                  /* backslash */
273
                        break;
274
                    default:
275
                        if (eflg >= 2) {        /* extended */
276
                            switch (c)
277
                            {
278
                            case 'a': c = '\007'; break;
279
                            case 'b': c = '\b'; break;
280
                            case 'c': nflg = 0; break;
281
                            case 'f': c = '\f'; break;
282
                            case 'r': c = '\r'; break;
283
                            case 't': c = '\t'; break;
284
                            case 'v': c = (int) 0x0B; break;
285
                            case '0': case '1': case '2': case '3':
286
                            case '4': case '5': case '6': case '7':
287
                                c -= '0';
288
                                if (*s >= '0' && *s <= '7')
289
                                    c = c * 8 + (*s++ - '0');
290
                                if (*s >= '0' && *s <= '7')
291
                                    c = c * 8 + (*s++ - '0');
292
                                break;
293
                            default:
294
                                PUTC('\\');     /* unknown echo */
295
                                break;
296
                            }
297
                        } else {
298
                            PUTC('\\');         /* unknown echo */
299
                        }
300
                        break;
301
                    }
302
                }
303
 
304
                /*
305
                 *  Macros ...
306
                 */
307
                else if (Mflg == 0 && c == MACROCHAR && *s)
308
                {
309
                    const char *str;
310
 
311
                    StringZero(&mstr);
312
                    s += macro(&mstr, s);
313
                    if ((str = StringData(&mstr)) != NULL && str[0]) {
314
                        PUTS(str);
315
                        nl = FALSE;
316
                    }
317
                    c = '\0';
318
                }
319
 
320
                /*
321
                 *  Default output ...
322
                 */
323
                if (c != '\0')
324
                {
325
                    if (nl && (c == ' ' || c == '\t') && wflg)
326
                        continue;               /* eat leading white space */
327
 
328
                    if (c == '\n' || c == NEWLINE)
329
                        nl = TRUE;
330
                    else nl = FALSE;
331
 
332
                    PUTC(c);                    /* output character */
333
                }
334
            }
335
 
336
            argc--, argv++;                     /* next argument */
337
 
338
            if (argc > 0 && nl == FALSE)
339
            {                                   /* seperator */
340
                PUTC(SEPERATOR);
341
                if (nlflg) nl = TRUE;
342
            }
343
        }
344
    }
345
 
346
    if (nflg)
347
        PUTC(NEWLINE);
348
 
349
    FLUSH();
350
 
351
/* Rename 'temp' */
352
    if (oflg)
353
    {
354
        if (k2flg)
355
            do_padkludge();
356
 
357
        fclose(output);
358
        tmp_fd = -1;
359
 
360
        (void) remove(oflg);
361
        verbose( " renaming(%s to %s)", tmp_name, oflg );
362
        if (rename(tmp_name, oflg) == -1)
363
            fatalerr("Cannot rename temp file \"%s\" to \"%s\" : %d (%s)",
364
                tmp_name, oflg, errno, strerror(errno));
365
        tmp_name[0] = '\0';                     /* stop cleanup of tmp_name */
366
    }
367
    return (0);
368
}
369
 
370
 
371
static void
372
__PUTC(int c)
373
{
374
    int     flushit = 0;
375
 
376
    if (c == NEWLINE || c == '\r' || c == '\n') /* flush on newline */
377
        flushit = 1;
378
 
379
    if (flushit && linelen > 2)
380
        if (rflg && linebuffer[0] == '-')       /* inline realpath */
381
        {
382
            inline_realpath( linebuffer, &linelen );
383
        }
384
 
385
    if (c == NEWLINE) {
386
        if (kflg)
387
            linebuffer[ linelen++ ] = '\r';
388
        linebuffer[ linelen++ ] = '\n';
389
    } else {
390
        linebuffer[ linelen++ ] = c;            /* append character */
391
    }
392
 
393
    if (flushit)
394
        FLUSH();
395
}
396
 
397
 
398
static void
399
PUTC(int c)
400
{
401
    if (vflg > 2) {
402
        if (c == NEWLINE)
403
            verbose( "PUTC[NEWLINE]" );
404
        else if (c == SEPERATOR)
405
            verbose( "PUTC[SEPERATOR]" );
406
        else {
407
            verbose( "PUTC[]%c", c );
408
        }
409
    }
410
    if (c == SEPERATOR)
411
        c = (nlflg ? NEWLINE : ' ');
412
    __PUTC(c);
413
}
414
 
415
 
416
static void
417
PUTS(const char *s)
418
{
419
    if (vflg > 2)
420
        verbose( "PUTS[]%s", s );
421
    while (*s)
422
        __PUTC(*s++);
423
}
424
 
425
 
426
static void
427
FLUSH(void)
428
{
429
    if (linelen)
430
    {
431
        register const char *p = linebuffer;
432
 
433
        linebuffer[ linelen ] = '\0';
434
        while (*p)                              /* flush line buffer */
435
            putc( *p++, output );
436
        linelen = 0;
437
    }
438
}
439
 
440
 
441
static int
442
inline_realpath(char *line, int *length)
443
{
444
    const   char *s = line;
445
    char    buf[ PATH_MAX ], *pbuf;
446
    int     space = 0;
447
    char    tag, sep;
448
    int     i;
449
 
450
    sep = *s++;
451
    tag = *s++;
452
    if (strchr(rtags, tag) == 0)                /* known tag? */
453
        return (0);
454
 
455
    while (s[0] == ' ' || s[0] == '\t' )
456
        space++, s++;
457
 
458
    line[ *length ] = '\0';                     /* terminate line */
459
 
460
    if (s[0] == '\0' ||
461
            Realpath(s, buf) == NULL)           /* expand */
462
        return (-1);
463
 
464
    verbose( " inline realpath(%s) = '-%c%s'", s, tag, buf );
465
 
466
    i = 0;
467
    line[ i++ ] = sep;
468
    line[ i++ ] = tag;
469
    if (space)
470
        line[ i++ ] = ' ';
471
    for (pbuf = buf; (line[ i++ ] = *pbuf) != '\0'; pbuf++)
472
        /*continue*/;
473
 
474
    *length = i-1;
475
 
476
    return (i);
477
}
478
 
479
 
480
static void
481
do_padkludge(void)
482
{
483
    long    flen, pad = 0;
484
 
485
    fflush(output);
486
    flen = ftell(output);
487
    if (flen > 440 && flen < 500)
488
        pad = 540 - flen;
489
    else if (flen > 740 && flen < 800)
490
        pad = 840 - flen;
491
 
492
    if (pad)
493
    {
494
        char    *image;                         /* current file image */
495
 
496
        verbose(" padding output (%d --> %d)", flen, flen+pad );
497
 
498
        if ((image = (void *)calloc(flen, 1)) == NULL)
499
            fatalerr("Memory allocation error");
500
        if (fseek(output, 0L, SEEK_SET) == -1 ||
501
                (int)fread(image, sizeof(char), flen, output) != flen)
502
            fatalerr("reading : %d (%s)", errno, strerror(errno));
503
 
504
        fseek(output, 0L, SEEK_SET);
505
        fputs("-DPADKLUDGE", output);
506
        if ((pad -= (12 + (kflg ? 2 : 1))) > 0)
507
        {
508
            fputs("=", output);
509
            while (pad-- > 0)
510
                fputc( "1234567890"[pad%10], output);
511
        }
512
        if (kflg)
513
            fputc('\r', output);
514
        fputc('\n', output);
515
 
516
        if ((int)fwrite(image, sizeof(char), flen, output) != flen)
517
            fatalerr("rewriting : %d (%s)", errno, strerror(errno));
518
        free(image);
519
    }
520
}
521
 
522
 
523
static void
524
usage(int status)
525
{
526
    if (status != 0) {
527
        fprintf(stderr, "Try `%s --help' for more information.\n",
528
            program_name);
529
        exit(status);
530
    }
531
 
532
printf("Usage: %s [-OPTION[OPTION]]... [STRING]...\n", program_name);
533
printf("Usage: %s @cmdfile\n", program_name);
534
printf("\n\
535
Options, all must be encoded as the first argument.\n\
536
\n\
537
  -n                   Do not output the trailing newline.\n\
538
  -N                   Use newline as argument seperator (default space).\n\
539
  -m?                  Alternative macro identifier (eg -m$, default '@').\n\
540
  -d?                  Alternative macro delimitor set (eg -m[, default '(').\n\
541
                        '[' implies the delimitor set of \"[]\", '(' = \"()\",\n\
542
                        '{' = \"{}\" and '<' implies \"<>\".\n\
543
  -k|-k1               DOS lf/cr kludge.\n\
544
  -k2                  MRI file padding Kludge.\n\
545
  -w                   Trim leading white after newlines.\n\
546
  -W{method}           Method of handling embedded whitespace within paths.\n\
547
    0,-                 - Disable warnings.\n\
548
    1,e                 - Escape (eg /Embedded\\ Space/x.h).\n\
549
    2,q                 - Quote the entire path (eg \"/Embedded Space/x.h\").\n\
550
  -r?                  Realpath expansion kludge (eg -rI, expands -Ipath)\n\
551
  -v                   Verbose output.\n\
552
  -c                   Redirect errors to stdout.\n\
553
  -E                   Disable interpolation of some sequences in STRINGs.\n\
554
  -e                   Enable extended interpolation.\n\
555
  -M                   Disable macros.\n\
556
  -t                   Dont remove temporary working files.\n\
557
  -o<file>             Output file (must be the last option).\n");
558
printf("\n\
559
\n\
560
The follow options must be specified alone;\n\
561
\n\
562
  --help               Display this help and exit\n\
563
  --version            Output version information and exit\n");
564
printf("\n\
565
Without -E, the following sequences are recognized and interpolated:\n\
566
\n\
567
  \\n                   New line.\n\
568
  \\\\                   Backslash.\n");
569
printf("\n\
570
If the -e option is given, the following additional sequences are\n\
571
recognized and interpolated:\n\
572
\n\
573
  \\a                   Alert (bell).\n\
574
  \\b                   Backspace.\n\
575
  \\c                   Suppress trailing newline (same as -n).\n\
576
  \\f                   Form feed.\n\
577
  \\r                   Carriage return.\n\
578
  \\t                   Horizontal tab.\n\
579
  \\v                   Vertical tab.\n\
580
  \\NNN                 The character whose ASCII code is NNN (octal).\n");
581
printf("\n\
582
Without -M, the following macros are recognized and executed:\n\
583
\n\
584
  @(env,var)           Get text from named EnvVar \n\
585
  @(dosify,path)       Convert the specify path to DOS conventions (8.3)\n\
586
  @(realpath,path)     Determine the absolute path, removing . and ..\n\
587
                       and resolving relative references\n\
588
  @(shortpath,path)    Convert the path into a short path (WIN32 specific).\n\
589
  @(vhost,ident)       Override the default host (Unix or WIN32).\n\
590
  @(vsep,ident)        Override the default path sep(colon or semicolon).\n\
591
  @(sep,c,text)        Use path element sep characacter and clean text.\n\
592
  @(vlibgcc,path)      Invoke the specific gcc version to determine the\n\
593
                       default library search directories.\n\
594
  @(vpath[23],n,path,[sep])  Resolve 'name' using search 'path'.\n\
595
  @(vlib[23],n,path,[sep])   Resolve library 'name' using the search 'path',\n\
596
  @(vlint[23],n,path,[sep])  Resolve lint resource 'n' using the search 'path'.\n\
597
  @(vglob[23],p,path,[sep])  Match the pattern 'p' using the search 'path'.\n\
598
\n\
599
The vpath, vlib, vglob and vlint macros have 3 forms that determine the\n\
600
handling of a \"not found\" condition.\n\
601
\n\
602
  1 (default)          Search name/pattern is quoted as specified.\n\
603
  2                    Fatal exit, stating target name/pattern not found.\n\
604
  3                    Outputs nothing.\n\
605
\n\
606
The vpath, vlib, vglob and vlint macros take an optional 3rd argument\n\
607
This is a single character that will be used to seperate the elements\n\
608
in the output path. Normally determined from the content 'path' argument.\n\
609
\n");
610
 
611
#if defined(DEFUNC)
612
printf("\n\
613
  @(url2path,url)      Convert the URL encoded path to a native path.\n\
614
  @(path2url,url)      Convert the path using URL encoding rules.\n\
615
\n");
616
#endif
617
 
618
    exit (status);
619
}
620
 
621
void
622
message(const char *msg, ...)
623
{
624
    va_list args;
625
    FILE    *out = cflg ? stderr : stdout;
626
 
627
    va_start(args, msg);
628
    vfprintf(out, msg, args);
629
    va_end(args);
630
}
631
 
632
 
633
void
634
fatalerr(const char *msg, ...)
635
{
636
    va_list args;
637
    FILE    *out = cflg ? stderr : stdout;
638
 
639
    fprintf(out, "%s: *** Error: ", program_name);
640
    va_start(args, msg);
641
    vfprintf(out, msg, args);
642
    fprintf(out, ". Stop\n");
643
    va_end(args);
644
    exit(1);
645
}
646
 
647
 
648
void
649
warning(const char *msg, ...)
650
{
651
    va_list args;
652
    FILE    *out = cflg ? stderr : stdout;
653
 
654
    fprintf(out, "%s: *** Warning: ", program_name);
655
    va_start(args, msg);
656
    vfprintf(out, msg, args);
657
    fprintf(out, ".\n");
658
    va_end(args);
659
}
660
 
661
 
662
void
663
verbose(const char *msg, ...)
664
{
665
    va_list args;
666
 
667
    if (!vflg)
668
        return;
669
 
670
    va_start(args, msg);
671
    vprintf(msg, args);
672
    va_end(args);
673
 
674
    printf(".\n");
675
    if (vflg >= 2)
676
        fflush(stdout);
677
}
678
 
679
 
680
static void
681
cleanup(void)
682
{
683
    if (oflg && tmp_name[0])
684
    {
685
        verbose( " removing(%s)", tmp_name );
686
        if (tmp_fd != -1)
687
            close(tmp_fd);
688
        if (!tflg)
689
        remove(tmp_name);
690
    }
691
}
692