diff options
author | Stef Walter <stef@thewalter.net> | 2003-09-20 07:12:49 +0000 |
---|---|---|
committer | Stef Walter <stef@thewalter.net> | 2003-09-20 07:12:49 +0000 |
commit | b49d8ebefe9b10c53a6a09ad564e22111b7b25c6 (patch) | |
tree | 1d5dd4abc38170a7bc106dabbc59b915017222f0 /src | |
parent | 1cda9ebbd62916c7c2136722597a86c583e1ecf6 (diff) |
Initial Import
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 13 | ||||
-rw-r--r-- | src/file.c | 197 | ||||
-rw-r--r-- | src/file.h | 85 | ||||
-rw-r--r-- | src/rep.1 | 95 | ||||
-rw-r--r-- | src/rep.c | 703 | ||||
-rw-r--r-- | src/rep.dsp | 114 | ||||
-rw-r--r-- | src/repc.1 | 73 | ||||
-rw-r--r-- | src/repc.c | 192 | ||||
-rw-r--r-- | src/repc.dsp | 110 |
9 files changed, 1582 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..59b1a23 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,13 @@ +bin_PROGRAMS = rep repc + +rep_SOURCES = rep.c file.c file.h ../common/compat.c ../common/xstring.c ../common/repfile.c +rep_LDADD = -lrlib -lpcre +rep_CFLAGS = -O0 -I${top_srcdir} -I/usr/local/include +rep_LDFLAGS = -L${top_srcdir}/lib/.libs -L${top_srcdir}/lib -L/usr/local/lib + +repc_SOURCES = repc.c ../common/compat.c ../common/xstring.c ../common/binfile.c ../common/repfile.c +repc_LDADD = -lrlib -lpcre +repc_CFLAGS = -O0 -I${top_srcdir} -I/usr/local/include +repc_LDFLAGS = -L${top_srcdir}/lib/.libs -L${top_srcdir}/lib -L/usr/local/lib + +EXTRA_DIST = rep.dsp repc.dsp diff --git a/src/file.c b/src/file.c new file mode 100644 index 0000000..4f42a5d --- /dev/null +++ b/src/file.c @@ -0,0 +1,197 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "common/compat.h" +#include "file.h" + + +#if HAVE_UNISTD_H && HAVE_DIRENT_H + +int dir_next(DIR* handle, const char* wildcard, struct dirent* entry) +{ + struct dirent* ent = 0; + +#ifndef HAVE_FNMATCH + if(wildcard != NULL) + { + errno = EOPNOTSUPPORTED; + return 0; + } +#endif + + if(!wildcard) + wildcard = "*"; + + do + { + ent = readdir(handle); + if(ent == NULL) + { + errno = ENOENT; + break; + } + } +#ifdef HAVE_FNMATCH + while(0); +#else + while(fnmatch(wildcard, ent->d_name, FNM_PATHNAME) == FNM_NOMATCH); +#endif + + if(ent) + memcpy(entry, ent, sizeof(struct dirent)); + + return ent != NULL; +} + +DIR* dir_first(const char* folder, const char* wildcard, struct dirent* entry) +{ + DIR* handle = opendir((folder && strlen(folder) > 0) ? folder : "."); + if(handle == NULL) + return INVALID_DIR; + + if(dir_next(handle, wildcard, entry)) + return handle; + else + { + errno = ENOENT; + dir_close(handle); + return INVALID_DIR; + } +} + +void dir_close(DIR* handle) +{ + closedir(handle); +} + + +#else + +#include <direct.h> +#include <io.h> + +static void copyDirStruct(struct _finddata_t* findinfo, struct dirent* entry) +{ + /* Copy the structure */ + memset(entry, 0, sizeof(struct dirent)); + entry->d_fileno = ~0; + entry->d_reclen = ~0; + strcpy(entry->d_name, findinfo->name); + entry->d_namlen = strlen(findinfo->name); + + if(findinfo->attrib & _A_SUBDIR) + entry->d_type |= DT_DIR; + else + entry->d_type |= DT_REG; +} + +DIR* dir_first(const char* folder, const char* wildcard, struct dirent* entry) +{ + DIR* handle; + char buff[MAX_PATH * 2]; + struct _finddata_t findinfo; + + if(!wildcard) + wildcard = "*.*"; + + /* Check against buffer overflow hacks */ + if(strlen(folder) + strlen(wildcard) + 1 > MAX_PATH * 2) + { + errno = ENAMETOOLONG; + return INVALID_DIR; + } + + if(!(handle = malloc(sizeof(DIR)))) + { + errno = ENOMEM; + return INVALID_DIR; + } + + strcpy(buff, folder); + strcat(buff, strlen(folder) ? "\\" : ""); + strcat(buff, wildcard); + + /* Okay get the first file */ + *handle = _findfirst(buff, &findinfo); + + if(*handle == -1) + { + free(handle); + return INVALID_DIR; + } + else + { + copyDirStruct(&findinfo, entry); + return handle; + } +} + +int dir_next(DIR* handle, const char* wildcard, struct dirent* entry) +{ + struct _finddata_t findinfo; + int ret = _findnext(*handle, &findinfo) == 0; + if(ret) + copyDirStruct(&findinfo, entry); + + return ret; +} + +void dir_close(DIR* handle) +{ + if(handle) + { + _findclose(*handle); + free(handle); + } +} + +#endif + +const char* getFilename(const char* path) +{ + const char* filename = strrchr(path, '\\'); + const char* filename2 = strrchr(path, '/'); + if(filename2 > filename) + filename = filename2; + return filename ? filename : path; +} + +const char* getExtension(const char* path) +{ + return strrchr(path, '.'); +} + +int isDots(const char* path) +{ + const char* filename = getFilename(path); + return (filename[0] == '.' && (filename[1] == '.' || filename[1] == '\0')); +} + +int isDirectory(const char* path) +{ + struct stat st; + if(stat(path, &st) == -1) + return 0; + + return (st.st_mode & S_IFDIR) ? 1 : 0; +} diff --git a/src/file.h b/src/file.h new file mode 100644 index 0000000..1b46dd2 --- /dev/null +++ b/src/file.h @@ -0,0 +1,85 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +/* ------------------------------------------------------------------------- + * Directory iterating routines and other handy stuff + */ + +#ifndef __FILE_H__ +#define __FILE_H__ + +#ifndef MAX_PATH +#if defined(MAXNAMLEN) +#define MAX_PATH MAXNAMLEN +#elif defined(_MAX_FNAME) +#define MAX_PATH _MAX_FNAME +#else +#define MAX_PATH 256 +#endif +#endif + +#if HAVE_UNISTD_H && HAVE_DIRENT_H + +#include <unistd.h> +#include <sys/types.h> +#include <dirent.h> + + +#else + + +struct dirent { + long d_fileno; /* file number of entry */ + short d_reclen; /* length of this record */ + unsigned char d_type; /* file type, see below */ + unsigned char d_namlen; /* length of string in d_name */ +#ifdef _POSIX_SOURCE + char d_name[255 + 1]; /* name must be no longer than this */ +#else +#define MAXNAMLEN 255 + char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */ +#endif +}; + +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +typedef int DIR; + +#endif + +DIR* dir_first(const char* folder, const char* wildcard, struct dirent* entry); +int dir_next(DIR* handle, const char* wildcard, struct dirent* entry); +void dir_close(DIR* handle); + +#define INVALID_DIR ((DIR*)-1) + +const char* getFilename(const char* szPath); +const char* getExtension(const char* szPath); +int isDots(const char* szPath); +int isDirectory(const char* szPath); + +#endif
\ No newline at end of file diff --git a/src/rep.1 b/src/rep.1 new file mode 100644 index 0000000..406dcc8 --- /dev/null +++ b/src/rep.1 @@ -0,0 +1,95 @@ +.Dd September, 2002 +.Dt REP 1 +.Os Rep 2.3 +.Sh NAME +.Nm rep +.Nd a regular expression search and replace language +.Sh SYNOPSIS +.Nm +.Op Fl ipq +.Op Fl z Ar buffsize +.Ar script +.Op Ar infile +.Op Ar outfile +.Nm +.Fl f +.Op Fl bipq +.Op Fl z Ar buffsize +.Ar script +.Ar file +.Ar +.Sh DESCRIPTION +The +.Nm +scripting language is a regular expression language used for fine grained, +buffer based search and replace. It is not limited to lines. A full description +of what +.Nm +is capable of is outside the scope of this document. +.Pp +.Ar script +is a text or compiled +.Nm +script. For details see the language documentation that came along with the distribution. +.Pp +When used with the +.Fl f +argument +.Nm +replaces files in place. Otherwise it reads from +.Ar infile +and writes to +.Ar outfile +\&. If either infile or outfile are missing or are equal to a dash +.Sq Li - +, then rep processes +.Em stdin +or +.Em stdout +respectively. +.Sh OPTIONS +The options are as follows: +.Bl -tag -width Fl +.It Fl b +Backup files where replacements have occurred. The backup files have an +.Sq x_r +extension appended to their filename. +.It Fl i +Prompt for confirmation before each replacement. +.It Fl p +Only output replaced text. Can be used as a rudimentary parser. +.It Fl q +Supress status messages. Only errors will be sent to stderr. +.It Fl z +Set the replacement buffer size to +.Ar buffsize . +This speeds up execution as regular expressions only have to act on a small +portion of the whole file at once. However the largest match will be limited to +roughly +.Ar buffsize +, so use this option with care. The script loops over each buffer until no more +matches are found within it. Care is taken to overlap the buffers as much as +possible to ensure that any match smaller than +.Ar buffsize +can be matched. +.Sh NOTE +The +.Nm +command uses +.Xr getopt 3 +to parse it's arguments, which allows it to accept +the +.Sq Li -- +option which will stop processing of flag options at that point. This allows +the processing of files with names that begin with a dash +.Pq Sq - . +.Sh BUGS +When reading from +.Em stdin +you must specify a buffer size. +.Sh SEE ALSO +.Xr repc 1 , +.Xr rlib 3 , +.Xr pcre 3 +.Sh AUTHOR +.An Nate Nielsen Aq nielsen@memberwebs.com
\ No newline at end of file diff --git a/src/rep.c b/src/rep.c new file mode 100644 index 0000000..b333ef5 --- /dev/null +++ b/src/rep.c @@ -0,0 +1,703 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <errno.h> +#include "common/usuals.h" +#include "common/compat.h" +#include "lib/rlib.h" +#include "lib/rep.h" +#include "common/repfile.h" +#include "file.h" + +/* Amount of context that will be shown in a prompt surrounding the text */ +#define CONFIRM_CONTEXT 0x20 + +/* Extension used for backup files */ +const char* kBackupExt = ".x_r"; + + + +/* ------------------------------------------------------------------------ + * GLOBALS + */ + +/* + * Since WIN32 has a hokey (did I say hokey, I meant crappy) shell + * we add a bit more file selection functionality if building there. + * We allow a set wildcards to be specified. These apply when recursing + * folders. + */ +#ifdef _WIN32 + #define MAX_WILDCARDS 0x20 + const char* kAllFiles = "*.*"; + + /* When recursing through folders this is the set of files to replace */ + char* g_wildcards[MAX_WILDCARDS]; +#endif + +/* Quiet mode or not? */ +bool g_showStatus = true; + +/* Backups or not? */ +bool g_keepBackups = false; + +/* + * For status updates the amount of replacements and the amount of + * files seen + */ +long g_totalReplaces = 0; +long g_totalFiles = 0; + + +/* ------------------------------------------------------------------------ + * FORWARD DECLARATIONS + */ + +/* The two match callbacks */ +int matchConfirm(r_stream* stream, r_replace* repl); +int matchStatus(r_stream* stream, r_replace* repl); + +/* The main replace functions */ +int replaceFolder(r_context* ctx, char* szWildCard); +int replaceFile(r_context* ctx, FILE* fIn, FILE* fOut); +int replaceFilename(r_context* ctx, const char* szIn, const char* szOut); +int replaceSingleFile(r_context* ctx, const char* szIn); + +/* Error stuff */ +int usage(); + + + +/* ------------------------------------------------------------------------ */ + +int main(int argc, char* argv[]) +{ + /* We use one r_stream throughout for speed */ + r_context context; + bool fileMode = false; + int ch = 0; + + + /* + * Function return values throughout program are a bit different + * + * 0 = normal operation + * -1 = nothing happened but successful + * >0 = error code + */ + int ret = 0; + int r = R_OK; + + + /* Enough params? */ + if(argc < 2) + return usage(); + + + /* Init the stream/context */ + memset(&context, 0, sizeof(context)); + + + /* A little preparation */ + context.stream.fMatch = matchStatus; + + +#ifdef _WIN32 + *g_wildcards = NULL; + + while((ch = getopt(argc, argv, "bcipqw:z:")) != -1) +#else + while((ch = getopt(argc, argv, "bcipqz:")) != -1) +#endif + { + switch(ch) + { + /* Use backups */ + case 'b': + g_keepBackups = true; + break; + + case 'i': + fileMode = true; + break; + + /* Confirmation mode */ + case 'c': + context.stream.fMatch = matchConfirm; + break; + + /* Quiet mode */ + case 'q': + g_showStatus = false; + break; + + /* Parse mode */ + case 'p': + context.options |= RLIB_MODE_PARSER; + break; + +#ifdef _WIN32 + case 'w': + { + /* Get all the wildcards out */ + size_t len = 0; + char* arg = optarg; + char** wildcards = g_wildcards; + + /* + * Enumerate the wild cards seperating them + * at semi-colons or colons + */ + while((len = strcspn(arg, ";")) + && (wildcards < (g_wildcards + MAX_WILDCARDS))) + { + bool last = (arg[len] == '\0'); + + arg[len] = '\0'; + *wildcards = arg; + + wildcards++; + arg += len; + arg++; + + if(last) break; + } + + /* Null terminate the array */ + *wildcards = NULL; + } + break; +#endif + + case 'z': + context.block = atoi(optarg); + if(context.block <= 0x20) + errx(2, "invalid argument. specify block size greater than 32."); + break; + + case '?': + default: + return usage(); + break; + } + } + + argc -= optind; + argv += optind; + + +#ifdef _WIN32 + if(*g_wildcards == NULL) + { + g_wildcards[0] = "*.*"; + g_wildcards[1] = NULL; + } +#endif + + + /* + * The next param should be the script file + * so read it... + */ + if(argc > 0) + { + FILE* file = fopen(argv[0], "rb"); + if(!file) + err(1, "couldn't open rep script file: %s", argv[0]); + + r = repLoad(&context, file); + fclose(file); + + if(r < 0) + exit(errmsg(r, &(context.script))); + } + else + return usage(); + + /* + * Now initialize the rlib library structures + */ + r = repInit(&context); + if(r < 0) + exit(errmsg(r, NULL)); + + argc--; + argv++; + + + /* + * Okay now we have two options: + * + * If -f was specified we read each argument and replace the file in place, + * possibly with backups, descend into folders etc... + * + * Or if not specified we read from the in file and output to the outfile + * If out or both in and out are not specified then use stdin/stdout + */ + if(!fileMode) + { + char* in = "-"; + char* out = "-"; + int ret; + + if(argc > 2) + usage(); + if(argc > 0) + in = argv[0]; + if(argc > 1) + out = argv[1]; + + ret = rlibSetVar(&(context.stream), "FILENAME", in); + if(ret < 0) + ret = errmsg(ret, &(context.script)); + else + ret = replaceFilename(&context, in, out); + } + else + { + while(argc > 0) + { + if(isDirectory(argv[0])) + ret = replaceFolder(&context, argv[0]); + else + ret = replaceSingleFile(&context, argv[0]); + + if(ret > 0) + break; + + argc--; + argv++; + } + } + + /* Release the stream */ + repFree(&context); + + /* Give some stats */ + if(ret == 0 && g_showStatus) + fprintf(stderr, "\n[%d replacements in %d files]\n", g_totalReplaces, g_totalFiles); + + /* Done! */ + return ret <= 0 ? 0 : ret; +} + + + +/* replaceFolder: -------------------------------------------------------------- + * Enumerate all files in a folder and do a replace on each one using + * backups (Note: this function is called recursively) + */ +int replaceFolder(r_context* ctx, char* folder) +{ + struct dirent ent; + char old[260]; /* Save the current folder (as we'll change it) */ + int ret = 0; + DIR* dir; + + /* + * We use a null wildcard as the default and replace below + * if running under WIN32 + */ + char* wildcard = NULL; + char** wildcards = &wildcard; + + + /* Backup the current directory */ + if(!getcwd(old, 260)) + err(1, "couldn't get working folder"); + + +#ifdef _WIN32 + /* + * Now loop through all the wildcards and search for them + * in the folder + */ + wildcards = g_wildcards; + while(*wildcards) + { +#endif + dir = dir_first(folder, *wildcards, &ent); + + if(dir == INVALID_DIR && errno != ENOENT) + err(1, "couldn't list path: %s", folder); + + /* Any files */ + if(dir != INVALID_DIR) + { + /* Change dir to the folder we're searching in */ + chdir(folder); + + /* And for every file */ + do + { + if(ent.d_type & DT_DIR) + { + int r = 0; + + if(g_showStatus) + fprintf(stderr, "%s ", ent.d_name); + + r = replaceSingleFile(ctx, ent.d_name); + + if(g_showStatus) + fputc('\n', stderr); + + if(r > 0) + return r; + if(r > ret) + ret = r; + } + + } + while(dir_next(dir, *wildcards, &ent)); + + if(errno != 0 && errno != ENOENT) + err(1, "couldn't list path: %s", folder); + + dir_close(dir); + + /* + * Change directory back to where we saved it + * the reason we do this is that in some cases (sub-folders) + */ + _chdir(old); + } + +#ifdef _WIN32 + wildcards++; + } +#endif + + /* Now we enumerate all the folders */ + dir = dir_first(folder, NULL, &ent); + + if(dir == INVALID_DIR && errno != ENOENT) + err(1, "couldn't list path: %s", folder); + + /* Any files? */ + if(dir != INVALID_DIR) + { + /* Go into folder */ + chdir(folder); + + do + { + /* For each folder... */ + if(ent.d_type & DT_DIR) + { + int r = 0; + + /* ... that's not dots ... */ + if(isDots(ent.d_name)) + continue; + + /* ... recursively call ourselves */ + ret = replaceFolder(ctx, ent.d_name); + + if(r > 0) + return r; + if(r > ret) + ret = r; + } + } + while(dir_next(dir, NULL, &ent)); + + if(errno != 0 && errno != ENOENT) + err(1, "couldn't list path: %s", folder); + + dir_close(dir); + + /* Change directory back */ + chdir(old); + } + + /* Did anything change? */ + return ret; +} + + +/* replaceSingleFile: ------------------------------------------------------- + * Replace a file into itself using temp/backups + */ +int replaceSingleFile(r_context* ctx, const char* file) +{ + int ret = 2; + char temp [260 + 4]; /* Temp buffer for filenames */ + + ret = rlibSetVar(&(ctx->stream), "FILENAME", file); + if(ret < 0) + return errmsg(ret, &(ctx->script)); + + /* Make a temp/backup name */ + strcpy(temp, file); + strcat(temp, kBackupExt); + + /* Now we do this 2 ways, if we're keeping backups then... */ + if(g_keepBackups) + { + /* Rename the original file to the backup */ + if(rename(file, temp)) + err(1, "couldn't make backup file: %s", temp); + + /* + * Do a replacement operation back to the original + * from the backup + */ + ret = replaceFilename(ctx, temp, file); + + /* If there were no replacements */ + if(ret == -1) + { + /* Remove the replacement file */ + unlink(file); + + /* And rename the backup file back */ + if(rename(temp, file)) + err(1, "couldn't rename file: %s", file); + } + } + + /* No backups, much faster! */ + else + { + /* Do a replacement operation to a temp file */ + ret = replaceFilename(ctx, file, temp); + + /* If there were replacements */ + if(ret == 0) + { + /* Remove original file */ + unlink(file); + + /* Rename temp to original */ + if(rename(temp, file)) + err(1, "couldn't rename file: %s", file); + } + + /* If no replacements */ + else + { + /* Remove temp file */ + unlink(temp); + } + } + + return ret; +} + + +/* replaceFilename: ----------------------------------------------------------- + * Replace one file into another + */ +int replaceFilename(r_context* ctx, const char* in, const char* out) +{ + FILE* fIn; + FILE* fOut; + int ret; + + if(!strcmp(in, "-")) + { + /* + * If someone specified stdin as the input make sure we're + * not going to put up confirmations as we need input from + * stdin ie: YNA or whatever key + */ + if(ctx->stream.fMatch == matchConfirm) + errx(2, "can't confirm replacements while using stdin as input"); + + /* Input is stdin */ + fIn = stdin; + } + + /* Input is a file */ + else + { +#ifdef _WIN32 + if(!(fIn = fopen(in, "rb"))) +#else + if(!(fIn = fopen(in, "r"))) +#endif + err(1, "couldn't open input file: %s", in); + } + + /* Output is stdout */ + if(!strcmp(out, "-")) + fOut = stdout; + + /* Output is a file */ +#ifdef _WIN32 + else if(!(fOut = fopen(out, "wb"))) +#else + else if(!(fOut = fopen(out, "w"))) +#endif + err(1, "couldn't open output file: %s", in); + + + /* Do replacements! */ + ret = replaceFile(ctx, fIn, fOut); + + /* Close any files */ + if(fIn != stdin) + fclose(fIn); + if(fOut != stdout) + fclose(fOut); + + return ret; +} + + + +/* matchConfirm: ----------------------------------------------------------------- + * Confirmation callback for rlib. Used when -i flag is given + */ +int matchConfirm(r_stream* stream, r_replace* repl) +{ + /* Remember if someone pressed A */ + static bool all = false; + char ch = 0; + int ret; + size_t context_start, context_end; + + if(all) + return 1; + + fprintf(stderr, "\n------------- ORIGINAL TEXT between ~{ and }~ markers ---------------\n"); + + /* Figure out if we have some context before replacement */ + /* and how much */ + context_start = 0; + if(repl->from >= CONFIRM_CONTEXT) + context_start = repl->from - CONFIRM_CONTEXT; + + + /* Figure out if we have context after replacement */ + context_end = stream->availIn; + + if(stream->availIn >= repl->from + repl->flen + CONFIRM_CONTEXT) + context_end = repl->from + repl->flen + CONFIRM_CONTEXT; + + /* Write Context Before */ + fwrite(stream->nextIn + context_start, sizeof(char), + repl->from - context_start, stderr); + /* Write our tag */ + fprintf(stderr, "~{"); + /* Write from text */ + fwrite(stream->nextIn + repl->from, sizeof(char), repl->flen, stderr); + + /* Write tag */ + fprintf(stderr, "}~"); + /* Write Context After */ + fwrite(stream->nextIn + repl->from + repl->flen, sizeof(char), + context_end - (repl->from + repl->flen), stderr); + /* Separator */ + fprintf(stderr, "\n----------------------------- NEW TEXT -------------------------------\n"); + // Replace with this text + fwrite(repl->to, sizeof(byte), repl->tlen, stderr); + /* Prompt */ + fprintf(stderr, "\n----------------------------------------------------------------------\n"); + + fprintf(stderr, "(YES\\No\\All)? "); + + setbuf(stdin, NULL); + while(ch != 'y' && ch != 'n' && ch != 'a' && ch != '\r') + ch = tolower(getchar()); + + if(ch == 'a') + stream->fMatch = matchStatus; + + fprintf(stderr, "\r \r"); + + ret = (ch == 'y' || ch == 'a' || ch == '\r') ? true : false; + + if(ret) + g_totalReplaces++; + + return ret; +} + + +/* matchStatus: ---------------------------------------------------------- + * Confirmation function for rlib. Used for status display + */ +int matchStatus(r_stream* stream, r_replace* repl) +{ + if(g_showStatus) + fputc('.', stderr); + + g_totalReplaces++; + + return 1; +} + + +/* outputMsg: -------------------------------------------------------------- + * Callback for rlib when a message goes to the console. + */ +void outputMsg(struct r_stream* stream, const char* data) +{ + fprintf(stderr, g_showStatus ? "\n%s " : "%s\n", data); +} + +/* replaceFile: ------------------------------------------------------------ + * Replace one file into another using file handles. This is where + * the main stuff actually happens. + */ +int replaceFile(r_context* ctx, FILE* fIn, FILE* fOut) +{ + int ret; + + ctx->stream.fMessage = outputMsg; + ctx->stream.total = 0; + + ret = repFiles(ctx, fIn, fOut); + + /* fatal errors */ + if(ret < 0) + return errmsg(ret, &(ctx->script)); + + ret = (ctx->stream.total > 0) ? 0 : -1; + + g_totalFiles++; + rlibClear(&(ctx->stream)); + + return ret; +} + +/* usage: ------------------------------------------------------------- + * Display usage info + */ +int usage() +{ +#ifdef _WIN32 + fprintf(stderr, "usage: rep [-cpqx] [ -z buffsize ] script infile outfile\n"); + fprintf(stderr, " rep -i [-bcpqx] [ -z buffsize ] [ -w wildcard ] script file ...\n"); +#else + fprintf(stderr, "usage: rep [-cpq] [ -z buffsize ] script infile outfile\n"); + fprintf(stderr, " rep -i [-bcpq] [ -z buffsize ] script file ...\n"); +#endif + return 2; +} + diff --git a/src/rep.dsp b/src/rep.dsp new file mode 100644 index 0000000..37a8fd0 --- /dev/null +++ b/src/rep.dsp @@ -0,0 +1,114 @@ +# Microsoft Developer Studio Project File - Name="rep" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=rep - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rep.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "rep.mak" CFG="rep - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rep - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "rep - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rep - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "release" +# PROP Intermediate_Dir "release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 libpcre.a kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../win32/release/rep.exe" /libpath:"..\win32\pcre\lib\\" + +!ELSEIF "$(CFG)" == "rep - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "debug" +# PROP Intermediate_Dir "debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 libpcre.a kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../win32/debug/rep.exe" /pdbtype:sept /libpath:"..\win32\pcre\lib\\" + +!ENDIF + +# Begin Target + +# Name "rep - Win32 Release" +# Name "rep - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\common\compat.c +# End Source File +# Begin Source File + +SOURCE=.\file.c +# End Source File +# Begin Source File + +SOURCE=.\rep.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\file.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/src/repc.1 b/src/repc.1 new file mode 100644 index 0000000..49045ae --- /dev/null +++ b/src/repc.1 @@ -0,0 +1,73 @@ +.Dd September, 2002 +.Dt REPC 1 +.Os Rep 2.3 +.Sh NAME +.Nm repc +.Nd compiler for the +.Xr rep 1 +scripting language +.Sh SYNOPSIS +.Nm +.Op Fl rp +.Op Fl z Ar buffsize +.Ar script +.Ar outfile +.Nm +.Fl d +.Ar script +.Sh DESCRIPTION +The +.Nm +command compiles +.Xr rep 1 +scripts into an byte code format. This speeds up execution as compilation is not +required at run time. Also useful when using the +.Xr rlib 3 +library. +.Pp +.Ar script +is compiled into +.Ar output . +The +.Fl z +and +.Fl p +options are stored with the compiled script, and will be used as defaults when the +script is executed. +.Sh OPTIONS +The options are as follows: +.Bl -tag -width Fl +.It Fl d +Dump dissassembled byte code. +.It Fl p +Default to parse mode. When the script is executed only replaments +will be written out, rather than unreplaced text. +.It Fl r +Compile into raw byte code format. For use with the +.Xr rlib 3 +library. Any options are not stored with the code. +.Xr rep 3 +cannot read this format. +.It Fl z +Set the default buffer size to +.Ar buffsize +and store it along with the compiled script. See +.Xr rep 1 +for a description of the buffer size. +.Sh NOTE +The +.Nm +command uses +.Xr getopt 3 +to parse it's arguments, which allows it to accept +the +.Sq Li -- +option which will stop processing of flag options at that point. This allows +the processing of files with names that begin with a dash +.Pq Sq - . +.Sh SEE ALSO +.Xr rep 1 , +.Xr rlib 3 , +.Xr pcre 3 +.Sh AUTHOR +.An Nate Nielsen Aq nielsen@memberwebs.com
\ No newline at end of file diff --git a/src/repc.c b/src/repc.c new file mode 100644 index 0000000..8cd5f9b --- /dev/null +++ b/src/repc.c @@ -0,0 +1,192 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include "common/usuals.h" +#include "common/compat.h" +#include "lib/rlib.h" +#include "lib/rep.h" +#include "common/binfile.h" +#include "common/repfile.h" + +int usage(); +int help(); + + +/* --------------------------------------------------------------------------- */ + +int main(int argc, char* argv[]) +{ + r_context ctx; + int ch = 0; + int r = R_OK; + + bool dump = false; /* Do we dump the ops to the display */ + bool raw = false; /* Output raw script data or rep file */ + + memset(&ctx, 0, sizeof(ctx)); + + /* Enough params? */ + if(argc < 2) + return usage(); + + /* Parse the arguments */ + while((ch = getopt(argc, argv, "dprz:")) != -1) + { + switch(ch) + { + /* Output raw script ops to file instead of rep file*/ + case 'r': + raw = true; + break; + + /* Dump rep script ops to stdout */ + case 'd': + dump = true; + break; + + /* Set parse mode option in rep file */ + case 'p': + ctx.options |= RLIB_MODE_PARSER; + break; + + /* Set the buffer size in rep file */ + case 'z': + ctx.block = atoi(optarg); + if(ctx.block <= 0x20) + errx(2, "invalid argument. specify block size greater than 32."); + break; + + case '?': + default: + return usage(); + break; + } + } + + argc -= optind; + argv += optind; + + + /* Okay now compile */ + + /* Make sure we have a valid amount of file names */ + if(argc < 1) + return usage(); + + /* + * The next param should be the script file + * so read it... + */ + { + FILE* file = fopen(argv[0], "rb"); + if(!file) + err(1, "couldn't open rep script file: %s", argv[0]); + + r = repLoad(&ctx, file); + fclose(file); + + if(r < 0) + exit(errmsg(r, &(ctx.script))); + } + + argc--; + argv++; + + /* Now if we were asked to dump then output the file */ + if(dump) + { + if(argc != 0) + return usage(); + + rlibDump(&(ctx.script), stdout); + } + + /* Otherwise we write it out to a file */ + else + { + FILE* file; + char* output = argv[0]; + + if(argc < 1) + return usage(); + + /* Opet the output file */ + if(!(file = fopen(output, "wb"))) + err(1, "couldn't open output file for compiled script: %s\n", output); + + + /* Write a full rep file if not in raw mode */ + if(!raw) + { + bfval val; + BFILE h; + + if(!(h = bfStartFile(file))) + errx(1, "couldn't open output file for compiled script: %s\n", output); + + repfWriteHeader(h); + + bfWriteInt(h, REPVAL_PARSEMODE, + ctx.options & RLIB_MODE_PARSER ? 1 : 0); + + if(ctx.block != 0) + bfWriteInt(h, REPVAL_BUFSIZE, ctx.block); + + val.id = REPVAL_SCRIPT; + val.len = ctx.script.len; + val.type = BINTYPE_DATA; + + bfWriteValue(h, &val, ctx.script.ops); + + bfWriteEnd(h); + bfClose(h); + } + else + { + /* Just write out the raw script */ + fwrite(ctx.script.ops, 1, ctx.script.len, file); + } + + if(ferror(file)) + err(1, "couldn't write to file: %s", output); + + fclose(file); + } + + repFree(&ctx); + + return 0; +} + +/* usage: ---------------------------------------------------------------- + */ +int usage() +{ + fprintf(stderr, "usage: repc [-rp] [ -z buffsize ] script output\n"); + fprintf(stderr, " repc -d script\n"); + return 2; +} diff --git a/src/repc.dsp b/src/repc.dsp new file mode 100644 index 0000000..47c66d9 --- /dev/null +++ b/src/repc.dsp @@ -0,0 +1,110 @@ +# Microsoft Developer Studio Project File - Name="repc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=repc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "repc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "repc.mak" CFG="repc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "repc - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "repc - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "repc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "release" +# PROP Intermediate_Dir "release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 libpcre.a kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../win32/release/repc.exe" /libpath:"..\win32\pcre\lib\\" + +!ELSEIF "$(CFG)" == "repc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "debug" +# PROP Intermediate_Dir "debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 libpcre.a kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../win32/debug/repc.exe" /pdbtype:sept /libpath:"..\win32\pcre\lib\\" + +!ENDIF + +# Begin Target + +# Name "repc - Win32 Release" +# Name "repc - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\common\compat.c +# End Source File +# Begin Source File + +SOURCE=.\repc.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\common\compat.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project |