wsplice.c
来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 988 行 · 第 1/2 页
C
988 行
/****************************************************************************
*
* Open Watcom Project
*
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
* ========================================================================
*
* This file contains Original Code and/or Modifications of Original
* Code as defined in and that are subject to the Sybase Open Watcom
* Public License version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
* provided with the Original Code and Modifications, and is also
* available at www.sybase.com/developer/opensource.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
* NON-INFRINGEMENT. Please see the License for the specific language
* governing rights and limitations under the License.
*
* ========================================================================
*
* Description: WHEN YOU FIGURE OUT WHAT THIS FILE DOES, PLEASE
* DESCRIBE IT HERE!
*
****************************************************************************/
// WSPLICE.C -- mainline for splice program
//
// This program reads a file and produces another according to the
// directives within the file(s) read. The directives are:
// (1) :include file-name
// - includes a file
// (2) :segment expr
// . . .
// {:elsesegment [expr]}
// . . .
// :endsegment
// - the lines between these directives are a segment
// - the section that is delimited by the first <expr> that is TRUE,
// up to the next :elsesegment or :endsegment are output.
// (3) :keep name
// - directs that the lines for the indicated segment are to be output,
// when that segment is subsequently encountered
// (4) :remove name
// - directs that the lines for the indicated segment are to be ignored,
// when that segment is subsequently encountered
// (5) :: comments
// - when the first two non-blank characters are "::" the line is treated
// as a comment and is not written to the output file
//
// The BNF for a expression looks like this:
//
// expr ::= and_expr { "|" and_expr }
// and_expr ::= not_expr { "&" not_expr }
// not_expr ::= [ "!" ] primary_expr
// primary_expr ::= "(" expr ")"
// | name
//
// Additional Rules:
// - when a number of :keep and :remove directives apply to a segment,
// the last one encountered before that segment applies
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
#include <sys/stat.h>
#ifdef __UNIX__
#include <utime.h>
#else
#include <sys/utime.h>
#endif
#include "watcom.h"
//#define local static
#define local
#define TRUE 1
#define FALSE 0
typedef union textent TEXTENT;
typedef struct segment SEGMENT;
typedef struct filestk FILESTK;
typedef struct kw KW;
typedef struct segstk SEGSTK;
typedef struct ipathlst IPATHLST;
typedef unsigned PROCMODE;
struct text { // define TEXT
TEXTENT *next; // - next element
char text_type; // - type of entry
#define TEXT_DATA 'D' // - - data
#define TEXT_CMD 'C' // - - command
};
struct segcmd { // define CMD
struct text base; // - base element
unsigned kw; // - keyword
SEGMENT *segment; // - segment ptr
};
struct datatext { // define DATA
struct text base; // - base element
char data[1]; // - text string
};
union textent { // define TEXTENT
struct text TEXT; // - base
struct segcmd CMD; // - command
struct datatext DATA; // - data
};
struct segment { // define SEGMENT
SEGMENT *next; // - next element
char seg_type; // - segment type
#define SEG_REMOVE 'R' // - - segment to be removed
#define SEG_KEEP 'K' // - - segment to be kept
#define SEG_NONE ' ' // - - keep and remove not specified
char name[1]; // - segment name
};
struct segstk { // define SEGSTK
SEGSTK *next; // - next entry
unsigned rec_def; // - record number of definition
PROCMODE action; // - saved processing mode
};
struct filestk { // define FILESTK
FILESTK *last; // - last element
FILE *file_ptr; // - file ptr.
SEGSTK *save_stk; // - saved segment stack
unsigned rec_count; // - current record
char name[1]; // - file name
};
struct kw { // define KW
unsigned code; // - code
char *name; // - name
};
struct ipathlst { // define ipathlst
struct ipathlst *next; // - next pointer
char path[1]; // - path
};
// LOCAL ROUTINES
local void ProcessSource( char *src_file ); // - process source file
local void ProcessTarget( void ); // - produce target file
local void SegmentCheck( void ); // - check if a seg section should be output
local SEGMENT *ScanSegment( void ); // - scan a new segment
local void AddText( void ); // - add text to a ring
local TEXTENT *AddTextEntry( void ); // - add text entry
local void CmdAdd( void ); // - execute or add a command
local void CmdExecute( void ); // - execute a command
local FILE *OpenFileTruncate( char *, char * ); // - open a file, truncate if necessary
local void OpenFile( char *, char * ); // - open a file
local void CloseFile( void ); // - close a file
local unsigned ReadInput( void ); // - read input record
local SEGMENT *SegmentLookUp( char *seg_name ); // - look up a segment
local SEGSTK *PushSegStack( void ); // - push the segment stack
local void PopSegStack( void ); // - pop the segment stack
local void Error( char*, ... ); // - write an error
local int ScanString( void ); // - scan a string
local void *GetMem( unsigned size ); // - get a block of memory
local unsigned RecordInitialize( char *record ); // - initialize for record processing
local void OutputString( char *p, char *record );// - send string to output file
local void PutNL( void ); // - output a newline
local void AddIncludePathList( char *path ); // - add to list of include paths
local void ProcessRecord( int, char * ); // - PROCESS A RECORD OF INPUT
local void EatWhite( void ); // - eat white space
local int Expr( void );
// DATA (READ ONLY)
enum // define KW codes
{
KW_EOF, // - end of file
KW_COMMENT, // - comment
KW_TEXT, // - text
KW_SEGMENT, // - SEGMENT
KW_ELSESEGMENT, // - ELSESEGMENT
KW_ENDSEGMENT, // - ENDSEGMENT
KW_REMOVE, // - REMOVE
KW_KEEP, // - KEEP
KW_INCLUDE // - INCLUDE
};
local KW KwTable[] = // - key words table
{
{ KW_SEGMENT, "segment" }, // SEGMENT expr
{ KW_ELSESEGMENT, "elsesegment" }, // ELSESEGMENT [expr]
{ KW_ENDSEGMENT, "endsegment" }, // ENDSEGMENT
{ KW_REMOVE, "remove" }, // REMOVE segment-name
{ KW_KEEP, "keep" }, // KEEP segment-name
{ KW_INCLUDE, "include" }, // INCLUDE file
{ 0, NULL }
};
enum {
OP_OR,
OP_AND,
OP_NOT,
OP_LPAREN,
OP_RPAREN
};
local char *Oper[] =
{
"|",
"&",
"!",
"(",
")",
};
// DATA (READ/WRITE)
local unsigned ErrCount; // - number of errors
local FILE *OutputFile; // - output file
local FILESTK *Files; // - stack of opened files
local SEGMENT *Segments; // - list of segments
local SEGSTK *SegStk; // - active-segments stack
local char KwChar = { ':' }; // - key word definition character
local TEXTENT *SourceText; // - source text
local char Token[32]; // - scan token
local char Record[1024]; // - input record
local char *Rptr; // - ptr into record
local PROCMODE ProcessMode; // - processing mode
local char *OutFmt = "%s"; // - output format
local unsigned OutNum = 0; // - output number
local int UnixStyle; // - Unix style newlines?
local int TabStop; // - tab spacing
local IPATHLST *IncPathList; // - list of include paths
local int RestoreTime = FALSE; // - set tgt-file timestamp to src-file
enum // PROCESSING MODES
{
MODE_DELETE, // - deleting
MODE_OUTPUT, // - outputting
MODE_SKIPPING // - skipping to ENDSEGMENT
};
int main( // MAIN-LINE
int argc, // - # arguments
char *argv[] ) // - arguments list
{
#define src_file param[ count ] // - name of source file
#define tgt_file param[ arg_count-1 ] // - name of modifications file
int count; // - current source file #
char *p; // - generic pointer
SEGMENT *seg; // - segment structure
struct utimbuf dest_time;
struct stat src_time;
char *src = NULL;
char *tgt = NULL;
char *param[32];
int arg_count = 0;
for( count = 0; count < argc; count++ ) {
if( argv[count][0] != '@' ) {
param[arg_count] = malloc( strlen( argv[count] ) + 1 );
strcpy( param[arg_count++], argv[count] );
} else {
FILE *f;
char st[512], separator;
int i, j, k;
f = fopen( argv[count] + 1, "r" );
if( f == NULL ) {
Error( "Unable to open indirect argument file" );
continue;
}
fgets( st, 512, f );
fclose( f );
if( st[strlen( st ) - 1] == '\n' )
st[strlen( st ) - 1] = 0;
i = 0;
while( i < strlen( st ) ) {
while( st[i] == ' ' )
i++;
if( st[i] == 0 )
break;
if( st[i] == '"' ) {
separator = '"';
i++;
} else {
separator = ' ';
}
j = i;
while( ( st[j] != separator ) && ( st[j] != 0 ) ) {
if( ( separator == '"' ) && ( st[j] == '\\' ) )
j++;
j++;
}
param[arg_count] = malloc( j - i + 1 );
for( k = 0; k < j - i; k++ ) {
if( ( separator == '"' ) && ( st[i + k] == '\\' ) )
i++;
param[arg_count][k] = st[i + k];
}
param[arg_count][k] = 0;
arg_count++;
i = j;
if( st[i] == '"' )
i++;
}
}
}
ErrCount = 0;
if( arg_count < 3 ) {
puts( "Usage: wsplice {src-file|option} tgt-file\n" );
puts( "options are:" );
puts( " -i path\t\tcheck <path> for included files" );
puts( " -k seg_name\t\tSame as :KEEP" );
puts( " -r seg_name\t\tSame as :REMOVE" );
puts( " -f string\t\tSet output format to be <string>" );
puts( " -o string\t\tOutput <string> to tgt-file" );
puts( " -t tabstop\t\tSet tab character spacing" );
puts( " -u\t\t\tUse Unix style newlines for output file" );
puts( " -p\t\t\tpreserve same time stamp as src-file on tgt-file" );
} else {
if( 0 == stricmp( tgt_file, "-" ) ) {
OutputFile = stdout;
} else {
OutputFile = fopen( tgt_file, "wb" );
tgt = tgt_file;
}
if( OutputFile == NULL ) {
Error( "Unable to open output file" );
} else {
#define get_value() ( (src_file[2]=='\0') ? (++count,src_file) : &src_file[2])
for( count = 1; count < arg_count - 1; ++count ) {
if( src_file[0] == '-' ) {
switch( src_file[1] ) {
case 'i':
p = get_value();
AddIncludePathList( p );
break;
case 'k':
p = get_value();
seg = SegmentLookUp( p );
if( seg != NULL )
seg->seg_type = SEG_KEEP;
break;
case 'r':
p = get_value();
seg = SegmentLookUp( p );
if( seg != NULL )
seg->seg_type = SEG_REMOVE;
break;
case 'f':
OutFmt = get_value();
break;
case 'o':
p = get_value();
OutputString( p, "" );
break;
case 't':
p = get_value();
TabStop = strtoul( p, &p, 0 );
if( TabStop == 0 || *p != '\0' ) {
Error( "Illegal tab value" );
exit( 1 );
}
break;
case 'u':
UnixStyle = TRUE;
break;
case 'p':
RestoreTime = TRUE;
break;
default:
Error( "Unknown option '%c'", src_file[1] );
break;
}
}
else {
src = src_file;
ProcessSource( src_file );
}
}
fclose( OutputFile );
}
}
if( RestoreTime ) {
if( stat( src, &src_time ) == 0 ) {
dest_time.actime = src_time.st_atime;
dest_time.modtime = src_time.st_mtime;
utime( tgt, &dest_time );
}
}
return( ErrCount );
#undef src_file
#undef tgt_file
}
// PROCESS SOURCE FILE
local void ProcessSource( char *src_file ) // - starting file
{
int kw; // - current key-word
ProcessMode = MODE_OUTPUT;
SegStk = NULL;
SourceText = NULL;
Files = NULL;
OpenFile( src_file, "r" );
while( Files != NULL ) {
kw = ReadInput();
if( kw == KW_EOF ) {
CloseFile();
} else {
ProcessRecord( kw, Record );
}
}
}
local void ProcessRecord( // PROCESS A RECORD OF INPUT
int kw, // - key-word for record
char *record )// - record
{
SEGMENT *seg; // - current segment
switch( kw ) {
case KW_SEGMENT:
PushSegStack();
switch( ProcessMode ) {
case MODE_DELETE:
ProcessMode = MODE_SKIPPING;
break;
case MODE_OUTPUT:
SegmentCheck();
break;
}
break;
case KW_ELSESEGMENT:
switch( ProcessMode ) {
case MODE_DELETE:
EatWhite();
if( *Rptr == '\0' ) {
ProcessMode = MODE_OUTPUT;
} else {
SegmentCheck();
}
break;
case MODE_OUTPUT:
ProcessMode = MODE_SKIPPING;
break;
}
break;
case KW_ENDSEGMENT:
PopSegStack();
break;
case KW_KEEP:
switch( ProcessMode ) {
case MODE_OUTPUT:
seg = ScanSegment();
if( seg != NULL )
seg->seg_type = SEG_KEEP;
break;
}
break;
case KW_REMOVE:
switch( ProcessMode ) {
case MODE_OUTPUT:
seg = ScanSegment();
if( seg != NULL )
seg->seg_type = SEG_REMOVE;
break;
}
break;
case KW_INCLUDE:
switch( ProcessMode ) {
case MODE_OUTPUT:
if( ScanString() ) {
OpenFile( Token, "r" );
} else {
Error( "Missing or invalid inclusion file" );
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?