/* fb.c File Browser Author: John Howard Swaby Version: 1.5 Date: 2 May 1999 Copyright (C) 1998, 1999 John Howard Swaby This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. John Howard Swaby polymath@uwyo.edu */ /* For MS-DOS */ /* #define MSDOS */ /* For Linux running on a PC: Used below to exclude non-printable characters */ /* #define LINUX_PC */ /* For the Borland compiler: Used below because Borland uses chsize whereas everyone else uses ftruncate */ /* #define BORLAND */ /* For compilers without data type long long */ /* #define LONG_ONLY */ #include #include #include #include #include #include #include #ifdef MSDOS #include /* DOS_BINARY should be defined as the flag used in open(...) to read files as binary -- this might be found in fcntl.h */ #define DOS_BINARY O_BINARY #else #define DOS_BINARY 0 #endif #define STDIN 0 short command(int); void setHeader(char*,char*,short); short finishLine(char*,char*,short); void setWidth(char*); void setFormat(char*); char* isNum(char*,short); short clrInput(char*,short); void contin(char*); void diff(char**,char*); void chng(char**,char*); void move(int,char**,char*); void tran(int,char**,char*,char,unsigned short); void listCmds1(short); void listCmds2(void); void usage(char*,short); void info(char**,char*); void help(char*); unsigned short bk, noStop = 1, edit = 0, compress = 0; unsigned short ln = 0, rw = 16, rl, len, charOnly, showChar = 1, interact = 1, head = 1, err, cnt = 1, inc = 0, base, usrWidth = 0; #ifdef LONG_ONLY unsigned long fpos, stop, start = 0, mfpos = 0, ofpos = 0; #else unsigned long long fpos, stop, start = 0, mfpos = 0, ofpos = 0; #endif long efpos, delta; unsigned char c; char fill[10], fs[6], hs[6], oh[2048], h[2048]; char *sp, numerals[23]; int fd = STDIN; int main(int argc,char** argv) { unsigned short ok = 1, argLast, lastArg, showAddr = 1; unsigned short j, i = 0, cont, argIdx, sl, cmd; char *p, *t, s[2048], line[2048], buffer[129], sc[2]; t = p = *argv; while( *p ) { #ifdef MSDOS if( *p=='\\' ) #else if( *p=='/' ) #endif t = p; ++p; } p = *(t+1)&&t!=*argv?t+1:t; #ifdef MSDOS t = p; while( *t ) if( *t=='.' ) *t = '\0'; else ++t; #endif if( argc==1 ) usage(p,0); strcpy(numerals,"fedcbaFEDCBA9876543210"); if( argc==3&&argv[1][0]=='-'&&!argv[1][1]&&!argv[2][1] ) info(argv,p); else if( argc==4&&(argv[1][0]=='b'||argv[1][0]=='d'||argv[1][0]=='h'|| argv[1][0]=='o'||argv[1][0]=='-'||argv[1][0]=='.')&&!argv[1][1] ) diff(argv,p); else if( argc==5&&argv[1][0]=='r'&&!argv[1][1] ) chng(argv,p); else if( 32 ) { while( argIdx64?64:rl?rl:16; usrWidth = 0; ln = 0; if( (sp=strstr(argv[argIdx],"."))&&sp[1] ) { ln = atoi(sp+1); ln = ln>128?128:ln; usrWidth = (ln!=0); } } else usage(p,1); break; case 'i': if( ++argIdx0)) ) ++fpos; } else { efpos = fpos = start; lseek(fd,efpos,SEEK_SET); } setFormat(s); if( (cont=(read(fd,&c,1)>0&&(noStop||fposstart ) start = 0; else if( start+delta>LONG_MAX ) { inc = 0; delta = bk; start = LONG_MAX; } else start += delta; efpos = fpos = start; if( lseek(fd,efpos,SEEK_SET)<0 ) { fprintf(stderr,"%s: An error has occurred while attempting",p); fprintf(stderr," to\n%*s position in file ",strlen(p),""); fprintf(stderr,"stream. This may be corrected\n"); fprintf(stderr,"%*s by either adjusting or ",strlen(p),""); fprintf(stderr,"removing the -i option.\n"); exit(1); } } start = fpos; ofpos = mfpos; } if( interact&&fpos>LONG_MAX ) { if( i ) finishLine(line,buffer,i); printf("\n<< Browsing limit: Use a non-interactive mode to "); printf("see more of this file. >>\n"); exit(0); } cont = cnt&&read(fd,&c,1)>0&&(noStop||fpos'&&!sp[1] ) start = ofpos; else if( *sp=='*'&&sp[1]&&isNum(sp+1,0) ) { num = strtol(sp+1,(char**)NULL,10); inum = inc?delta:bk; if( inum<0&&-inum*num>start ) num = 0; else num = start+inum*num; start = num; } else if( *sp=='/'&&!sp[1] ) { if( head=!head ) setHeader(h,oh,1); } else if( *sp=='|'&&!sp[1] ) { showChar = !showChar; if( head ) setHeader(h,oh,0); } else if( *sp=='+'&&sp[1]&&isNum(sp+1,0) ) { start += delta = strtol(sp+1,(char**)NULL,10); inc = 1; } else if( *sp=='-'&&!sp[1] ) { delta = -(inc?delta:bk); inc = 1; if( delta<0&&-delta>start ) start = 0; else start += delta; } else if( *sp=='-'&&sp[1]&&isNum(sp+1,0) ) { delta = -strtol(sp+1,(char**)NULL,10); inc = 1; if( delta<0&&-delta>start ) start = 0; else start += delta; } else if( *sp=='?'&&!sp[1] ) { listCmds1(edit); contin(""); listCmds2(); contin(""); if( head ) printf("%s\n",h); } else if( *sp=='#'&&!sp[1] ) { printf("INC = "); if( inc ) printf("%ld ",delta); printf("%sdefault%s%ld)",inc?"(":"",inc?": ":" (",bk); contin(" "); if( head ) printf("%s\n",h); } else if( *sp=='!'&&!sp[1] ) { compress = !compress; if( head ) setHeader(h,oh,0); } else if( strstr("BbCcDdHhOo",sc)&& (!sp[1]||sp[1]=='.'&&isNum(sp+2,0)) ) { if( sp[1]=='.'||!usrWidth ) { usrWidth = 0; if( sp[1]=='.'&&(cl=atoi(sp+2))>0 ) { usrWidth = 1; ln = cl>128?128:cl; bk = rw*ln; } else setWidth(sc); delta = inc?delta:bk; } setFormat(sc); if( head ) setHeader(h,oh,0); } else if( tp=isNum(sp,1) ) { if( (cl=atoi(tp+1))>0 ) { ln = cl>128?128:cl; usrWidth = 1; if( head ) setHeader(h,oh,0); } if( (rl=atoi(sp))>0 ) rw = rl>64?64:rl; bk = rw*ln; delta = inc?delta:bk; } else if( edit&&*sp=='@'&&sp[1]&&isNum(sp+1,0) ) { efpos = strtol(sp+1,(char**)NULL,10); lseek(fd,efpos,SEEK_SET); if( read(fd,&value,1)>0 ) { lseek(fd,-1,SEEK_CUR); tp = numerals+22-base-(base==16?6:0); *t = '@'; while( *t&&read(fd,&value,1)>0 ) { printf("%0*ld:%s",len,efpos,!base?" ":compress?"":" "); while( !fgets(t,2048,stdin) ); stp = t; bad = clrInput(t,base); if( *t ) { if( bad ) { lseek(fd,-1,SEEK_CUR); printf("Invalid characters in input.\n"); } else if( base ) { lseek(fd,-1,SEEK_CUR); num = 1; ttp = stp; while( num&&*stp ) if( *stp==' ' ) ++stp; else { sc[0] = *ttp = *stp; num = (strstr(tp,sc)!=NULL); ++ttp; ++stp; } *ttp = '\0'; if( !num ) printf("Base %d number invalid.\n",base); else if( (num=strtoul(t,(char**)NULL,base))>255 ) printf("Value out of range [0..%s].\n", base==2?"11111111":base==8?"377": base==10?"255":"FF"); else { value = num; write(fd,&value,1); ++efpos; } } else { do { lseek(fd,-1,SEEK_CUR); write(fd,stp,1); ++stp; ++efpos; } while( *stp&&read(fd,&value,1)>0 ); } } } } else contin("Offset out of range "); if( head ) printf("%s\n",h); } else if( isNum(sp,0) ) start = strtol(sp,(char**)NULL,10); else { contin("Unrecognized command "); if( head ) printf("%s\n",h); } if( stay ) { if( start>LONG_MAX ) start = LONG_MAX; efpos = fpos = start; lseek(fd,efpos,SEEK_SET); } } } return *sp; } short finishLine(char* line,char* buffer,short i) { short j; printf("%s",line); if( showChar||charOnly ) { buffer[i] = '\0'; for(j=i;j> Press Enter to continue: ",s); while( !fgets(t,2048,stdin) ); clrInput(t,1); } void diff(char** argv,char* p) { short i, j, u, zero; int n1, n2; #ifdef LONG_ONLY unsigned long f1 = 0, f2 = 0; #else unsigned long long f1 = 0, f2 = 0; #endif unsigned char c1[8192], c2[8192]; int fdo; if( (fd=open(argv[2],O_RDONLY|DOS_BINARY))<0 ) { if( argv[1][0]!='.' ) fprintf(stderr,"%s: Can't open file \"%s\".\n",p,argv[2]); exit(1); } if( (fdo=open(argv[3],O_RDONLY|DOS_BINARY))<0 ) { if( argv[1][0]!='.' ) fprintf(stderr,"%s: Can't open file \"%s\".\n",p,argv[3]); exit(1); } fs[0] = '\0'; if( argv[1][0]=='d' ) { ln = 3; strcpy(fs,"%3u %3u\n"); } else if( argv[1][0]=='h' ) { ln = 2; strcpy(fs,"%2X %2X\n"); } else if( argv[1][0]=='o' ) { ln = 3; strcpy(fs,"%3o %3o\n"); } else if( argv[1][0]=='b' ) ln = 8; sprintf(h,"%ld",LONG_MAX); len = strlen(h); fpos = u = 0; f1 += n1 = read(fd,c1,8192); f2 += n2 = read(fdo,c2,8192); while( n1>0||n2>0 ) { for(i=0;i255 ) usage(p,1); value = num; if( (fd=open(argv[4],O_RDWR|DOS_BINARY))<0 ) { fprintf(stderr,"%s: Can't open file \"%s\" for reading and writing.\n", p,argv[4]); exit(1); } efpos = start; lseek(fd,efpos,SEEK_SET); while( read(fd,&c,1)>0&&(noStop||efpos0 ) { fprintf(stderr,"%s: Cannot append to file \"%s\".\n",p,argv[argc-1]); exit(1); } efpos = lseek(fd,0,SEEK_END); if( read(fd,&c,1)>0 ) { fprintf(stderr,"%s: Cannot append from file \"%s\".\n",p,argv[3]); exit(1); } } ffpos = fpos = start; lseek(fd,ffpos,SEEK_SET); ffpos = 0; while( read(fd,&c,1)>0&&(noStop||fpos0 ) { for(j=0;j255 ) { fprintf(stderr,"%s: Value %s out of range [0..%s].\n",p, fill,base==8?"377":"255"); exit(1); } else { b = num; write(fdo,&b,1); ++efpos; } i = 0; if( digit ) fill[i++] = c; else digits = 0; } } if( !digit&&(c=='|'||c=='\n') ) cmnts = (c!='\n'&&(cmnts||c=='|')); } if( n2>0 ) n2 = read(fd,buf,8192); } close(fd); #ifdef BORLAND chsize(fdo,efpos); #else ftruncate(fdo,efpos); #endif close(fdo); exit(0); } void listCmds1(short showEdCmd) { printf(" + Set increment INC to and"); printf(" increment.\n"); printf(" - Set increment INC to - and"); printf(" increment.\n"); printf(" * Increment * INC bytes.\n"); printf(" Increment INC bytes.\n"); printf(" - Set INC to -INC and increment.\n"); printf(" + Set INC to default (rows * columns) and increment.\n"); printf(" # Display current value of INC.\n"); printf(" Go to file offset position .\n"); if( showEdCmd ) { printf(" @ Overwrite bytes starting at position . "); printf(" (Edit mode only.)\n"); } printf(" b|B Binary display (default 8|16 columns).\n"); printf(" d|D Decimal display (default 16|32 columns).\n"); printf(" h|H Hexadecimal display (default 16|32 columns).\n"); printf(" o|O Octal display (default 16|32 columns).\n"); printf(" c|C Character display only (default 64|128 columns).\n"); } void listCmds2(void) { printf(" [r].[c] If r is nonzero, set number of rows to r"); printf(" (range [1..64]).\n"); printf(" If c is nonzero, override column defaults of above"); printf(" alphabetic\n"); printf(" commands and set number of columns to c"); printf(" (range [1..128]).\n"); printf(" A.[c] Display according to A (=b|B|d|D|h|H|o|O|c|C)"); printf(" and restore\n"); printf(" default column settings if c not present or zero; "); printf("otherwise,\n"); printf(" override column defaults and set number of columns "); printf("to c.\n"); printf(" | Toggle between supplemental character display "); printf("present or absent.\n"); printf(" / Toggle between column labels or no column labels.\n"); printf(" ! Toggle between compressed or uncompressed numeric "); printf("display.\n"); printf(" Go to previous marked position. (Initial marked"); printf(" position is 0.)\n"); printf(" ? Display this list of commands.\n"); printf(" Q|q Terminate program.\n"); } void usage(char* p,short u) { FILE* fp; fp = u==1?stderr:stdout; fprintf(fp,"Usage: %s\n [- c|h|n|v] | [[-AaBbCcDdEeHhKkLlNnOoSsTt]",p); fprintf(fp," [-i INC] [-r RNG] [-p PS] File] |\n"); fprintf(fp," [r RNG BYTE File] | [a|c RNG File1 [File2]] | "); fprintf(fp,"[t[ ]b|d|h|o File1 [File2]] |\n"); fprintf(fp," [b|d|h|o|-|. File1 File2]\n"); fprintf(fp,"Enter: %s\n - h for help, - n for notes, ",p); fprintf(fp,"- v for version, or - c for commands.\n"); if( u<2 ) exit(u); } void info(char** argv,char* p) { if( argv[2][0]=='h' ) { usage(p,2); help(p); exit(0); } else if( argv[2][0]=='n' ) { printf("+ Program terminates if it reads past the last byte of "); printf("the file.\n"); printf("+ INC is set to default whenever the offset is beyond %ld.\n", LONG_MAX); printf("+ To prevent skewing of column labels when reading some files "); printf("(e.g. a growing\n file) pass the program "); printf("a maximum file size in the argument to the -r option.\n"); printf("+ Overwriting, command @ (edit mode only), will "); printf("accept input according to the\n current numeric base and "); printf("will accept character strings (byte values in the\n range "); printf("[32..126]) when no numeric display is present.\n"); printf("+ Enter \"<>\" to toggle between two file positions.\n"); printf("+ Files open for writing are also open for reading (see "); printf("exception below).\n"); printf("+ Translate method: %s t[ ]b|d|h|o File1 [File2]\n",p); printf(" - All characters from \"|\" to end of line are ignored for "); printf("input lines.\n"); printf(" - If File1 and File2 are not the same file, File2 is open "); printf("for writing only.\n"); printf(" - Numbers in input may be delimited by any non-digit "); printf("for base 2|8|10|16 with\n"); printf(" the exception of the \"|\" character. If not delimited, "); printf("8|3|3|2 digits will\n"); printf(" be converted at a time.\n"); printf(" - Example: %s -als File | %s th File.out\n",p,p); printf(" results in File and File.out having identical "); printf("contents.\n"); exit(0); } else if( argv[2][0]=='v' ) { #ifdef MSDOS printf("FB"); #else printf("fb"); #endif printf(" - File Browser, version 1.5, 2 May 1999\n"); printf(" Copyright (C) 1998, 1999 John Howard Swaby\n"); printf(" Report bugs to polymath@uwyo.edu\n"); #ifdef MSDOS if( strcmp(p,"FB") ) #else if( strcmp(p,"fb") ) #endif printf("Current alias: %s\n",p); exit(0); } else if( argv[2][0]=='c' ) { listCmds1(1); listCmds2(); exit(0); } } void help(char* p) { printf("Base options:\n"); printf(" -b,-B Binary numbers (8 or 16 columns).\n"); printf(" -d,-D Decimal numbers (16 or 32 columns).\n"); printf(" -h,-H Hexadecimal numbers (16 or 32 columns).\n"); printf(" -o,-O Octal numbers (16 or 32 columns).\n"); printf("Display options:\n"); printf(" -a,-A Suppress or display offset addresses.\n"); printf(" (Option -a is ignored if effective mode is "); printf("interactive.)\n"); printf(" -c,-C Characters only (64 or 128 columns).\n"); printf(" -k,-K Compressed or uncompressed numeric display.\n"); printf(" -l,-L Suppress or display column labels.\n"); printf(" -n,-N Exclude or include supplemental character display.\n"); printf("Mode options:\n"); printf(" -e Edit mode. (Ignored if either -s or -t options "); printf("in effect.)\n"); printf(" -E Read-only mode.\n"); printf(" -s Stream output (non-interactive mode).\n"); printf(" -S Interactive mode. (Will nullify previous -s or -t "); printf("options.)\n"); printf(" -t Read from standard input and stream output "); printf("(non-interactive mode).\n"); printf(" (Will nullify previous -S or -T options.) "); printf("File name MUST be \"-\".\n"); printf(" -T Read from File.\n"); printf("Options with arguments:\n"); printf(" -i INC Increment page INC bytes [0..%ld].\n",LONG_MAX); printf(" 0 sets increment to default, (number of rows)*"); printf("(number of columns).\n"); printf(" -r RNG Portion of File to browse. RNG is Start | "); printf("[Start].[Stop];\n"); printf(" where Start [0..%ld] (default: 0) and Stop ",LONG_MAX); printf("(default:\n"); printf(" end of file) are the starting and stopping offsets "); printf("of File.\n"); printf(" -p PS Page size. PS is Rw | [Rw].[Cl]. Rw [0..64] is "); printf("number of rows\n"); printf(" (default: 16). Cl [0..128] is number of columns "); printf("(default: specified\n"); printf(" by either the base option or "); printf("-c,-C options). 0 for either Rw or Cl\n"); printf(" will also invoke their defaults.\n"); printf("Note: Options are evaluated left-to-right. When options\n"); printf(" are mutually exclusive, only last one is effective.\n"); printf(" \"%s File\" performs the same as\n",p); printf(" \"%s -hAEKLNST -p . -r . -i 0 File\".",p); printf("\n"); printf("Replace method: %s r RNG BYTE File\n",p); printf(" Replace bytes in range RNG of File with value BYTE "); printf("[0..255]. RNG is\n Start | [Start].[Stop]; where Start "); printf("(default: 0) is offset in File where\n replacement should "); printf("start and Stop (default: end of file) is offset where\n"); printf(" replacement should stop.\n"); printf("Append/Copy method: %s a|c RNG File1 [File2]\n",p); printf(" Copy bytes in range RNG from File1 and append|copy to "); printf("File1 or optional\n"); printf(" File2. RNG is Start | [Start].[Stop]; where Start (default: 0)"); printf(" is offset\n in File1 where copying should start and Stop "); printf("(default: end of file) is\n offset where copying should "); printf("stop.\n"); printf("Translate method: %s t[ ]b|d|h|o File1 [File2]\n",p); printf(" Convert binary|decimal|hexadecimal|octal numbers in File1 "); printf("(or standard input\n if space is absent between \"t\" and base "); printf("indicator) into bytes and copy bytes\n to File1 (or optional "); printf("File2 if input is NOT from standard input). See under\n"); printf(" notes (%s - n) for more information.\n",p); printf("Compare method: %s b|d|h|o|-|. File1 File2\n",p); printf(" Report offsets and byte values (in binary|decimal|hexadecimal|"); printf("octal) where\n File1 and File2 differ till end of shortest file."); printf(" If file lengths differ,\n report end of file (EOF) for both "); printf("files. \"-\" - report only if files differ\n and how. \".\" - "); printf("return exit status only: 0 - files the same, 1 - error,\n "); printf("2 - bytes differ, 3 - sizes differ, 4 - bytes and sizes differ.\n"); } /* end of file: fb.c */