home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_progs
/
fileutil
/
sed.lzh
/
SED
/
SED.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-08-16
|
59KB
|
1,790 lines
/* sed.c emulate the UN*X sed command.
Copyright (C) 1989 Free Software Foundation, Inc.
NO WARRANTY
BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT
WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY
AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
GENERAL PUBLIC LICENSE TO COPY
1. You may copy and distribute verbatim copies of this source file
as you receive it, in any medium, provided that you conspicuously
and appropriately publish on each copy a valid copyright notice
"Copyright (C) 1989 Free Software Foundation, Inc.", and include
following the copyright notice a verbatim copy of the above disclaimer
of warranty and of this License.
2. You may modify your copy or copies of this source file or
any portion of it, and copy and distribute such modifications under
the terms of Paragraph 1 above, provided that you also do the following:
a) cause the modified files to carry prominent notices stating
that you changed the files and the date of any change; and
b) cause the whole of any work that you distribute or publish,
that in whole or in part contains or is a derivative of this
program or any part thereof, to be licensed at no charge to all
third parties on terms identical to those contained in this
License Agreement (except that you may choose to grant more
extensive warranty protection to third parties, at your option).
c) You may charge a distribution fee for the physical act of
transferring a copy, and you may at your option offer warranty
protection in exchange for a fee.
3. You may copy and distribute this program or any portion of it in
compiled, executable or object code form under the terms of Paragraphs
1 and 2 above provided that you do the following:
a) cause each such copy to be accompanied by the
corresponding machine-readable source code, which must
be distributed under the terms of Paragraphs 1 and 2 above; or,
b) cause each such copy to be accompanied by a
written offer, with no time limit, to give any third party
free (except for a nominal shipping charge) a machine readable
copy of the corresponding source code, to be distributed
under the terms of Paragraphs 1 and 2 above; or,
c) in the case of a recipient of this program in compiled, executable
or object code form (without the corresponding source code) you
shall cause copies you distribute to be accompanied by a copy
of the written offer of source code which you received along
with the copy you received.
4. You may not copy, sublicense, distribute or transfer this program
except as expressly provided under this License Agreement. Any attempt
otherwise to copy, sublicense, distribute or transfer this program is void and
your rights to use the program under this License agreement shall be
automatically terminated. However, parties who have received computer
software programs from you with this License Agreement will not have
their licenses terminated so long as such parties remain in full compliance.
5. If you wish to incorporate parts of this program into other free
programs whose distribution conditions are different, write to the Free
Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet
worked out a simple rule that can be stated here, but we will often permit
this. We will be guided by the two goals of preserving the free status of
all derivatives of our free software and of promoting the sharing and reuse of
software.
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them. Help stamp out software-hoarding! */
#include <stdio.h>
#include <ctype.h>
#include "regex.h"
#ifdef AZTEC_C
#define memcpy(dst,src,n) movmem((src),(dst),(n))
#define memset(src,value,howmany) setmem((src),(howmany),(value))
#define bcopy(s, d, n) movmem((s),(d),(n))
#endif
/* Compile with 'gcc [-g] [-DHAS_UTILS] [-O] -o sed sed.c [-lutils]' */
/* Use '-DHAS_UTILS', -lutils if you if you have hack's utils library */
/* Add '-I. regex.c' if regex is not in the system include dir/library */
#ifdef USG
#define bcopy(s, d, n) ((void)memcpy((d),(s), (n)))
#endif
/* Struct vector is used to describe a chunk of a sed program. There is one
vector for the main program, and one for each { } pair.
*/
struct vector {
struct sed_cmd *v;
int v_length;
int v_allocated;
struct vector *up_one;
struct vector *next_one;
};
/* Goto structure is used to hold both GOTO's and labels. There are two
separate lists, one of goto's, called 'jumps', and one of labels, called
'labels'.
the V element points to the descriptor for the program-chunk in which the
goto was encountered.
the v_index element counts which element of the vector actually IS the
goto/label. The first element of the vector is zero.
the NAME element is the null-terminated name of the label.
next is the next goto/label in the list
*/
struct sed_label {
struct vector *v;
int v_index;
char *name;
struct sed_label *next;
};
/* ADDR_TYPE is zero for a null address,
one if addr_number is valid, or
two if addr_regex is valid,
three, if the address is '$'
Other values are undefined.
*/
#define ADDR_NULL 0
#define ADDR_NUM 1
#define ADDR_REGEX 2
#define ADDR_LAST 3
struct addr {
int addr_type;
struct re_pattern_buffer *addr_regex;
int addr_number;
};
/* Aflags: If the low order bit is set, a1 has been
matched; apply this command until a2 matches.
If the next bit is set, apply this command to all
lines that DON'T match the address(es).
*/
#define A1_MATCHED_BIT 01
#define ADDR_BANG_BIT 02
struct sed_cmd {
struct addr a1,a2;
int aflags;
char cmd;
union {
/* This structure is used for a, i, and c commands */
struct {
char *text;
int text_len;
} cmd_txt;
/* This is used for b and t commands */
struct sed_cmd *label;
/* This for r and w commands */
FILE *io_file;
/* This for the hairy s command */
/* For the flags var:
low order bit means the 'g' option was given,
next bit means the 'p' option was given,
and the next bit means a 'w' option was given,
and wio_file contains the file to write to. */
#define S_GLOBAL_BIT 01
#define S_PRINT_BIT 02
#define S_WRITE_BIT 04
#define S_NUM_BIT 010
struct {
struct re_pattern_buffer *regx;
char *replacement;
int replace_length;
int flags;
int numb;
FILE *wio_file;
} cmd_regex;
/* This for the y command */
char *translate;
/* For { and } */
struct vector *sub;
struct sed_label *jump;
} x;
};
/* Sed operates a line at a time. */
struct line {
char *text; /* Pointer to line allocated by malloc. */
int length; /* Length of text. */
int alloc; /* Allocated space for text. */
};
/* This for all you losing compilers out there that can't handle void * */
#ifdef __GNU__
#define VOID void
#else
#define VOID char
#endif
extern int optind;
extern char *optarg;
extern int getopt();
extern char *memchr();
extern VOID *memmove();
extern VOID *ck_malloc(),*ck_realloc();
extern VOID *init_buffer();
extern char *get_buffer();
extern FILE *ck_fopen();
extern void ck_fclose();
extern void ck_fwrite();
extern void flush_buffer();
extern void add1_buffer();
extern char *strdup();
struct vector *compile_program();
void savchar();
struct sed_label *setup_jump();
void line_copy();
void line_append();
void append_pattern_space();
#ifndef HAS_UTILS
char *myname;
#else
extern char *myname;
#endif
/* This is a good idea */
char *version_string = "GNU sed version 1.02 (or so)";
/* 1.00 Began distributing this file
* 1.01 Added s/re/rep/[digits]
* added #n as first line of script
* added filename globbing
* added 'l' command
* All in the name of POSIX
* 1.02 Fixed D command
* Added Amiga porting ifdefs
* Edwin Hoogerbeets 1989
*/
/* If set, don't write out the line unless explictly told to */
int no_default_output = 0;
/* Current input line # */
int input_line_number = 0;
/* Are we on the last input file? */
int last_input_file = 0;
/* Have we hit EOF on the last input file? */
int input_EOF = 0;
/* non-zero if a quit command has been hit */
int quit_cmd = 0;
/* Have we done any replacements lately? */
int replaced = 0;
/* How many '{'s are we executing at the moment */
int program_depth = 0;
/* The current SED program */
struct vector *the_program = 0;
/* */
struct sed_label *jumps = 0;
struct sed_label *labels = 0;
/* The 'current' input line. */
struct line line;
/* An input line that's been stored by later use by the program */
struct line hold;
/* A 'line' to append to the current line when it comes time to write it out */
struct line append;
static char ONE_ADDR[] = "Command only uses one address";
static char NO_ADDR[] = "Command doesn't take any addresses";
static char LINE_JUNK[] ="Extra characters after command";
static char BAD_EOF[] = "Unexpected End-of-file";
static char USAGE[] = "Usage: %s [-n] [-e script...] [-f sfile...] [file...]\n";
static char NO_REGEX[] = "No previous regular expression";
struct re_pattern_buffer *last_regex;
FILE *input_file;
int bad_input = 0;
main(argc,argv)
char **argv;
{
int opt;
int compiled = 0;
struct sed_label *go,*lbl;
#ifdef AZTEC_C
extern long _Heapsize;
_Heapsize = 80 * 1024;
#endif
myname=argv[0];
while((opt=getopt(argc,argv,"ne:f:"))!=EOF) {
switch(opt) {
case 'n':
if(no_default_output)
panic(USAGE);
no_default_output++;
break;
case 'e':
compile_string(optarg);
compiled++;
break;
case 'f':
compile_file(optarg);
compiled++;
break;
}
}
if(!compiled) {
if(argc<=optind)
panic("No program to run\n");
compile_string(argv[optind]);
optind++;
}
for(go=jumps;go;go=go->next) {
for(lbl=labels;lbl;lbl=lbl->next)
if(!strcmp(lbl->name,go->name))
break;
if(!lbl)
panic("Can't find label for jump to '%s'\n",go->name);
go->v->v[go->v_index].x.jump=lbl;
}
line.length=0;
line.alloc=50;
line.text=ck_malloc(50);
append.length=0;
append.alloc=50;
append.text=ck_malloc(50);
hold.length=0;
hold.alloc=50;
hold.text=ck_malloc(50);
if(argc<=optind) {
last_input_file++;
read_file("-");
} else while(optind<argc) {
if(optind==argc-1)
last_input_file++;
read_file(argv[optind]);
optind++;
if(quit_cmd)
break;
}
if(bad_input)
exit(2);
exit(0);
}
char *prog_start;
char *prog_end;
char *prog_cur;
char *prog_name;
FILE *prog_file;
int prog_line = 1;
compile_string(str)
char *str;
{
prog_file = 0;
prog_line=0;
prog_start=prog_cur=str;
prog_end=str+strlen(str);
the_program=compile_program(the_program);
}
compile_file(str)
char *str;
{
FILE *file;
int ch;
prog_start=prog_cur=prog_end=0;
prog_name=str;
prog_line=1;
if(str[0]=='-' && str[1]=='\0')
prog_file=stdin;
else
prog_file=ck_fopen(str,"r");
ch=getc(prog_file);
if(ch=='#') {
ch=getc(prog_file);
if(ch=='n')
no_default_output++;
while(ch!=EOF && ch!='\n')
ch=getc(prog_file);
} else if(ch!=EOF)
ungetc(ch,prog_file);
the_program=compile_program(the_program);
}
#define MORE_CMDS 40
struct vector *
compile_program(vector)
struct vector *vector;
{
struct sed_cmd *cur_cmd;
int ch;
int slash;
VOID *b;
char *string;
int num;
FILE *compile_filename();
if(!vector) {
vector=(struct vector *)ck_malloc(sizeof(struct vector));
vector->v=(struct sed_cmd *)ck_malloc(MORE_CMDS*sizeof(struct sed_cmd));
vector->v_allocated=MORE_CMDS;
vector->v_length=0;
vector->up_one = 0;
vector->next_one = 0;
}
for(;;) {
do ch=inchar();
while(ch!=EOF && (isspace(ch) || ch=='\n' || ch==';'));
if(ch==EOF)
break;
savchar(ch);
if(vector->v_length==vector->v_allocated) {
vector->v=(struct sed_cmd *)ck_realloc((VOID *)vector->v,(vector->v_length+MORE_CMDS)*sizeof(struct sed_cmd));
vector->v_allocated+=MORE_CMDS;
}
cur_cmd=vector->v+vector->v_length;
vector->v_length++;
cur_cmd->a1.addr_type=0;
cur_cmd->a2.addr_type=0;
cur_cmd->aflags=0;
cur_cmd->cmd=0;
skip_comment:
if(compile_address(&(cur_cmd->a1))) {
ch=inchar();
if(ch==',') {
do ch=inchar();
while(ch!=EOF && isspace(ch));
savchar(ch);
if(compile_address(&(cur_cmd->a2)))
;
else
bad_prog("Unexpected ','");
} else
savchar(ch);
}
ch=inchar();
if(ch==EOF)
break;
new_cmd:
switch(ch) {
case '#':
if(cur_cmd->a1.addr_type!=0)
bad_prog(NO_ADDR);
do ch=inchar();
while(ch!=EOF && ch!='\n');
goto skip_comment;
case '!':
if(cur_cmd->aflags & ADDR_BANG_BIT)
bad_prog("Multiple '!'s");
cur_cmd->aflags|= ADDR_BANG_BIT;
do ch=inchar();
while(ch!=EOF && isspace(ch));
if(ch==EOF)
bad_prog(BAD_EOF);
/* savchar(ch); */
goto new_cmd;
case 'a':
case 'i':
if(cur_cmd->a2.addr_type!=0)
bad_prog(ONE_ADDR);
/* Fall Through */
case 'c':
cur_cmd->cmd=ch;
if(inchar()!='\\' || inchar()!='\n')
bad_prog(LINE_JUNK);
b=init_buffer();
while((ch=inchar())!=EOF && ch!='\n') {
if(ch=='\\')
ch=inchar();
add1_buffer(b,ch);
}
if(ch!=EOF)
add1_buffer(b,ch);
num=size_buffer(b);
string=(char *)ck_malloc(num);
bcopy(get_buffer(b),string,num);
flush_buffer(b);
cur_cmd->x.cmd_txt.text_len=num;
cur_cmd->x.cmd_txt.text=string;
break;
case '{':
cur_cmd->cmd=ch;
program_depth++;
/* while((ch=inchar())!=EOF && ch!='\n')
if(!isspace(ch))
bad_prog(LINE_JUNK); */
cur_cmd->x.sub=compile_program((struct vector *)0);
/* FOO JF is this the right thing to do? */
break;
case '}':
if(!program_depth)
bad_prog("Unexpected '}'");
--(vector->v_length);
while((ch=inchar())!=EOF && ch!='\n')
if(!isspace(ch))
bad_prog(LINE_JUNK);
return vector;
case ':':
cur_cmd->cmd=ch;
if(cur_cmd->a1.addr_type!=0)
bad_prog(": doesn't want any addresses");
labels=setup_jump(labels,cur_cmd,vector);
break;
case 'b':
case 't':
cur_cmd->cmd=ch;
jumps=setup_jump(jumps,cur_cmd,vector);
break;
case 'q':
case '=':
if(cur_cmd->a2.addr_type)
bad_prog(ONE_ADDR);
/* Fall Through */
case 'd':
case 'D':
case 'g':
case 'G':
case 'h':
case 'H':
case 'l':
case 'n':
case 'N':
case 'p':
case 'P':
case 'x':
cur_cmd->cmd=ch;
do ch=inchar();
while(ch!=EOF && isspace(ch) && ch!='\n' && ch!=';');
if(ch!='\n' && ch!=';' && ch!=EOF)
bad_prog(LINE_JUNK);
break;
case 'r':
if(cur_cmd->a2.addr_type!=0)
bad_prog(ONE_ADDR);
/* FALL THROUGH */
case 'w':
cur_cmd->cmd=ch;
cur_cmd->x.io_file=compile_filename(ch=='r');
break;
case 's':
cur_cmd->cmd=ch;
b=init_buffer();
slash=inchar();
while((ch=inchar())!=EOF && ch!=slash) {
if(ch!='\\') {
add1_buffer(b,ch);
continue;
}
ch=inchar();
switch(ch) {
case 'n':
add1_buffer(b,'\n');
break;
/* case 'b':
add1_buffer(b,'\b');
break;
case 'f':
add1_buffer(b,'\f');
break;
case 'r':
add1_buffer(b,'\r');
break;
case 't':
add1_buffer(b,'\t');
break; */
case EOF:
break;
default:
add1_buffer(b,'\\');
add1_buffer(b,ch);
break;
}
}
if(ch==EOF)
bad_prog(BAD_EOF);
if(size_buffer(b)) {
last_regex=(struct re_pattern_buffer *)ck_malloc(sizeof(struct re_pattern_buffer));
last_regex->allocated=size_buffer(b);
last_regex->buffer=ck_malloc(last_regex->allocated);
last_regex->fastmap=0;
last_regex->translate=0;
re_compile_pattern(get_buffer(b),size_buffer(b),last_regex);
} else if(!last_regex)
bad_prog(NO_REGEX);
cur_cmd->x.cmd_regex.regx=last_regex;
flush_buffer(b);
b=init_buffer();
while((ch=inchar())!=EOF && ch!=slash) {
if(ch=='\\') {
int ci;
ci=inchar();
if(ci!=EOF) {
if(ci!='\n')
add1_buffer(b,ch);
add1_buffer(b,ci);
}
} else
add1_buffer(b,ch);
}
cur_cmd->x.cmd_regex.replace_length=size_buffer(b);
cur_cmd->x.cmd_regex.replacement=ck_malloc(cur_cmd->x.cmd_regex.replace_length);
bcopy(get_buffer(b),cur_cmd->x.cmd_regex.replacement,cur_cmd->x.cmd_regex.replace_length);
flush_buffer(b);
cur_cmd->x.cmd_regex.flags=0;
cur_cmd->x.cmd_regex.numb=0;
if(ch==EOF)
break;
do {
ch=inchar();
switch(ch) {
case 'p':
if(cur_cmd->x.cmd_regex.flags&S_PRINT_BIT)
bad_prog("multiple 'p' options to 's' command");
cur_cmd->x.cmd_regex.flags|=S_PRINT_BIT;
break;
case 'g':
if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT)
cur_cmd->x.cmd_regex.flags&= ~S_NUM_BIT;
if(cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT)
bad_prog("multiple 'g' options to 's' command");
cur_cmd->x.cmd_regex.flags|=S_GLOBAL_BIT;
break;
case 'w':
cur_cmd->x.cmd_regex.flags|=S_WRITE_BIT;
cur_cmd->x.cmd_regex.wio_file=compile_filename(0);
ch='\n';
break;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9':
if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT)
bad_prog("multiple number options to 's' command");
if(cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT==0)
cur_cmd->x.cmd_regex.flags|=S_NUM_BIT;
num = 0;
while(isdigit(ch)) {
num=num*10+ch-'0';
ch=inchar();
}
savchar(ch);
cur_cmd->x.cmd_regex.numb=num;
break;
case '\n':
case ';':
case EOF:
break;
default:
bad_prog("Unknown option to 's'");
break;
}
} while(ch!=EOF && ch!='\n' && ch!=';');
if(ch==EOF)
break;
break;
case 'y':
cur_cmd->cmd=ch;
string=ck_malloc(256);
for(num=0;num<256;num++)
string[num]=num;
b=init_buffer();
slash=inchar();
while((ch=inchar())!=EOF && ch!=slash)
add1_buffer(b,ch);
cur_cmd->x.translate=string;
string=get_buffer(b);
for(num=size_buffer(b);num;--num) {
ch=inchar();
if(ch==EOF)
bad_prog(BAD_EOF);
if(ch==slash)
bad_prog("strings for y command are different lengths");
cur_cmd->x.translate[*string++]=ch;
}
flush_buffer(b);
if(inchar()!=slash || inchar()!='\n')
bad_prog(LINE_JUNK);
break;
default:
fprintf(stderr,"sed: Command: %c\n",ch);
bad_prog("Unknown command");
}
}
return vector;
}
bad_prog(why)
char *why;
{
if(prog_line)
fprintf(stderr,"%s: file %s line %d: %s\n",myname,prog_name,prog_line,why);
else
fprintf(stderr,"%s: %s\n",myname,why);
exit(1);
}
int
inchar()
{
int ch;
if(prog_file)
ch=getc(prog_file);
else {
if(!prog_cur)
return EOF;
else if(prog_cur==prog_end) {
ch=EOF;
prog_cur=0;
} else
ch= *prog_cur++;
}
if(ch=='\n' && prog_line)
prog_line++;
return ch;
}
void
savchar(ch)
int ch;
{
if(ch==EOF)
return;
if(ch=='\n' && prog_line>1)
--prog_line;
if(prog_file)
ungetc(ch,prog_file);
else
*--prog_cur=ch;
}
compile_address(addr)
struct addr *addr;
{
int ch;
int num;
char *b,*init_buffer();
ch=inchar();
if(isdigit(ch)) {
num=ch-'0';
while((ch=inchar())!=EOF && isdigit(ch))
num=num*10+ch-'0';
while(ch!=EOF && isspace(ch))
ch=inchar();
savchar(ch);
addr->addr_type=ADDR_NUM;
addr->addr_number = num;
return 1;
} else if(ch=='/') {
addr->addr_type=ADDR_REGEX;
b=init_buffer();
while((ch=inchar())!=EOF && ch!='/') {
add1_buffer(b,ch);
if(ch=='\\') {
ch=inchar();
if(ch!=EOF)
add1_buffer(b,ch);
}
}
if(size_buffer(b)) {
last_regex=(struct re_pattern_buffer *)ck_malloc(sizeof(struct re_pattern_buffer));
last_regex->allocated=size_buffer(b);
last_regex->buffer=ck_malloc(last_regex->allocated);
last_regex->fastmap=0;
last_regex->translate=0;
re_compile_pattern(get_buffer(b),size_buffer(b),last_regex);
} else if(!last_regex)
bad_prog(NO_REGEX);
addr->addr_regex=last_regex;
flush_buffer(b);
do ch=inchar();
while(ch!=EOF && isspace(ch));
savchar(ch);
return 1;
} else if(ch=='$') {
addr->addr_type=ADDR_LAST;
do ch=inchar();
while(ch!=EOF && isspace(ch));
savchar(ch);
return 1;
} else
savchar(ch);
return 0;
}
struct sed_label *
setup_jump(list,cmd,vec)
struct sed_label *list;
struct sed_cmd *cmd;
struct vector *vec;
{
struct sed_label *tmp;
VOID *b;
int ch;
b=init_buffer();
while((ch=inchar())!=EOF && ch!='\n')
add1_buffer(b,ch);
add1_buffer(b,'\0');
tmp=(struct sed_label *)ck_malloc(sizeof(struct sed_label));
tmp->v=vec;
tmp->v_index=cmd-vec->v;
tmp->name=strdup(get_buffer(b));
tmp->next=list;
flush_buffer(b);
return tmp;
}
#define NUM_FPS 32
struct {
FILE *phile;
char *name;
int readit;
} file_ptrs[NUM_FPS];
FILE *
compile_filename(readit)
{
char *file_name;
int n;
VOID *b;
int ch;
char **globbed;
extern char **glob_filename();
if(inchar()!=' ')
bad_prog("missing ' ' before filename");
b=init_buffer();
while((ch=inchar())!=EOF && ch!='\n')
add1_buffer(b,ch);
add1_buffer(b,'\0');
file_name=get_buffer(b);
globbed=glob_filename(file_name);
if(globbed==0 || globbed==(char **)-1)
bad_prog("can't parse filename");
if(globbed[0] && globbed[1]!=0)
bad_prog("multiple files");
if(globbed[0])
file_name=globbed[0];
for(n=0;n<NUM_FPS;n++) {
if(!file_ptrs[n].name)
break;
if(!strcmp(file_ptrs[n].name,file_name)) {
if(file_ptrs[n].readit!=readit)
bad_prog("Can't open file for both reading and writing");
flush_buffer(b);
return file_ptrs[n].phile;
}
}
if(n<NUM_FPS) {
file_ptrs[n].name=strdup(file_name);
file_ptrs[n].readit=readit;
file_ptrs[n].phile=ck_fopen(file_name,readit ? "r" : "a");
flush_buffer(b);
return file_ptrs[n].phile;
} else {
bad_prog("Hopelessely evil compiled in limit on number of open files. re-compile sed\n");
return 0;
}
}
read_file(name)
char *name;
{
if(*name=='-' && name[1]=='\0')
input_file=stdin;
else {
input_file=fopen(name,"r");
if(input_file==0) {
bad_input++;
return;
}
}
while(read_pattern_space()) {
execute_program(the_program);
if(!no_default_output)
ck_fwrite(line.text,1,line.length,stdout);
if(append.length) {
ck_fwrite(append.text,1,append.length,stdout);
append.length=0;
}
if(quit_cmd)
break;
}
ck_fclose(input_file);
}
execute_program(vec)
struct vector *vec;
{
struct sed_cmd *cur_cmd;
int n;
int addr_matched;
static int end_cycle;
int start;
int remain;
int offset;
static struct line tmp;
struct line t;
char *rep,*rep_end,*rep_next,*rep_cur;
struct re_registers regs;
int count = 0;
void str_append();
end_cycle = 0;
for(cur_cmd=vec->v,n=vec->v_length;n;cur_cmd++,n--) {
exe_loop:
addr_matched=0;
if(cur_cmd->aflags&A1_MATCHED_BIT) {
addr_matched=1;
if(match_address(&(cur_cmd->a2)))
cur_cmd->aflags&=~A1_MATCHED_BIT;
} else if(match_address(&(cur_cmd->a1))) {
addr_matched=1;
if(cur_cmd->a2.addr_type!=ADDR_NULL)
cur_cmd->aflags|=A1_MATCHED_BIT;
}
if(cur_cmd->aflags&ADDR_BANG_BIT)
addr_matched= !addr_matched;
if(!addr_matched)
continue;
switch(cur_cmd->cmd) {
case '{': /* Execute sub-program */
execute_program(cur_cmd->x.sub);
break;
case ':': /* Executing labels is easy. */
break;
case '=':
printf("%d\n",input_line_number);
break;
case 'a':
if(append.alloc-append.length<cur_cmd->x.cmd_txt.text_len) {
append.text=ck_realloc(append.text,append.alloc+cur_cmd->x.cmd_txt.text_len);
append.alloc+=cur_cmd->x.cmd_txt.text_len;
}
bcopy(cur_cmd->x.cmd_txt.text,append.text+append.length,cur_cmd->x.cmd_txt.text_len);
append.length+=cur_cmd->x.cmd_txt.text_len;
break;
case 'b':
if(!cur_cmd->x.jump)
end_cycle++;
else {
struct sed_label *j = cur_cmd->x.jump;
n= j->v->v_length - j->v_index;
cur_cmd= j->v->v + j->v_index;
goto exe_loop;
}
break;
case 'c':
line.length=0;
if(!(cur_cmd->aflags&A1_MATCHED_BIT))
ck_fwrite(cur_cmd->x.cmd_txt.text,1,cur_cmd->x.cmd_txt.text_len,stdout);
end_cycle++;
break;
case 'd':
line.length=0;
end_cycle++;
break;
case 'D':
{
char *tmp;
int newlength;
tmp=memchr(line.text,'\n',line.length);
newlength=line.length-(tmp-line.text+1);
if(newlength)
memmove(line.text,tmp,newlength);
line.length=newlength;
}
end_cycle++;
break;
case 'g':
line_copy(&hold,&line);
break;
case 'G':
line_append(&hold,&line);
break;
case 'h':
line_copy(&line,&hold);
break;
case 'H':
line_append(&line,&hold);
break;
case 'i':
ck_fwrite(cur_cmd->x.cmd_txt.text,1,cur_cmd->x.cmd_txt.text_len,stdout);
break;
case 'l':
{
char *tmp;
int n;
int width = 0;
n=line.length;
tmp=line.text;
/* Use --n so this'll skip the trailing newline */
while(--n) {
if(width>77) {
width=0;
putchar('\n');
}
if(isprint(*tmp)) {
putchar(*tmp);
width++;
} else switch(*tmp) {
case '\0':
printf("\\0");
width+=2;
break;
case '\a':
printf("\\a");
width+=2;
break;
case '\b':
printf("\\b");
width+=2;
break;
case '\f':
printf("\\f");
width+=2;
break;
case '\n':
printf("\\n");
width+=2;
break;
case '\r':
printf("\\r");
width+=2;
break;
case '\t':
printf("\\t");
width+=2;
break;
case '\v':
printf("\\v");
width+=2;
break;
default:
printf("/%02x",(*tmp)&0xFF);
width+=2;
break;
}
tmp++;
}
putchar('\n');
}
break;
case 'n':
ck_fwrite(line.text,1,line.length,stdout);
read_pattern_space();
break;
case 'N':
append_pattern_space();
break;
case 'p':
ck_fwrite(line.text,1,line.length,stdout);
break;
case 'P':
{
char *tmp;
tmp=memchr(line.text,'\n',line.length);
ck_fwrite(line.text,1,line.length-(tmp-line.text),stdout);
}
break;
case 'q':
quit_cmd++;
end_cycle++;
break;
case 'r':
{
int n;
char tmp_buf[1024];
rewind(cur_cmd->x.io_file);
while((n=fread(tmp_buf,sizeof(char),1024,cur_cmd->x.io_file))>0)
ck_fwrite(tmp_buf,sizeof(char),n,stdout);
if(ferror(cur_cmd->x.io_file))
panic("Read error on input file to 'r' command\n");
}
break;
case 's':
if(!tmp.alloc) {
tmp.alloc=50;
tmp.text=ck_malloc(50);
}
count=0;
start = 0;
remain=line.length-1;
tmp.length=0;
rep = cur_cmd->x.cmd_regex.replacement;
rep_end=rep+cur_cmd->x.cmd_regex.replace_length;
while((offset = re_search(cur_cmd->x.cmd_regex.regx,
line.text,
line.length-1,
start,
remain,
®s))>=0) {
count++;
if(offset-start)
str_append(&tmp,line.text+start,offset-start);
if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT) {
if(count!=cur_cmd->x.cmd_regex.numb) {
str_append(&tmp,line.text+regs.start[0],regs.end[0]-regs.start[0]);
start = (offset == regs.end[0] ? offset + 1 : regs.end[0]);
remain = (line.length-1) - start;
continue;
}
}
for(rep_next=rep_cur=rep;rep_next<rep_end;rep_next++) {
if(*rep_next=='&') {
if(rep_next-rep_cur)
str_append(&tmp,rep_cur,rep_next-rep_cur);
str_append(&tmp,line.text+regs.start[0],regs.end[0]-regs.start[0]);
rep_cur=rep_next+1;
} else if(*rep_next=='\\') {
if(rep_next-rep_cur)
str_append(&tmp,rep_cur,rep_next-rep_cur);
rep_next++;
if(rep_next!=rep_end) {
int n;
if(*rep_next>='0' && *rep_next<='9') {
n= *rep_next -'0';
str_append(&tmp,line.text+regs.start[n],regs.end[n]-regs.start[n]);
} else
str_append(&tmp,&rep_next,1);
}
rep_cur=rep_next+1;
}
}
if(rep_next-rep_cur)
str_append(&tmp,rep_cur,rep_next-rep_cur);
start = (offset == regs.end[0] ? offset + 1 : regs.end[0]);
remain = (line.length-1) - start;
if(!(cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT))
break;
}
if(!count)
break;
replaced=1;
str_append(&tmp,line.text+regs.end[0],line.length-regs.end[0]);
t.text=line.text;
t.length=line.length;
t.alloc=line.alloc;
line.text=tmp.text;
line.length=tmp.length;
line.alloc=tmp.alloc;
tmp.text=t.text;
tmp.length=t.length;
tmp.alloc=t.alloc;
if(cur_cmd->x.cmd_regex.flags&S_WRITE_BIT)
ck_fwrite(line.text,1,line.length,cur_cmd->x.cmd_regex.wio_file);
if(cur_cmd->x.cmd_regex.flags&S_PRINT_BIT)
ck_fwrite(line.text,1,line.length,stdout);
break;
case 't':
if(replaced) {
replaced = 0;
if(!cur_cmd->x.jump)
end_cycle++;
else {
struct sed_label *j = cur_cmd->x.jump;
n= j->v->v_length - j->v_index;
cur_cmd= j->v->v + j->v_index;
goto exe_loop;
}
}
break;
case 'w':
ck_fwrite(line.text,1,line.length,cur_cmd->x.io_file);
break;
case 'x':
{
struct line tmp;
tmp=line;
line=hold;
hold=tmp;
}
break;
case 'y':
{
unsigned char *p,*e;
for(p=(unsigned char *)(line.text),e=p+line.length;p<e;p++)
*p=cur_cmd->x.translate[*p];
}
break;
default:
panic("INTERNAL ERROR: Bad cmd %c\n",cur_cmd->cmd);
}
if(end_cycle)
break;
}
}
match_address(addr)
struct addr *addr;
{
switch(addr->addr_type) {
case ADDR_NULL:
return 1;
case ADDR_NUM:
return (input_line_number==addr->addr_number);
case ADDR_REGEX:
return (re_search(addr->addr_regex,
line.text,
line.length-1,
0,
line.length-1,
0)>=0) ? 1 : 0;
case ADDR_LAST:
return (input_EOF) ? 1 : 0;
default:
panic("INTERNAL ERROR: bad address type\n");
break;
}
return -1;
}
int
read_pattern_space()
{
int n;
char *p;
int ch;
p=line.text;
n=line.alloc;
input_line_number++;
replaced=0;
for(;;) {
ch=getc(input_file);
if(ch==EOF) {
if(n==line.alloc)
return 0;
*p++='\n';
--n;
line.length=line.alloc-n;
break;
}
*p++=ch;
--n;
if(ch=='\n') {
line.length=line.alloc-n;
break;
}
if(n==0) {
line.text=ck_realloc(line.text,line.alloc*2);
p=line.text+line.alloc;
n=line.alloc;
line.alloc*=2;
}
}
ch=getc(input_file);
if(ch!=EOF)
ungetc(ch,input_file);
else if(last_input_file)
input_EOF++;
return 1;
}
void
append_pattern_space()
{
char *p;
int n;
int ch;
p=line.text+line.length;
n=line.alloc-line.length;
input_line_number++;
replaced=0;
for(;;) {
ch=getc(input_file);
if(ch==EOF) {
if(n==line.alloc)
return;
*p++='\n';
--n;
line.length=line.alloc-n;
break;
}
*p++=ch;
--n;
if(ch=='\n') {
line.length=line.alloc-n;
break;
}
if(n==0) {
line.text=ck_realloc(line.text,line.alloc*2);
p=line.text+line.alloc;
n=line.alloc;
line.alloc*=2;
}
}
ch=getc(input_file);
if(ch!=EOF)
ungetc(ch,input_file);
else if(last_input_file)
input_EOF++;
}
void
line_copy(from,to)
struct line *from,*to;
{
if(from->length>to->alloc) {
to->alloc=from->length;
to->text=ck_realloc(to->text,to->alloc);
}
bcopy(from->text,to->text,from->length);
to->length=from->length;
}
void
line_append(from,to)
struct line *from,*to;
{
if(from->length>(to->alloc-to->length)) {
to->alloc+=from->length;
to->text=ck_realloc(to->text,to->alloc);
}
bcopy(from->text,to->text+to->length,from->length);
to->length+=from->length;
}
void
str_append(to,string,length)
struct line *to;
char *string;
int length;
{
if(length>to->alloc-to->length) {
to->alloc+=length;
to->text=ck_realloc(to->text,to->alloc);
}
bcopy(string,to->text+to->length,length);
to->length+=length;
}
#ifndef HAS_UTILS
#ifdef __STDC__
#include "stdarg.h"
panic(str)
char *str;
{
va_list iggy;
va_start(iggy,str);
fprintf(stderr,"%s: ",myname);
#ifdef NO_VFPRINTF
_doprnt(str,&iggy,stderr);
#else
vfprintf(stderr,str,iggy);
#endif
putc('\n',stderr);
va_end(iggy);
exit(4);
}
#else
#ifdef AZTEC_C
/* this kludge inserted because of lack of vfprintf or _doprnt in the
* Manx libs, and this was easier than rewriting printf, which I have
* no source to! Delete this if and when vfprintf is written on the Amiga.
*/
panic(str,arg1,arg2,arg3)
char *str;
int arg1,arg2,arg3;
{
fprintf(stderr,"%s: ",myname);
fprintf(stderr,str,arg1,arg2,arg3);
fprintf(stderr,"\n");
exit(4);
}
#else
#include "varargs.h"
panic(str,va_alist)
char *str;
va_dcl
{
va_list iggy;
va_start(iggy);
fprintf(stderr,"%s: ",myname);
#ifdef NO_VFPRINTF
_doprnt(str,&iggy,stderr);
#else
vfprintf(stderr,str,iggy);
#endif
putc('\n',stderr);
va_end(iggy);
exit(4);
}
#endif
#endif
#define N_FILE 20
struct id {
FILE *fp;
char *name;
};
static struct id __id_s[N_FILE];
char *
__fp_name(fp)
FILE *fp;
{
int n;
for(n=0;n<N_FILE;n++) {
if(__id_s[n].fp==fp)
return __id_s[n].name;
}
return "{Unknown file pointer}";
}
/* Panic on failing fopen */
FILE *
ck_fopen(name,mode)
char *name;
char *mode;
{
FILE *ret;
int n;
ret=fopen(name,mode);
if(ret==(FILE *)0)
panic("Couldn't open file %s\n",name);
for(n=0;n<N_FILE;n++) {
if(ret==__id_s[n].fp) {
free((VOID *)__id_s[n].name);
__id_s[n].name=(char *)ck_malloc(strlen(name)+1);
strcpy(__id_s[n].name,name);
break;
}
}
if(n==N_FILE) {
for(n=0;n<N_FILE;n++)
if(__id_s[n].fp==(FILE *)0)
break;
if(n==N_FILE)
panic("Internal error: too many files open\n");
__id_s[n].fp=ret;
__id_s[n].name=(char *)ck_malloc(strlen(name)+1);
strcpy(__id_s[n].name,name);
}
return ret;
}
void
ck_fwrite(ptr,size,nmemb,stream)
char *ptr;
int size,nmemb;
FILE *stream;
{
if(fwrite(ptr,size,nmemb,stream)!=nmemb)
panic("couldn't write %d items to %s",nmemb,__fp_name(stream));
}
void
ck_fclose(stream)
FILE *stream;
{
if(fclose(stream)==EOF)
panic("Couldn't close %s\n",__fp_name(stream));
}
VOID *
ck_malloc(size)
int size;
{
VOID *ret;
VOID *malloc();
ret=malloc(size);
if(ret==(VOID *)0)
panic("Couldn't allocate memory\n");
return ret;
}
VOID *
ck_realloc(ptr,size)
VOID *ptr;
int size;
{
VOID *ret;
VOID *realloc();
ret=realloc(ptr,size);
if(ret==(VOID *)0)
panic("Couldn't re-allocate memory\n");
return ret;
}
char *
strdup(str)
char *str;
{
char *ret;
ret=(char *)ck_malloc(strlen(str)+2);
strcpy(ret,str);
return ret;
}
#ifndef AZTEC_C
/*
* memmove - copy bytes, being careful about overlap.
*/
VOID *
memmove(dst, src, size)
VOID *dst;
VOID *src;
int size;
{
register char *d;
register char *s;
register int n;
if (size <= 0)
return(dst);
s = (char *)src;
d = (char *)dst;
if (s <= d && s + (size-1) >= d) {
/* Overlap, must copy right-to-left. */
s += size-1;
d += size-1;
for (n = size; n > 0; n--)
*d-- = *s--;
} else
for (n = size; n > 0; n--)
*d++ = *s++;
return(dst);
}
#else
VOID *
memmove(dst,src,size)
VOID *dst;
VOID *src;
int size;
{
return ( (VOID *) movmem(src,dst,size) );
}
VOID *memchr(mem,val,len)
char *mem;
int val,len;
{
register int index = 0;
while ( index < len ) {
if ( mem[index] == (char) val ) {
return(&mem[index]);
}
++index;
}
return(NULL);
}
#endif
/* Implement a variable sized buffer of 'stuff'. We don't know what it is,
nor do we care, as long as it doesn't mind being aligned on a char boundry.
'b' could be made non-global, with a little work, so we could have more
than one buffer, but we don't need more than one, so why bother? */
struct buffer {
int allocated;
int length;
char *b;
};
#define MIN_ALLOCATE 50
VOID *
init_buffer()
{
struct buffer *b;
b=(struct buffer *)ck_malloc(sizeof(struct buffer));
b->allocated=MIN_ALLOCATE;
b->b=(char *)ck_malloc(MIN_ALLOCATE);
b->length=0;
return (VOID *)b;
}
void
flush_buffer(bb)
VOID *bb;
{
struct buffer *b;
b=(struct buffer *)bb;
free(b->b);
b->b=0;
b->allocated=0;
b->length=0;
free(b);
}
int
size_buffer(b)
VOID *b;
{
struct buffer *bb;
bb=(struct buffer *)b;
return bb->length;
}
void
add_buffer(bb,p,n)
VOID *bb;
char *p;
int n;
{
struct buffer *b;
b=(struct buffer *)bb;
if(b->length+n>b->allocated) {
b->allocated*=2;
b->b=(char *)ck_realloc(b->b,b->allocated);
}
bcopy(p,b->b+b->length,n);
b->length+=n;
}
void
add1_buffer(bb,ch)
VOID *bb;
int ch;
{
struct buffer *b;
b=(struct buffer *)bb;
if(b->length+1>b->allocated) {
b->allocated*=2;
b->b=(char *)ck_realloc(b->b,b->allocated);
}
b->b[b->length]=ch;
b->length++;
}
char *
get_buffer(bb)
VOID *bb;
{
struct buffer *b;
b=(struct buffer *)bb;
return b->b;
}
#endif