/* 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 #include #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(optindv=(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;nv,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.lengthx.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='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;px.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= 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