X-Git-Url: https://feistymeow.org/gitweb/?a=blobdiff_plain;f=scripts%2Ffiles%2Fphrase_replacer.py;fp=scripts%2Ffiles%2Fphrase_replacer.py;h=0000000000000000000000000000000000000000;hb=221dfeaf97ade253b3522f6b8405519a527fff77;hp=a7ea015534313a33a7f82ed4439d2f5f38d0e8ac;hpb=d00b933b3ef123e5233e15845944ff76545a314a;p=feisty_meow.git diff --git a/scripts/files/phrase_replacer.py b/scripts/files/phrase_replacer.py deleted file mode 100644 index a7ea0155..00000000 --- a/scripts/files/phrase_replacer.py +++ /dev/null @@ -1,257 +0,0 @@ -#!/usr/bin/python - -class phrase_replacer: - """ A simple replacement tool that honors some C/C++ syntax when replacing. - - This will take a particular phrase given by the user and find it in a set of - documents. That phrase will be replaced when it appears completely, and is not - in a C or C++ style comment (// or /* ... */). It also must be clear of any - other alphanumeric pollution, and only be surrounded by white space or operation - characters. - """ - - def __init__(self, argv): - """ Initializes the class with a set of arguments to work with. - - The arguments need to be in the form described by print_instructions(). - """ - self.arguments = argv - # we have three states for the processing: consuming normal code (not within a comment), - # consuming a single line comment, and consuming a multi-line comment. - self.EATING_NORMAL_TEXT = 0 - self.EATING_ONELINE_COMMENT = 1 - self.EATING_MULTILINE_COMMENT = 2 - - def print_instructions(self): - """ Shows the instructions for using this class. """ - print(""" -This script will replace all occurrences of a phrase you specify in a set of files. The -replacement process will be careful about C and C++ syntax and will not replace occurrences -within comments or which are not "complete" phrases (due to other alpha-numeric characters -that abut the phrase). The arguments to the script are: - - {0}: PhraseToReplace ReplacementPhrase File1 [File2 ...] - -For example, if the phrase to replace is Goop, it will be replaced in these contexts: - Goop[32] - molo-Goop - *Goop -but it will not be found in these contexts: - // doop de Goop - rGoop - Goop23 -""".format(self.arguments[0])) - - def validate_and_consume_command_line(self): - """ Performs command line argument handling. """ - arg_count = len(self.arguments) -# for i in range(1, arg_count): -# print("i is {0}, arg is {1}".format(i, self.arguments[i])) - # we need more than 2 arguments, since there needs to be at least one file also. - if arg_count < 4: - return False - self.phrase_to_replace = self.arguments[1] - self.replacement_bit = self.arguments[2] - print("got phrase to replace: \'{0}\' and replacement: \'{1}\'".format(self.phrase_to_replace, self.replacement_bit)) - self.files = self.arguments[3:] - return True - - def read_file_data(self, filename): - """ loads the file into our memory buffer for processing. """ - try: - our_file = open(filename, "rb") - try: - file_buffer = our_file.read() - except IOError: - print("There was an error reading the file {0}".format(filename)) - return False - finally: - our_file.close() - except IOError: - print("There was an error opening the file {0}".format(filename)) - return False - self.file_lines = file_buffer.splitlines() - return True - - def write_file_data(self, filename): - """ takes the processed buffer and sends it back out to the filename. """ -# output_filename = filename + ".new" # safe testing version. - output_filename = filename - try: - our_file = open(output_filename, "wb") - try: - file_buffer = our_file.write(self.processed_buffer) - except IOError: - print("There was an error writing the file {0}".format(output_filename)) - return False - finally: - our_file.close() - except IOError: - print("There was an error opening the file {0}".format(output_filename)) - return False - return True - - def is_alphanumeric(self, check_char): - """ given a character, this returns true if it's between a-z, A-Z or 0-9. """ - if (check_char[0] == "_"): - return True - if ( (check_char[0] <= "z") and (check_char[0] >= "a")): - return True - if ( (check_char[0] <= "Z") and (check_char[0] >= "A")): - return True - if ( (check_char[0] <= "9") and (check_char[0] >= "0")): - return True - return False - - def replace_within_string(self, fix_string): - """ given a string to fix, this replaces all appropriate locations of the phrase. """ - indy = 0 -# print("got to replace within string") - while (indy < len(fix_string)): - # locate next occurrence of replacement text, if any. - indy = fix_string.find(self.phrase_to_replace, indy) -# print("find indy={0}".format(indy)) - if (indy > -1): -# print("found occurrence of replacement string") - # we found an occurrence, but we have to validate it's separated enough. - char_before = "?" # simple default that won't fail our check. - char_after = "?" - if (indy > 0): - char_before = fix_string[indy-1] - if (indy + len(self.phrase_to_replace) < len(fix_string) - 1): - char_after = fix_string[indy+len(self.phrase_to_replace)] -# print("char before {0}, char after {1}".format(char_before, char_after)) - if (not self.is_alphanumeric(char_before) and not self.is_alphanumeric(char_after)): - # this looks like a good candidate for replacement. - fix_string = "{0}{1}{2}".format(fix_string[0:indy], self.replacement_bit, fix_string[indy+len(self.phrase_to_replace):]) -# print("changed string to: {0}".format(fix_string)) - else: - break - indy += 1 # no matches means we have to keep skipping forward. - return fix_string # give back processed form. - - def emit_normal_accumulator(self): - """ handle emission of a chunk of normal code (without comments). """ - # process the text to perform the replacement... - self.normal_accumulator = self.replace_within_string(self.normal_accumulator) - # then send the text into our main buffer; we're done looking at it. - self.processed_buffer += self.normal_accumulator - self.normal_accumulator = "" - - def emit_comment_accumulator(self): - """ emits the piled up text for comments found in the code. """ - self.processed_buffer += self.comment_accumulator - self.comment_accumulator = "" - - def process_file_data(self): - """ iterates through the stored version of the file and replaces the phrase. """ - self.state = self.EATING_NORMAL_TEXT; - # clear out any previously processed text. - self.processed_buffer = "" # reset our new version of the file contents. - self.normal_accumulator = "" - self.comment_accumulator = "" - # iterate through the file's lines. - while (len(self.file_lines) > 0): - # get the next line out of the input. - next_line = self.file_lines[0] - # drop that line from the remaining items. - self.file_lines = self.file_lines[1:] -# print("next line: {0}".format(next_line)) - # decide if we need a state transition. - indy = 0 - if ((len(next_line) > 0) and (self.state == self.EATING_NORMAL_TEXT) and ('/' in next_line)): - # loop to catch cases where multiple slashes are in line and one IS a comment. - while (indy < len(next_line)): - # locate next slash, if any. - indy = next_line.find('/', indy) - if (indy < 0): - break - if ((len(next_line) > indy + 1) and (next_line[indy + 1] == '/')): - # switch states and handle any pent-up text. - self.normal_accumulator += next_line[0:indy] # get last tidbit before comment start. - next_line = next_line[indy:] # keep only the stuff starting at slash. - self.state = self.EATING_ONELINE_COMMENT -# print("state => oneline comment") - self.emit_normal_accumulator() - break - if ((len(next_line) > indy + 1) and (next_line[indy + 1] == '*')): - # switch states and deal with accumulated text. - self.normal_accumulator += next_line[0:indy] # get last tidbit before comment start. - next_line = next_line[indy:] # keep only the stuff starting at slash. - self.state = self.EATING_MULTILINE_COMMENT -# print("state => multiline comment") - self.emit_normal_accumulator() - break - indy += 1 # no matches means we have to keep skipping forward. - - # now handle things appropriately for our current state. - if (self.state == self.EATING_NORMAL_TEXT): - # add the text to the normal accumulator. -# print("would handle normal text") - self.normal_accumulator += next_line + "\n" - elif (self.state == self.EATING_ONELINE_COMMENT): - # save the text in comment accumulator. -# print("would handle oneline comment") - self.comment_accumulator += next_line + "\n" - self.emit_comment_accumulator() - self.state = self.EATING_NORMAL_TEXT - elif (self.state == self.EATING_MULTILINE_COMMENT): - # save the text in comment accumulator. -# print("would handle multiline comment") - self.comment_accumulator += next_line + "\n" - # check for whether the multi-line comment is completed on this line. - if ("*/" in next_line): -# print("found completion for multiline comment on line.") - self.emit_comment_accumulator() - self.state = self.EATING_NORMAL_TEXT - # verify we're not in the wrong state still. - if (self.state == self.EATING_MULTILINE_COMMENT): - print("file seems to have unclosed multi-line comment.") - # last step is to spit out whatever was trailing in the accumulator. - self.emit_normal_accumulator() - # if we got to here, we seem to have happily consumed the file. - return True - - def replace_all_occurrences(self): - """ Orchestrates the process of replacing the phrases. """ - # process our command line arguments to see what we need to do. - try_command_line = self.validate_and_consume_command_line() - if (try_command_line != True): - print("failed to process the command line...\n") - self.print_instructions() - exit(1) - # iterate through the list of files we were given and process them. - for i in range(0, len(self.files)): - print("file {0} is \'{1}\'".format(i, self.files[i])) - worked = self.read_file_data(self.files[i]) - if (worked is False): - print("skipping since file read failed on: {0}".format(self.files[i])) - continue -# print("{0} got file contents:\n{1}".format(self.files[i], self.file_lines)) - worked = self.process_file_data() - if (worked is False): - print("skipping, since processing failed on: {0}".format(self.files[i])) - continue - worked = self.write_file_data(self.files[i]) - if (worked is False): - print("writing file back failed on: {0}".format(self.files[i])) - print("finished processing all files.") - - -if __name__ == "__main__": - import sys - slicer = phrase_replacer(sys.argv) - slicer.replace_all_occurrences() - -############## - -# parking lot of things to do in future: - -#hmmm: actually sometimes one DOES want to replace within comments. argh. -# make ignoring inside comments an optional thing. later. - -# hmmm: one little issue here is if the text to be replaced happens to reside on -# the same line after a multi-line comment. we are okay with ignoring that -# possibility for now since it seems brain-dead to write code that way. - -